From a72700b489aa0e66ee554997334609bc83cc092e Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Wed, 26 Feb 2025 00:05:58 +0000 Subject: [PATCH] Added chart versions: codefresh/cf-runtime: - 7.5.5 instana/instana-agent: - 2.0.13 jfrog/artifactory-ha: - 107.104.10 jfrog/artifactory-jcr: - 107.104.10 kasten/k10: - 7.5.4 --- assets/codefresh/cf-runtime-7.5.5.tgz | Bin 0 -> 48972 bytes assets/instana/instana-agent-2.0.13.tgz | Bin 0 -> 134699 bytes assets/jfrog/artifactory-ha-107.104.10.tgz | Bin 0 -> 224816 bytes assets/jfrog/artifactory-ha-107.104.9.tgz | Bin 224821 -> 224810 bytes assets/jfrog/artifactory-jcr-107.104.10.tgz | Bin 0 -> 470909 bytes assets/kasten/k10-7.5.301.tgz | Bin 186290 -> 186286 bytes assets/kasten/k10-7.5.401.tgz | Bin 0 -> 186353 bytes charts/codefresh/cf-runtime/7.5.5/.helmignore | 3 + charts/codefresh/cf-runtime/7.5.5/Chart.yaml | 28 + charts/codefresh/cf-runtime/7.5.5/README.md | 1296 ++++ .../cf-runtime/7.5.5/README.md.gotmpl | 1071 +++ .../cf-runtime/7.5.5/files/cleanup-runtime.sh | 37 + .../7.5.5/files/configure-dind-certs.sh | 132 + .../cf-runtime/7.5.5/files/init-runtime.sh | 80 + .../7.5.5/files/reconcile-runtime.sh | 38 + .../_components/app-proxy/_deployment.yaml | 70 + .../_components/app-proxy/_env-vars.yaml | 19 + .../_components/app-proxy/_helpers.tpl | 43 + .../_components/app-proxy/_ingress.yaml | 32 + .../_components/app-proxy/_rbac.yaml | 47 + .../_components/app-proxy/_service.yaml | 17 + .../event-exporter/_deployment.yaml | 62 + .../_components/event-exporter/_env-vars.yaml | 14 + .../_components/event-exporter/_helpers.tpl | 43 + .../_components/event-exporter/_rbac.yaml | 47 + .../_components/event-exporter/_service.yaml | 17 + .../event-exporter/_serviceMontor.yaml | 14 + .../_components/monitor/_deployment.yaml | 70 + .../_components/monitor/_env-vars.yaml | 26 + .../_components/monitor/_helpers.tpl | 42 + .../templates/_components/monitor/_rbac.yaml | 56 + .../_components/monitor/_service.yaml | 17 + .../_components/runner/_deployment.yaml | 103 + .../templates/_components/runner/_helpers.tpl | 42 + .../templates/_components/runner/_rbac.yaml | 53 + .../_init-container.yaml | 30 + .../_main-container.yaml | 29 + .../_sidecar-container.yaml | 22 + .../volume-provisioner/_cronjob.yaml | 58 + .../volume-provisioner/_daemonset.yaml | 98 + .../volume-provisioner/_deployment.yaml | 67 + .../volume-provisioner/_env-vars.yaml | 88 + .../volume-provisioner/_helpers.tpl | 93 + .../_components/volume-provisioner/_rbac.yaml | 71 + .../volume-provisioner/_secret.yaml | 22 + .../volume-provisioner/_storageclass.yaml | 47 + .../cf-runtime/7.5.5/templates/_helpers.tpl | 51 + .../7.5.5/templates/app-proxy/deployment.yaml | 9 + .../7.5.5/templates/app-proxy/ingress.yaml | 9 + .../7.5.5/templates/app-proxy/rbac.yaml | 9 + .../7.5.5/templates/app-proxy/service.yaml | 9 + .../templates/event-exporter/deployment.yaml | 9 + .../7.5.5/templates/event-exporter/rbac.yaml | 9 + .../templates/event-exporter/service.yaml | 11 + .../templates/extra/extra-resources.yaml | 6 + .../templates/extra/runtime-images-cm.yaml | 19 + .../hooks/post-install/cm-update-runtime.yaml | 18 + .../hooks/post-install/job-gencerts-dind.yaml | 68 + .../post-install/job-update-runtime.yaml | 77 + .../post-install/rbac-gencerts-dind.yaml | 37 + .../pre-delete/job-cleanup-resources.yaml | 73 + .../pre-delete/rbac-cleanup-resources.yaml | 46 + .../7.5.5/templates/monitor/deployment.yaml | 9 + .../7.5.5/templates/monitor/rbac.yaml | 9 + .../7.5.5/templates/monitor/service.yaml | 9 + .../templates/other/external-secrets.yaml | 2 + .../7.5.5/templates/other/podMonitor.yaml | 2 + .../7.5.5/templates/other/serviceMonitor.yaml | 2 + .../7.5.5/templates/runner/deployment.yaml | 9 + .../7.5.5/templates/runner/rbac.yaml | 9 + .../7.5.5/templates/runtime/_helpers.tpl | 123 + .../templates/runtime/cm-dind-daemon.yaml | 10 + .../7.5.5/templates/runtime/rbac.yaml | 48 + .../runtime/runtime-env-spec-tmpl.yaml | 235 + .../7.5.5/templates/runtime/secret.yaml | 11 + .../7.5.5/templates/runtime/svc-dind.yaml | 16 + .../templates/volume-provisioner/cronjob.yaml | 11 + .../volume-provisioner/daemonset.yaml | 11 + .../volume-provisioner/deployment.yaml | 10 + .../templates/volume-provisioner/rbac.yaml | 9 + .../templates/volume-provisioner/secret.yaml | 10 + .../volume-provisioner/storageclass.yaml | 10 + .../cf-runtime/7.5.5/values-rootless.yaml | 38 + charts/codefresh/cf-runtime/7.5.5/values.yaml | 916 +++ .../instana/instana-agent/2.0.13/.helmignore | 23 + .../instana/instana-agent/2.0.13/Chart.yaml | 39 + .../instana-agent/2.0.13/DEPLOYMENT.md | 54 + charts/instana/instana-agent/2.0.13/README.md | 810 +++ .../instana-agent/2.0.13/app-readme.md | 5 + ...omresourcedefinition_agents_instana_io.yml | 6195 ++++++++++++++++ .../2.0.13/kubernetes.deployment.enabled.png | Bin 0 -> 71985 bytes .../kubernetes_deployment_mode_diagram.py | 20 + .../instana-agent/2.0.13/questions.yml | 236 + .../instana-agent/2.0.13/templates/NOTES.txt | 73 + .../instana-agent/2.0.13/templates/agent.yml | 309 + ..._clusterrole_instana-agent-clusterrole.yml | 187 + ...rator_clusterrole_leader-election-role.yml | 27 + ...nding_instana-agent-clusterrolebinding.yml | 13 + ...olebinding_leader-election-rolebinding.yml | 13 + .../operator_configmap_manager-config.yml | 17 + ...yment_instana-agent-controller-manager.yml | 66 + ..._serviceaccount_instana-agent-operator.yml | 6 + .../instana/instana-agent/2.0.13/values.yaml | 282 + .../artifactory-ha/107.104.10/.helmignore | 24 + .../artifactory-ha/107.104.10/CHANGELOG.md | 1515 ++++ .../artifactory-ha/107.104.10/Chart.lock | 6 + .../artifactory-ha/107.104.10/Chart.yaml | 33 + .../jfrog/artifactory-ha/107.104.10/LICENSE | 201 + .../jfrog/artifactory-ha/107.104.10/README.md | 69 + .../artifactory-ha/107.104.10/app-readme.md | 16 + .../107.104.10/charts/postgresql/.helmignore | 21 + .../107.104.10/charts/postgresql/Chart.lock | 6 + .../107.104.10/charts/postgresql/Chart.yaml | 29 + .../107.104.10/charts/postgresql/README.md | 770 ++ .../postgresql/charts/common/.helmignore | 22 + .../postgresql/charts/common/Chart.yaml | 23 + .../charts/postgresql/charts/common/README.md | 322 + .../charts/common/templates/_affinities.tpl | 94 + .../charts/common/templates/_capabilities.tpl | 95 + .../charts/common/templates/_errors.tpl | 23 + .../charts/common/templates/_images.tpl | 47 + .../charts/common/templates/_ingress.tpl | 42 + .../charts/common/templates/_labels.tpl | 18 + .../charts/common/templates/_names.tpl | 32 + .../charts/common/templates/_secrets.tpl | 129 + .../charts/common/templates/_storage.tpl | 23 + .../charts/common/templates/_tplvalues.tpl | 13 + .../charts/common/templates/_utils.tpl | 62 + .../charts/common/templates/_warnings.tpl | 14 + .../templates/validations/_cassandra.tpl | 72 + .../common/templates/validations/_mariadb.tpl | 103 + .../common/templates/validations/_mongodb.tpl | 108 + .../templates/validations/_postgresql.tpl | 131 + .../common/templates/validations/_redis.tpl | 72 + .../templates/validations/_validations.tpl | 46 + .../postgresql/charts/common/values.yaml | 3 + .../postgresql/ci/commonAnnotations.yaml | 3 + .../charts/postgresql/ci/default-values.yaml | 1 + .../ci/shmvolume-disabled-values.yaml | 2 + .../charts/postgresql/files/README.md | 1 + .../charts/postgresql/files/conf.d/README.md | 4 + .../docker-entrypoint-initdb.d/README.md | 3 + .../charts/postgresql/templates/NOTES.txt | 59 + .../charts/postgresql/templates/_helpers.tpl | 337 + .../postgresql/templates/configmap.yaml | 31 + .../templates/extended-config-configmap.yaml | 26 + .../postgresql/templates/extra-list.yaml | 4 + .../templates/initialization-configmap.yaml | 25 + .../templates/metrics-configmap.yaml | 14 + .../postgresql/templates/metrics-svc.yaml | 26 + .../postgresql/templates/networkpolicy.yaml | 39 + .../templates/podsecuritypolicy.yaml | 38 + .../postgresql/templates/prometheusrule.yaml | 23 + .../charts/postgresql/templates/role.yaml | 20 + .../postgresql/templates/rolebinding.yaml | 20 + .../charts/postgresql/templates/secrets.yaml | 24 + .../postgresql/templates/serviceaccount.yaml | 12 + .../postgresql/templates/servicemonitor.yaml | 33 + .../templates/statefulset-readreplicas.yaml | 411 ++ .../postgresql/templates/statefulset.yaml | 609 ++ .../postgresql/templates/svc-headless.yaml | 28 + .../charts/postgresql/templates/svc-read.yaml | 43 + .../charts/postgresql/templates/svc.yaml | 41 + .../charts/postgresql/values.schema.json | 103 + .../107.104.10/charts/postgresql/values.yaml | 824 +++ .../107.104.10/ci/access-tls-values.yaml | 34 + .../107.104.10/ci/default-values.yaml | 32 + .../107.104.10/ci/global-values.yaml | 255 + .../107.104.10/ci/large-values.yaml | 85 + .../107.104.10/ci/loggers-values.yaml | 43 + .../107.104.10/ci/medium-values.yaml | 85 + .../ci/migration-disabled-values.yaml | 31 + .../ci/nginx-autoreload-values.yaml | 53 + .../ci/rtsplit-access-tls-values.yaml | 106 + .../107.104.10/ci/rtsplit-values.yaml | 155 + .../107.104.10/ci/small-values.yaml | 90 + .../107.104.10/ci/test-values.yaml | 85 + .../107.104.10/files/binarystore.xml | 442 ++ .../107.104.10/files/installer-info.json | 32 + .../107.104.10/files/migrate.sh | 4311 +++++++++++ .../107.104.10/files/migrationHelmInfo.yaml | 27 + .../107.104.10/files/migrationStatus.sh | 44 + .../files/nginx-artifactory-conf.yaml | 114 + .../107.104.10/files/nginx-main-conf.yaml | 83 + .../107.104.10/files/system.yaml | 192 + .../107.104.10/logo/artifactory-logo.png | Bin 0 -> 82419 bytes .../artifactory-ha/107.104.10/questions.yml | 424 ++ .../sizing/artifactory-2xlarge.yaml | 185 + .../107.104.10/sizing/artifactory-large.yaml | 185 + .../107.104.10/sizing/artifactory-medium.yaml | 185 + .../107.104.10/sizing/artifactory-small.yaml | 184 + .../107.104.10/sizing/artifactory-xlarge.yaml | 184 + .../107.104.10/sizing/artifactory-xsmall.yaml | 186 + .../107.104.10/templates/NOTES.txt | 149 + .../107.104.10/templates/_helpers.tpl | 682 ++ .../templates/_system-yaml-render.tpl | 5 + .../templates/additional-resources.yaml | 3 + .../templates/admin-bootstrap-creds.yaml | 15 + .../templates/artifactory-access-config.yaml | 15 + .../artifactory-binarystore-secret.yaml | 18 + .../templates/artifactory-configmaps.yaml | 13 + .../templates/artifactory-custom-secrets.yaml | 19 + .../artifactory-database-secrets.yaml | 24 + .../artifactory-gcp-credentials-secret.yaml | 16 + .../templates/artifactory-installer-info.yaml | 16 + .../templates/artifactory-license-secret.yaml | 16 + .../artifactory-migration-scripts.yaml | 18 + .../templates/artifactory-networkpolicy.yaml | 34 + .../templates/artifactory-nfs-pvc.yaml | 101 + .../templates/artifactory-node-pdb.yaml | 26 + .../artifactory-node-statefulset.yaml | 1695 +++++ .../templates/artifactory-primary-pdb.yaml | 24 + .../artifactory-primary-service.yaml | 61 + .../artifactory-primary-statefulset.yaml | 1884 +++++ .../templates/artifactory-priority-class.yaml | 9 + .../templates/artifactory-role.yaml | 14 + .../templates/artifactory-rolebinding.yaml | 19 + .../templates/artifactory-secrets.yaml | 30 + .../templates/artifactory-service-grpc.yaml | 49 + .../templates/artifactory-service.yaml | 68 + .../templates/artifactory-serviceaccount.yaml | 17 + .../templates/artifactory-storage-pvc.yaml | 27 + .../templates/artifactory-system-yaml.yaml | 16 + .../templates/artifactory-unified-secret.yaml | 96 + .../templates/filebeat-configmap.yaml | 15 + .../107.104.10/templates/ingress-grpc.yaml | 80 + .../107.104.10/templates/ingress.yaml | 107 + .../templates/logger-configmap.yaml | 63 + .../templates/nginx-artifactory-conf.yaml | 18 + .../templates/nginx-certificate-secret.yaml | 14 + .../107.104.10/templates/nginx-conf.yaml | 18 + .../templates/nginx-deployment.yaml | 221 + .../107.104.10/templates/nginx-pdb.yaml | 23 + .../107.104.10/templates/nginx-pvc.yaml | 26 + .../templates/nginx-scripts-conf.yaml | 52 + .../107.104.10/templates/nginx-service.yaml | 94 + .../107.104.10/templates/rtfs-deployment.yaml | 349 + .../107.104.10/templates/rtfs-hpa.yaml | 32 + .../templates/rtfs-properties-configmap.yaml | 17 + .../107.104.10/templates/rtfs-service.yaml | 41 + .../artifactory-ha/107.104.10/values.yaml | 2133 ++++++ .../jfrog/artifactory-ha/107.104.9/Chart.yaml | 1 - .../artifactory-jcr/107.104.10/CHANGELOG.md | 213 + .../artifactory-jcr/107.104.10/Chart.yaml | 30 + .../jfrog/artifactory-jcr/107.104.10/LICENSE | 201 + .../artifactory-jcr/107.104.10/README.md | 125 + .../artifactory-jcr/107.104.10/app-readme.md | 18 + .../107.104.10/charts/artifactory/.helmignore | 24 + .../charts/artifactory/CHANGELOG.md | 1411 ++++ .../107.104.10/charts/artifactory/Chart.lock | 6 + .../107.104.10/charts/artifactory/Chart.yaml | 24 + .../107.104.10/charts/artifactory/LICENSE | 201 + .../107.104.10/charts/artifactory/README.md | 60 + .../artifactory/charts/postgresql/.helmignore | 21 + .../artifactory/charts/postgresql/Chart.lock | 6 + .../artifactory/charts/postgresql/Chart.yaml | 29 + .../artifactory/charts/postgresql/README.md | 770 ++ .../postgresql/charts/common/.helmignore | 22 + .../postgresql/charts/common/Chart.yaml | 23 + .../charts/postgresql/charts/common/README.md | 322 + .../charts/common/templates/_affinities.tpl | 94 + .../charts/common/templates/_capabilities.tpl | 95 + .../charts/common/templates/_errors.tpl | 23 + .../charts/common/templates/_images.tpl | 47 + .../charts/common/templates/_ingress.tpl | 42 + .../charts/common/templates/_labels.tpl | 18 + .../charts/common/templates/_names.tpl | 32 + .../charts/common/templates/_secrets.tpl | 129 + .../charts/common/templates/_storage.tpl | 23 + .../charts/common/templates/_tplvalues.tpl | 13 + .../charts/common/templates/_utils.tpl | 62 + .../charts/common/templates/_warnings.tpl | 14 + .../templates/validations/_cassandra.tpl | 72 + .../common/templates/validations/_mariadb.tpl | 103 + .../common/templates/validations/_mongodb.tpl | 108 + .../templates/validations/_postgresql.tpl | 131 + .../common/templates/validations/_redis.tpl | 72 + .../templates/validations/_validations.tpl | 46 + .../postgresql/charts/common/values.yaml | 3 + .../postgresql/ci/commonAnnotations.yaml | 3 + .../charts/postgresql/ci/default-values.yaml | 1 + .../ci/shmvolume-disabled-values.yaml | 2 + .../charts/postgresql/files/README.md | 1 + .../charts/postgresql/files/conf.d/README.md | 4 + .../docker-entrypoint-initdb.d/README.md | 3 + .../charts/postgresql/templates/NOTES.txt | 59 + .../charts/postgresql/templates/_helpers.tpl | 337 + .../postgresql/templates/configmap.yaml | 31 + .../templates/extended-config-configmap.yaml | 26 + .../postgresql/templates/extra-list.yaml | 4 + .../templates/initialization-configmap.yaml | 25 + .../templates/metrics-configmap.yaml | 14 + .../postgresql/templates/metrics-svc.yaml | 26 + .../postgresql/templates/networkpolicy.yaml | 39 + .../templates/podsecuritypolicy.yaml | 38 + .../postgresql/templates/prometheusrule.yaml | 23 + .../charts/postgresql/templates/role.yaml | 20 + .../postgresql/templates/rolebinding.yaml | 20 + .../charts/postgresql/templates/secrets.yaml | 24 + .../postgresql/templates/serviceaccount.yaml | 12 + .../postgresql/templates/servicemonitor.yaml | 33 + .../templates/statefulset-readreplicas.yaml | 411 ++ .../postgresql/templates/statefulset.yaml | 609 ++ .../postgresql/templates/svc-headless.yaml | 28 + .../charts/postgresql/templates/svc-read.yaml | 43 + .../charts/postgresql/templates/svc.yaml | 41 + .../charts/postgresql/values.schema.json | 103 + .../artifactory/charts/postgresql/values.yaml | 824 +++ .../artifactory/ci/access-tls-values.yaml | 24 + .../charts/artifactory/ci/default-values.yaml | 21 + .../artifactory/ci/derby-test-values.yaml | 19 + .../charts/artifactory/ci/global-values.yaml | 247 + .../charts/artifactory/ci/large-values.yaml | 82 + .../charts/artifactory/ci/loggers-values.yaml | 43 + .../charts/artifactory/ci/medium-values.yaml | 82 + .../ci/migration-disabled-values.yaml | 21 + .../ci/nginx-autoreload-values.yaml | 42 + .../ci/rtsplit-values-access-tls-values.yaml | 96 + .../charts/artifactory/ci/rtsplit-values.yaml | 151 + .../charts/artifactory/ci/small-values.yaml | 85 + .../charts/artifactory/ci/test-values.yaml | 84 + .../charts/artifactory/files/binarystore.xml | 429 ++ .../artifactory/files/installer-info.json | 32 + .../charts/artifactory/files/migrate.sh | 4311 +++++++++++ .../artifactory/files/migrationHelmInfo.yaml | 27 + .../artifactory/files/migrationStatus.sh | 44 + .../files/nginx-artifactory-conf.yaml | 114 + .../artifactory/files/nginx-main-conf.yaml | 83 + .../charts/artifactory/files/system.yaml | 186 + .../artifactory/logo/artifactory-logo.png | Bin 0 -> 82419 bytes .../sizing/artifactory-2xlarge.yaml | 182 + .../artifactory/sizing/artifactory-large.yaml | 184 + .../sizing/artifactory-medium.yaml | 183 + .../artifactory/sizing/artifactory-small.yaml | 183 + .../sizing/artifactory-xlarge.yaml | 184 + .../sizing/artifactory-xsmall.yaml | 185 + .../charts/artifactory/templates/NOTES.txt | 106 + .../charts/artifactory/templates/_helpers.tpl | 647 ++ .../templates/_system-yaml-render.tpl | 5 + .../templates/additional-resources.yaml | 3 + .../templates/admin-bootstrap-creds.yaml | 15 + .../templates/artifactory-access-config.yaml | 15 + .../artifactory-binarystore-secret.yaml | 18 + .../templates/artifactory-configmaps.yaml | 13 + .../templates/artifactory-custom-secrets.yaml | 19 + .../artifactory-database-secrets.yaml | 24 + .../artifactory-gcp-credentials-secret.yaml | 16 + .../templates/artifactory-hpa.yaml | 29 + .../templates/artifactory-installer-info.yaml | 16 + .../templates/artifactory-license-secret.yaml | 16 + .../artifactory-migration-scripts.yaml | 18 + .../templates/artifactory-networkpolicy.yaml | 34 + .../templates/artifactory-nfs-pvc.yaml | 101 + .../templates/artifactory-pdb.yaml | 24 + .../templates/artifactory-priority-class.yaml | 9 + .../templates/artifactory-role.yaml | 14 + .../templates/artifactory-rolebinding.yaml | 19 + .../templates/artifactory-secrets.yaml | 30 + .../templates/artifactory-service-grpc.yaml | 44 + .../templates/artifactory-service.yaml | 59 + .../templates/artifactory-serviceaccount.yaml | 17 + .../templates/artifactory-statefulset.yaml | 1789 +++++ .../templates/artifactory-system-yaml.yaml | 16 + .../templates/artifactory-unified-secret.yaml | 94 + .../templates/filebeat-configmap.yaml | 15 + .../artifactory/templates/ingress-grpc.yaml | 80 + .../charts/artifactory/templates/ingress.yaml | 110 + .../templates/logger-configmap.yaml | 63 + .../templates/nginx-artifactory-conf.yaml | 18 + .../templates/nginx-certificate-secret.yaml | 14 + .../artifactory/templates/nginx-conf.yaml | 18 + .../templates/nginx-deployment.yaml | 223 + .../artifactory/templates/nginx-pdb.yaml | 23 + .../artifactory/templates/nginx-pvc.yaml | 26 + .../templates/nginx-scripts-conf.yaml | 52 + .../artifactory/templates/nginx-service.yaml | 88 + .../templates/rtfs-deployment.yaml | 350 + .../artifactory/templates/rtfs-hpa.yaml | 32 + .../templates/rtfs-properties-configmap.yaml | 17 + .../artifactory/templates/rtfs-service.yaml | 42 + .../107.104.10/charts/artifactory/values.yaml | 2049 ++++++ .../107.104.10/ci/default-values.yaml | 7 + .../107.104.10/logo/jcr-logo.png | Bin 0 -> 77047 bytes .../artifactory-jcr/107.104.10/questions.yml | 271 + .../107.104.10/templates/NOTES.txt | 1 + .../artifactory-jcr/107.104.10/values.yaml | 80 + charts/kasten/k10/7.5.301/Chart.yaml | 1 - charts/kasten/k10/7.5.401/Chart.lock | 6 + charts/kasten/k10/7.5.401/Chart.yaml | 22 + charts/kasten/k10/7.5.401/README.md | 342 + charts/kasten/k10/7.5.401/app-readme.md | 5 + .../k10/7.5.401/charts/prometheus/.helmignore | 23 + .../k10/7.5.401/charts/prometheus/Chart.lock | 15 + .../k10/7.5.401/charts/prometheus/Chart.yaml | 53 + .../k10/7.5.401/charts/prometheus/OWNERS | 6 + .../k10/7.5.401/charts/prometheus/README.md | 387 + .../charts/alertmanager/.helmignore | 25 + .../prometheus/charts/alertmanager/Chart.yaml | 24 + .../prometheus/charts/alertmanager/README.md | 62 + .../alertmanager/ci/config-reload-values.yaml | 2 + .../charts/alertmanager/templates/NOTES.txt | 21 + .../alertmanager/templates/_helpers.tpl | 92 + .../alertmanager/templates/configmap.yaml | 21 + .../alertmanager/templates/ingress.yaml | 44 + .../templates/ingressperreplica.yaml | 56 + .../charts/alertmanager/templates/pdb.yaml | 14 + .../templates/serviceaccount.yaml | 14 + .../templates/serviceperreplica.yaml | 44 + .../alertmanager/templates/services.yaml | 75 + .../alertmanager/templates/statefulset.yaml | 256 + .../templates/tests/test-connection.yaml | 20 + .../charts/alertmanager/templates/vpa.yaml | 26 + .../charts/alertmanager/values.schema.json | 946 +++ .../charts/alertmanager/values.yaml | 404 ++ .../charts/kube-state-metrics/.helmignore | 21 + .../charts/kube-state-metrics/Chart.yaml | 26 + .../charts/kube-state-metrics/README.md | 85 + .../kube-state-metrics/templates/NOTES.txt | 23 + .../kube-state-metrics/templates/_helpers.tpl | 156 + .../templates/ciliumnetworkpolicy.yaml | 33 + .../templates/clusterrolebinding.yaml | 20 + .../templates/crs-configmap.yaml | 16 + .../templates/deployment.yaml | 344 + .../templates/extra-manifests.yaml | 4 + .../templates/kubeconfig-secret.yaml | 12 + .../templates/networkpolicy.yaml | 43 + .../kube-state-metrics/templates/pdb.yaml | 18 + .../templates/podsecuritypolicy.yaml | 39 + .../templates/psp-clusterrole.yaml | 19 + .../templates/psp-clusterrolebinding.yaml | 16 + .../templates/rbac-configmap.yaml | 22 + .../kube-state-metrics/templates/role.yaml | 212 + .../templates/rolebinding.yaml | 24 + .../kube-state-metrics/templates/service.yaml | 53 + .../templates/serviceaccount.yaml | 18 + .../templates/servicemonitor.yaml | 120 + .../templates/stsdiscovery-role.yaml | 26 + .../templates/stsdiscovery-rolebinding.yaml | 17 + .../templates/verticalpodautoscaler.yaml | 44 + .../charts/kube-state-metrics/values.yaml | 549 ++ .../prometheus-node-exporter/.helmignore | 21 + .../prometheus-node-exporter/Chart.yaml | 25 + .../charts/prometheus-node-exporter/README.md | 96 + .../templates/NOTES.txt | 29 + .../templates/_helpers.tpl | 237 + .../templates/clusterrole.yaml | 19 + .../templates/clusterrolebinding.yaml | 20 + .../templates/daemonset.yaml | 348 + .../templates/endpoints.yaml | 18 + .../templates/extra-manifests.yaml | 4 + .../templates/networkpolicy.yaml | 27 + .../templates/podmonitor.yaml | 91 + .../templates/psp-clusterrole.yaml | 14 + .../templates/psp-clusterrolebinding.yaml | 16 + .../templates/psp.yaml | 49 + .../templates/rbac-configmap.yaml | 16 + .../templates/service.yaml | 38 + .../templates/serviceaccount.yaml | 18 + .../templates/servicemonitor.yaml | 65 + .../templates/verticalpodautoscaler.yaml | 40 + .../prometheus-node-exporter/values.yaml | 618 ++ .../charts/prometheus-pushgateway/.helmignore | 24 + .../charts/prometheus-pushgateway/Chart.yaml | 24 + .../charts/prometheus-pushgateway/README.md | 88 + .../templates/NOTES.txt | 19 + .../templates/_helpers.tpl | 307 + .../templates/deployment.yaml | 31 + .../templates/extra-manifests.yaml | 8 + .../templates/ingress.yaml | 50 + .../templates/networkpolicy.yaml | 26 + .../prometheus-pushgateway/templates/pdb.yaml | 14 + .../templates/pushgateway-pvc.yaml | 29 + .../templates/secret.yaml | 10 + .../templates/service.yaml | 45 + .../templates/serviceaccount.yaml | 17 + .../templates/servicemonitor.yaml | 51 + .../templates/statefulset.yaml | 49 + .../charts/prometheus-pushgateway/values.yaml | 377 + .../charts/prometheus/templates/NOTES.txt | 113 + .../charts/prometheus/templates/_helpers.tpl | 237 + .../prometheus/templates/clusterrole.yaml | 56 + .../templates/clusterrolebinding.yaml | 16 + .../charts/prometheus/templates/cm.yaml | 103 + .../charts/prometheus/templates/deploy.yaml | 412 ++ .../prometheus/templates/extra-manifests.yaml | 4 + .../prometheus/templates/headless-svc.yaml | 35 + .../charts/prometheus/templates/ingress.yaml | 57 + .../prometheus/templates/network-policy.yaml | 16 + .../charts/prometheus/templates/pdb.yaml | 26 + .../charts/prometheus/templates/psp.yaml | 53 + .../charts/prometheus/templates/pvc.yaml | 43 + .../prometheus/templates/rolebinding.yaml | 18 + .../charts/prometheus/templates/service.yaml | 63 + .../prometheus/templates/serviceaccount.yaml | 16 + .../charts/prometheus/templates/sts.yaml | 436 ++ .../charts/prometheus/templates/vpa.yaml | 26 + .../charts/prometheus/values.schema.json | 752 ++ .../k10/7.5.401/charts/prometheus/values.yaml | 1315 ++++ charts/kasten/k10/7.5.401/config.json | 0 charts/kasten/k10/7.5.401/eula.txt | 459 ++ charts/kasten/k10/7.5.401/files/favicon.png | Bin 0 -> 1802 bytes .../kasten/k10/7.5.401/files/kasten-logo.svg | 24 + charts/kasten/k10/7.5.401/files/styles.css | 113 + .../grafana/dashboards/default/default.json | 6337 +++++++++++++++++ charts/kasten/k10/7.5.401/license | 1 + charts/kasten/k10/7.5.401/questions.yaml | 295 + charts/kasten/k10/7.5.401/templates/NOTES.txt | 215 + .../k10/7.5.401/templates/_definitions.tpl | 259 + .../kasten/k10/7.5.401/templates/_helpers.tpl | 1563 ++++ .../k10/7.5.401/templates/_k10_container.tpl | 1161 +++ .../k10/7.5.401/templates/_k10_image_tag.tpl | 1 + .../k10/7.5.401/templates/_k10_metering.tpl | 352 + .../7.5.401/templates/_k10_serviceimage.tpl | 50 + .../k10/7.5.401/templates/_k10_template.tpl | 249 + .../k10/7.5.401/templates/_prometheus.tpl | 29 + .../templates/aggregatedaudit-policy.yaml | 34 + .../k10/7.5.401/templates/apiservice.yaml | 25 + .../k10/7.5.401/templates/daemonsets.yaml | 26 + .../k10/7.5.401/templates/deployments.yaml | 41 + .../templates/fluentbit-configmap.yaml | 34 + .../templates/frontend-nginx-configmap.yaml | 50 + .../k10/7.5.401/templates/gateway-ext.yaml | 36 + .../kasten/k10/7.5.401/templates/gateway.yaml | 107 + .../kasten/k10/7.5.401/templates/ingress.yaml | 73 + .../k10/7.5.401/templates/k10-config.yaml | 365 + .../k10/7.5.401/templates/k10-eula.yaml | 21 + .../kasten/k10/7.5.401/templates/k10-scc.yaml | 46 + .../7.5.401/templates/kopia-tls-certs.yaml | 33 + .../kasten/k10/7.5.401/templates/license.yaml | 25 + charts/kasten/k10/7.5.401/templates/mc.yaml | 6 + .../7.5.401/templates/mutatingwebhook.yaml | 78 + .../k10/7.5.401/templates/networkpolicy.yaml | 273 + .../templates/ocp-ca-cert-extract-hook.yaml | 218 + .../7.5.401/templates/ocp-consoleplugin.yaml | 27 + .../templates/ocp-plugin-configmap.yaml | 28 + .../templates/ocp-plugin-deployment.yaml | 74 + .../templates/ocp-plugin-proxy-configmap.yaml | 36 + .../ocp-plugin-proxy-deployment.yaml | 67 + .../templates/ocp-plugin-proxy-service.yaml | 25 + .../7.5.401/templates/ocp-plugin-service.yaml | 23 + .../templates/prometheus-configmap.yaml | 75 + .../k10/7.5.401/templates/prometheus-scc.yaml | 41 + .../7.5.401/templates/prometheus-service.yaml | 25 + charts/kasten/k10/7.5.401/templates/rbac.yaml | 386 + .../kasten/k10/7.5.401/templates/route.yaml | 36 + .../kasten/k10/7.5.401/templates/secrets.yaml | 257 + .../7.5.401/templates/secure_deployment.tpl | 16 + .../k10/7.5.401/templates/serviceaccount.yaml | 48 + .../k10/7.5.401/templates/v0services.yaml | 120 + charts/kasten/k10/7.5.401/templates/vap.yaml | 151 + .../templates/workloadIdentityFederation.tpl | 10 + .../{charts}/values/prometheus_values.tpl | 186 + charts/kasten/k10/7.5.401/triallicense | 1 + charts/kasten/k10/7.5.401/values.schema.json | 3124 ++++++++ charts/kasten/k10/7.5.401/values.yaml | 600 ++ index.yaml | 178 +- 556 files changed, 89209 insertions(+), 6 deletions(-) create mode 100644 assets/codefresh/cf-runtime-7.5.5.tgz create mode 100644 assets/instana/instana-agent-2.0.13.tgz create mode 100644 assets/jfrog/artifactory-ha-107.104.10.tgz create mode 100644 assets/jfrog/artifactory-jcr-107.104.10.tgz create mode 100644 assets/kasten/k10-7.5.401.tgz create mode 100644 charts/codefresh/cf-runtime/7.5.5/.helmignore create mode 100644 charts/codefresh/cf-runtime/7.5.5/Chart.yaml create mode 100644 charts/codefresh/cf-runtime/7.5.5/README.md create mode 100644 charts/codefresh/cf-runtime/7.5.5/README.md.gotmpl create mode 100644 charts/codefresh/cf-runtime/7.5.5/files/cleanup-runtime.sh create mode 100644 charts/codefresh/cf-runtime/7.5.5/files/configure-dind-certs.sh create mode 100644 charts/codefresh/cf-runtime/7.5.5/files/init-runtime.sh create mode 100644 charts/codefresh/cf-runtime/7.5.5/files/reconcile-runtime.sh create mode 100644 charts/codefresh/cf-runtime/7.5.5/templates/_components/app-proxy/_deployment.yaml create mode 100644 charts/codefresh/cf-runtime/7.5.5/templates/_components/app-proxy/_env-vars.yaml create mode 100644 charts/codefresh/cf-runtime/7.5.5/templates/_components/app-proxy/_helpers.tpl create mode 100644 charts/codefresh/cf-runtime/7.5.5/templates/_components/app-proxy/_ingress.yaml create mode 100644 charts/codefresh/cf-runtime/7.5.5/templates/_components/app-proxy/_rbac.yaml create mode 100644 charts/codefresh/cf-runtime/7.5.5/templates/_components/app-proxy/_service.yaml create mode 100644 charts/codefresh/cf-runtime/7.5.5/templates/_components/event-exporter/_deployment.yaml create mode 100644 charts/codefresh/cf-runtime/7.5.5/templates/_components/event-exporter/_env-vars.yaml create mode 100644 charts/codefresh/cf-runtime/7.5.5/templates/_components/event-exporter/_helpers.tpl create mode 100644 charts/codefresh/cf-runtime/7.5.5/templates/_components/event-exporter/_rbac.yaml create mode 100644 charts/codefresh/cf-runtime/7.5.5/templates/_components/event-exporter/_service.yaml create mode 100644 charts/codefresh/cf-runtime/7.5.5/templates/_components/event-exporter/_serviceMontor.yaml create mode 100644 charts/codefresh/cf-runtime/7.5.5/templates/_components/monitor/_deployment.yaml create mode 100644 charts/codefresh/cf-runtime/7.5.5/templates/_components/monitor/_env-vars.yaml create mode 100644 charts/codefresh/cf-runtime/7.5.5/templates/_components/monitor/_helpers.tpl create mode 100644 charts/codefresh/cf-runtime/7.5.5/templates/_components/monitor/_rbac.yaml create mode 100644 charts/codefresh/cf-runtime/7.5.5/templates/_components/monitor/_service.yaml create mode 100644 charts/codefresh/cf-runtime/7.5.5/templates/_components/runner/_deployment.yaml create mode 100644 charts/codefresh/cf-runtime/7.5.5/templates/_components/runner/_helpers.tpl create mode 100644 charts/codefresh/cf-runtime/7.5.5/templates/_components/runner/_rbac.yaml create mode 100644 charts/codefresh/cf-runtime/7.5.5/templates/_components/runner/environment-variables/_init-container.yaml create mode 100644 charts/codefresh/cf-runtime/7.5.5/templates/_components/runner/environment-variables/_main-container.yaml create mode 100644 charts/codefresh/cf-runtime/7.5.5/templates/_components/runner/environment-variables/_sidecar-container.yaml create mode 100644 charts/codefresh/cf-runtime/7.5.5/templates/_components/volume-provisioner/_cronjob.yaml create mode 100644 charts/codefresh/cf-runtime/7.5.5/templates/_components/volume-provisioner/_daemonset.yaml create mode 100644 charts/codefresh/cf-runtime/7.5.5/templates/_components/volume-provisioner/_deployment.yaml create mode 100644 charts/codefresh/cf-runtime/7.5.5/templates/_components/volume-provisioner/_env-vars.yaml create mode 100644 charts/codefresh/cf-runtime/7.5.5/templates/_components/volume-provisioner/_helpers.tpl create mode 100644 charts/codefresh/cf-runtime/7.5.5/templates/_components/volume-provisioner/_rbac.yaml create mode 100644 charts/codefresh/cf-runtime/7.5.5/templates/_components/volume-provisioner/_secret.yaml create mode 100644 charts/codefresh/cf-runtime/7.5.5/templates/_components/volume-provisioner/_storageclass.yaml create mode 100644 charts/codefresh/cf-runtime/7.5.5/templates/_helpers.tpl create mode 100644 charts/codefresh/cf-runtime/7.5.5/templates/app-proxy/deployment.yaml create mode 100644 charts/codefresh/cf-runtime/7.5.5/templates/app-proxy/ingress.yaml create mode 100644 charts/codefresh/cf-runtime/7.5.5/templates/app-proxy/rbac.yaml create mode 100644 charts/codefresh/cf-runtime/7.5.5/templates/app-proxy/service.yaml create mode 100644 charts/codefresh/cf-runtime/7.5.5/templates/event-exporter/deployment.yaml create mode 100644 charts/codefresh/cf-runtime/7.5.5/templates/event-exporter/rbac.yaml create mode 100644 charts/codefresh/cf-runtime/7.5.5/templates/event-exporter/service.yaml create mode 100644 charts/codefresh/cf-runtime/7.5.5/templates/extra/extra-resources.yaml create mode 100644 charts/codefresh/cf-runtime/7.5.5/templates/extra/runtime-images-cm.yaml create mode 100644 charts/codefresh/cf-runtime/7.5.5/templates/hooks/post-install/cm-update-runtime.yaml create mode 100644 charts/codefresh/cf-runtime/7.5.5/templates/hooks/post-install/job-gencerts-dind.yaml create mode 100644 charts/codefresh/cf-runtime/7.5.5/templates/hooks/post-install/job-update-runtime.yaml create mode 100644 charts/codefresh/cf-runtime/7.5.5/templates/hooks/post-install/rbac-gencerts-dind.yaml create mode 100644 charts/codefresh/cf-runtime/7.5.5/templates/hooks/pre-delete/job-cleanup-resources.yaml create mode 100644 charts/codefresh/cf-runtime/7.5.5/templates/hooks/pre-delete/rbac-cleanup-resources.yaml create mode 100644 charts/codefresh/cf-runtime/7.5.5/templates/monitor/deployment.yaml create mode 100644 charts/codefresh/cf-runtime/7.5.5/templates/monitor/rbac.yaml create mode 100644 charts/codefresh/cf-runtime/7.5.5/templates/monitor/service.yaml create mode 100644 charts/codefresh/cf-runtime/7.5.5/templates/other/external-secrets.yaml create mode 100644 charts/codefresh/cf-runtime/7.5.5/templates/other/podMonitor.yaml create mode 100644 charts/codefresh/cf-runtime/7.5.5/templates/other/serviceMonitor.yaml create mode 100644 charts/codefresh/cf-runtime/7.5.5/templates/runner/deployment.yaml create mode 100644 charts/codefresh/cf-runtime/7.5.5/templates/runner/rbac.yaml create mode 100644 charts/codefresh/cf-runtime/7.5.5/templates/runtime/_helpers.tpl create mode 100644 charts/codefresh/cf-runtime/7.5.5/templates/runtime/cm-dind-daemon.yaml create mode 100644 charts/codefresh/cf-runtime/7.5.5/templates/runtime/rbac.yaml create mode 100644 charts/codefresh/cf-runtime/7.5.5/templates/runtime/runtime-env-spec-tmpl.yaml create mode 100644 charts/codefresh/cf-runtime/7.5.5/templates/runtime/secret.yaml create mode 100644 charts/codefresh/cf-runtime/7.5.5/templates/runtime/svc-dind.yaml create mode 100644 charts/codefresh/cf-runtime/7.5.5/templates/volume-provisioner/cronjob.yaml create mode 100644 charts/codefresh/cf-runtime/7.5.5/templates/volume-provisioner/daemonset.yaml create mode 100644 charts/codefresh/cf-runtime/7.5.5/templates/volume-provisioner/deployment.yaml create mode 100644 charts/codefresh/cf-runtime/7.5.5/templates/volume-provisioner/rbac.yaml create mode 100644 charts/codefresh/cf-runtime/7.5.5/templates/volume-provisioner/secret.yaml create mode 100644 charts/codefresh/cf-runtime/7.5.5/templates/volume-provisioner/storageclass.yaml create mode 100644 charts/codefresh/cf-runtime/7.5.5/values-rootless.yaml create mode 100644 charts/codefresh/cf-runtime/7.5.5/values.yaml create mode 100644 charts/instana/instana-agent/2.0.13/.helmignore create mode 100644 charts/instana/instana-agent/2.0.13/Chart.yaml create mode 100644 charts/instana/instana-agent/2.0.13/DEPLOYMENT.md create mode 100644 charts/instana/instana-agent/2.0.13/README.md create mode 100644 charts/instana/instana-agent/2.0.13/app-readme.md create mode 100644 charts/instana/instana-agent/2.0.13/crds/operator_customresourcedefinition_agents_instana_io.yml create mode 100644 charts/instana/instana-agent/2.0.13/kubernetes.deployment.enabled.png create mode 100644 charts/instana/instana-agent/2.0.13/kubernetes_deployment_mode_diagram.py create mode 100644 charts/instana/instana-agent/2.0.13/questions.yml create mode 100644 charts/instana/instana-agent/2.0.13/templates/NOTES.txt create mode 100644 charts/instana/instana-agent/2.0.13/templates/agent.yml create mode 100644 charts/instana/instana-agent/2.0.13/templates/operator_clusterrole_instana-agent-clusterrole.yml create mode 100644 charts/instana/instana-agent/2.0.13/templates/operator_clusterrole_leader-election-role.yml create mode 100644 charts/instana/instana-agent/2.0.13/templates/operator_clusterrolebinding_instana-agent-clusterrolebinding.yml create mode 100644 charts/instana/instana-agent/2.0.13/templates/operator_clusterrolebinding_leader-election-rolebinding.yml create mode 100644 charts/instana/instana-agent/2.0.13/templates/operator_configmap_manager-config.yml create mode 100644 charts/instana/instana-agent/2.0.13/templates/operator_deployment_instana-agent-controller-manager.yml create mode 100644 charts/instana/instana-agent/2.0.13/templates/operator_serviceaccount_instana-agent-operator.yml create mode 100644 charts/instana/instana-agent/2.0.13/values.yaml create mode 100644 charts/jfrog/artifactory-ha/107.104.10/.helmignore create mode 100644 charts/jfrog/artifactory-ha/107.104.10/CHANGELOG.md create mode 100644 charts/jfrog/artifactory-ha/107.104.10/Chart.lock create mode 100644 charts/jfrog/artifactory-ha/107.104.10/Chart.yaml create mode 100644 charts/jfrog/artifactory-ha/107.104.10/LICENSE create mode 100644 charts/jfrog/artifactory-ha/107.104.10/README.md create mode 100644 charts/jfrog/artifactory-ha/107.104.10/app-readme.md create mode 100644 charts/jfrog/artifactory-ha/107.104.10/charts/postgresql/.helmignore create mode 100644 charts/jfrog/artifactory-ha/107.104.10/charts/postgresql/Chart.lock create mode 100644 charts/jfrog/artifactory-ha/107.104.10/charts/postgresql/Chart.yaml create mode 100644 charts/jfrog/artifactory-ha/107.104.10/charts/postgresql/README.md create mode 100644 charts/jfrog/artifactory-ha/107.104.10/charts/postgresql/charts/common/.helmignore create mode 100644 charts/jfrog/artifactory-ha/107.104.10/charts/postgresql/charts/common/Chart.yaml create mode 100644 charts/jfrog/artifactory-ha/107.104.10/charts/postgresql/charts/common/README.md create mode 100644 charts/jfrog/artifactory-ha/107.104.10/charts/postgresql/charts/common/templates/_affinities.tpl create mode 100644 charts/jfrog/artifactory-ha/107.104.10/charts/postgresql/charts/common/templates/_capabilities.tpl create mode 100644 charts/jfrog/artifactory-ha/107.104.10/charts/postgresql/charts/common/templates/_errors.tpl create mode 100644 charts/jfrog/artifactory-ha/107.104.10/charts/postgresql/charts/common/templates/_images.tpl create mode 100644 charts/jfrog/artifactory-ha/107.104.10/charts/postgresql/charts/common/templates/_ingress.tpl create mode 100644 charts/jfrog/artifactory-ha/107.104.10/charts/postgresql/charts/common/templates/_labels.tpl create mode 100644 charts/jfrog/artifactory-ha/107.104.10/charts/postgresql/charts/common/templates/_names.tpl create mode 100644 charts/jfrog/artifactory-ha/107.104.10/charts/postgresql/charts/common/templates/_secrets.tpl create mode 100644 charts/jfrog/artifactory-ha/107.104.10/charts/postgresql/charts/common/templates/_storage.tpl create mode 100644 charts/jfrog/artifactory-ha/107.104.10/charts/postgresql/charts/common/templates/_tplvalues.tpl create mode 100644 charts/jfrog/artifactory-ha/107.104.10/charts/postgresql/charts/common/templates/_utils.tpl create mode 100644 charts/jfrog/artifactory-ha/107.104.10/charts/postgresql/charts/common/templates/_warnings.tpl create mode 100644 charts/jfrog/artifactory-ha/107.104.10/charts/postgresql/charts/common/templates/validations/_cassandra.tpl create mode 100644 charts/jfrog/artifactory-ha/107.104.10/charts/postgresql/charts/common/templates/validations/_mariadb.tpl create mode 100644 charts/jfrog/artifactory-ha/107.104.10/charts/postgresql/charts/common/templates/validations/_mongodb.tpl create mode 100644 charts/jfrog/artifactory-ha/107.104.10/charts/postgresql/charts/common/templates/validations/_postgresql.tpl create mode 100644 charts/jfrog/artifactory-ha/107.104.10/charts/postgresql/charts/common/templates/validations/_redis.tpl create mode 100644 charts/jfrog/artifactory-ha/107.104.10/charts/postgresql/charts/common/templates/validations/_validations.tpl create mode 100644 charts/jfrog/artifactory-ha/107.104.10/charts/postgresql/charts/common/values.yaml create mode 100644 charts/jfrog/artifactory-ha/107.104.10/charts/postgresql/ci/commonAnnotations.yaml create mode 100644 charts/jfrog/artifactory-ha/107.104.10/charts/postgresql/ci/default-values.yaml create mode 100644 charts/jfrog/artifactory-ha/107.104.10/charts/postgresql/ci/shmvolume-disabled-values.yaml create mode 100644 charts/jfrog/artifactory-ha/107.104.10/charts/postgresql/files/README.md create mode 100644 charts/jfrog/artifactory-ha/107.104.10/charts/postgresql/files/conf.d/README.md create mode 100644 charts/jfrog/artifactory-ha/107.104.10/charts/postgresql/files/docker-entrypoint-initdb.d/README.md create mode 100644 charts/jfrog/artifactory-ha/107.104.10/charts/postgresql/templates/NOTES.txt create mode 100644 charts/jfrog/artifactory-ha/107.104.10/charts/postgresql/templates/_helpers.tpl create mode 100644 charts/jfrog/artifactory-ha/107.104.10/charts/postgresql/templates/configmap.yaml create mode 100644 charts/jfrog/artifactory-ha/107.104.10/charts/postgresql/templates/extended-config-configmap.yaml create mode 100644 charts/jfrog/artifactory-ha/107.104.10/charts/postgresql/templates/extra-list.yaml create mode 100644 charts/jfrog/artifactory-ha/107.104.10/charts/postgresql/templates/initialization-configmap.yaml create mode 100644 charts/jfrog/artifactory-ha/107.104.10/charts/postgresql/templates/metrics-configmap.yaml create mode 100644 charts/jfrog/artifactory-ha/107.104.10/charts/postgresql/templates/metrics-svc.yaml create mode 100644 charts/jfrog/artifactory-ha/107.104.10/charts/postgresql/templates/networkpolicy.yaml create mode 100644 charts/jfrog/artifactory-ha/107.104.10/charts/postgresql/templates/podsecuritypolicy.yaml create mode 100644 charts/jfrog/artifactory-ha/107.104.10/charts/postgresql/templates/prometheusrule.yaml create mode 100644 charts/jfrog/artifactory-ha/107.104.10/charts/postgresql/templates/role.yaml create mode 100644 charts/jfrog/artifactory-ha/107.104.10/charts/postgresql/templates/rolebinding.yaml create mode 100644 charts/jfrog/artifactory-ha/107.104.10/charts/postgresql/templates/secrets.yaml create mode 100644 charts/jfrog/artifactory-ha/107.104.10/charts/postgresql/templates/serviceaccount.yaml create mode 100644 charts/jfrog/artifactory-ha/107.104.10/charts/postgresql/templates/servicemonitor.yaml create mode 100644 charts/jfrog/artifactory-ha/107.104.10/charts/postgresql/templates/statefulset-readreplicas.yaml create mode 100644 charts/jfrog/artifactory-ha/107.104.10/charts/postgresql/templates/statefulset.yaml create mode 100644 charts/jfrog/artifactory-ha/107.104.10/charts/postgresql/templates/svc-headless.yaml create mode 100644 charts/jfrog/artifactory-ha/107.104.10/charts/postgresql/templates/svc-read.yaml create mode 100644 charts/jfrog/artifactory-ha/107.104.10/charts/postgresql/templates/svc.yaml create mode 100644 charts/jfrog/artifactory-ha/107.104.10/charts/postgresql/values.schema.json create mode 100644 charts/jfrog/artifactory-ha/107.104.10/charts/postgresql/values.yaml create mode 100644 charts/jfrog/artifactory-ha/107.104.10/ci/access-tls-values.yaml create mode 100644 charts/jfrog/artifactory-ha/107.104.10/ci/default-values.yaml create mode 100644 charts/jfrog/artifactory-ha/107.104.10/ci/global-values.yaml create mode 100644 charts/jfrog/artifactory-ha/107.104.10/ci/large-values.yaml create mode 100644 charts/jfrog/artifactory-ha/107.104.10/ci/loggers-values.yaml create mode 100644 charts/jfrog/artifactory-ha/107.104.10/ci/medium-values.yaml create mode 100644 charts/jfrog/artifactory-ha/107.104.10/ci/migration-disabled-values.yaml create mode 100644 charts/jfrog/artifactory-ha/107.104.10/ci/nginx-autoreload-values.yaml create mode 100644 charts/jfrog/artifactory-ha/107.104.10/ci/rtsplit-access-tls-values.yaml create mode 100644 charts/jfrog/artifactory-ha/107.104.10/ci/rtsplit-values.yaml create mode 100644 charts/jfrog/artifactory-ha/107.104.10/ci/small-values.yaml create mode 100644 charts/jfrog/artifactory-ha/107.104.10/ci/test-values.yaml create mode 100644 charts/jfrog/artifactory-ha/107.104.10/files/binarystore.xml create mode 100644 charts/jfrog/artifactory-ha/107.104.10/files/installer-info.json create mode 100644 charts/jfrog/artifactory-ha/107.104.10/files/migrate.sh create mode 100644 charts/jfrog/artifactory-ha/107.104.10/files/migrationHelmInfo.yaml create mode 100644 charts/jfrog/artifactory-ha/107.104.10/files/migrationStatus.sh create mode 100644 charts/jfrog/artifactory-ha/107.104.10/files/nginx-artifactory-conf.yaml create mode 100644 charts/jfrog/artifactory-ha/107.104.10/files/nginx-main-conf.yaml create mode 100644 charts/jfrog/artifactory-ha/107.104.10/files/system.yaml create mode 100644 charts/jfrog/artifactory-ha/107.104.10/logo/artifactory-logo.png create mode 100644 charts/jfrog/artifactory-ha/107.104.10/questions.yml create mode 100644 charts/jfrog/artifactory-ha/107.104.10/sizing/artifactory-2xlarge.yaml create mode 100644 charts/jfrog/artifactory-ha/107.104.10/sizing/artifactory-large.yaml create mode 100644 charts/jfrog/artifactory-ha/107.104.10/sizing/artifactory-medium.yaml create mode 100644 charts/jfrog/artifactory-ha/107.104.10/sizing/artifactory-small.yaml create mode 100644 charts/jfrog/artifactory-ha/107.104.10/sizing/artifactory-xlarge.yaml create mode 100644 charts/jfrog/artifactory-ha/107.104.10/sizing/artifactory-xsmall.yaml create mode 100644 charts/jfrog/artifactory-ha/107.104.10/templates/NOTES.txt create mode 100644 charts/jfrog/artifactory-ha/107.104.10/templates/_helpers.tpl create mode 100644 charts/jfrog/artifactory-ha/107.104.10/templates/_system-yaml-render.tpl create mode 100644 charts/jfrog/artifactory-ha/107.104.10/templates/additional-resources.yaml create mode 100644 charts/jfrog/artifactory-ha/107.104.10/templates/admin-bootstrap-creds.yaml create mode 100644 charts/jfrog/artifactory-ha/107.104.10/templates/artifactory-access-config.yaml create mode 100644 charts/jfrog/artifactory-ha/107.104.10/templates/artifactory-binarystore-secret.yaml create mode 100644 charts/jfrog/artifactory-ha/107.104.10/templates/artifactory-configmaps.yaml create mode 100644 charts/jfrog/artifactory-ha/107.104.10/templates/artifactory-custom-secrets.yaml create mode 100644 charts/jfrog/artifactory-ha/107.104.10/templates/artifactory-database-secrets.yaml create mode 100644 charts/jfrog/artifactory-ha/107.104.10/templates/artifactory-gcp-credentials-secret.yaml create mode 100644 charts/jfrog/artifactory-ha/107.104.10/templates/artifactory-installer-info.yaml create mode 100644 charts/jfrog/artifactory-ha/107.104.10/templates/artifactory-license-secret.yaml create mode 100644 charts/jfrog/artifactory-ha/107.104.10/templates/artifactory-migration-scripts.yaml create mode 100644 charts/jfrog/artifactory-ha/107.104.10/templates/artifactory-networkpolicy.yaml create mode 100644 charts/jfrog/artifactory-ha/107.104.10/templates/artifactory-nfs-pvc.yaml create mode 100644 charts/jfrog/artifactory-ha/107.104.10/templates/artifactory-node-pdb.yaml create mode 100644 charts/jfrog/artifactory-ha/107.104.10/templates/artifactory-node-statefulset.yaml create mode 100644 charts/jfrog/artifactory-ha/107.104.10/templates/artifactory-primary-pdb.yaml create mode 100644 charts/jfrog/artifactory-ha/107.104.10/templates/artifactory-primary-service.yaml create mode 100644 charts/jfrog/artifactory-ha/107.104.10/templates/artifactory-primary-statefulset.yaml create mode 100644 charts/jfrog/artifactory-ha/107.104.10/templates/artifactory-priority-class.yaml create mode 100644 charts/jfrog/artifactory-ha/107.104.10/templates/artifactory-role.yaml create mode 100644 charts/jfrog/artifactory-ha/107.104.10/templates/artifactory-rolebinding.yaml create mode 100644 charts/jfrog/artifactory-ha/107.104.10/templates/artifactory-secrets.yaml create mode 100644 charts/jfrog/artifactory-ha/107.104.10/templates/artifactory-service-grpc.yaml create mode 100644 charts/jfrog/artifactory-ha/107.104.10/templates/artifactory-service.yaml create mode 100644 charts/jfrog/artifactory-ha/107.104.10/templates/artifactory-serviceaccount.yaml create mode 100644 charts/jfrog/artifactory-ha/107.104.10/templates/artifactory-storage-pvc.yaml create mode 100644 charts/jfrog/artifactory-ha/107.104.10/templates/artifactory-system-yaml.yaml create mode 100644 charts/jfrog/artifactory-ha/107.104.10/templates/artifactory-unified-secret.yaml create mode 100644 charts/jfrog/artifactory-ha/107.104.10/templates/filebeat-configmap.yaml create mode 100644 charts/jfrog/artifactory-ha/107.104.10/templates/ingress-grpc.yaml create mode 100644 charts/jfrog/artifactory-ha/107.104.10/templates/ingress.yaml create mode 100644 charts/jfrog/artifactory-ha/107.104.10/templates/logger-configmap.yaml create mode 100644 charts/jfrog/artifactory-ha/107.104.10/templates/nginx-artifactory-conf.yaml create mode 100644 charts/jfrog/artifactory-ha/107.104.10/templates/nginx-certificate-secret.yaml create mode 100644 charts/jfrog/artifactory-ha/107.104.10/templates/nginx-conf.yaml create mode 100644 charts/jfrog/artifactory-ha/107.104.10/templates/nginx-deployment.yaml create mode 100644 charts/jfrog/artifactory-ha/107.104.10/templates/nginx-pdb.yaml create mode 100644 charts/jfrog/artifactory-ha/107.104.10/templates/nginx-pvc.yaml create mode 100644 charts/jfrog/artifactory-ha/107.104.10/templates/nginx-scripts-conf.yaml create mode 100644 charts/jfrog/artifactory-ha/107.104.10/templates/nginx-service.yaml create mode 100644 charts/jfrog/artifactory-ha/107.104.10/templates/rtfs-deployment.yaml create mode 100644 charts/jfrog/artifactory-ha/107.104.10/templates/rtfs-hpa.yaml create mode 100644 charts/jfrog/artifactory-ha/107.104.10/templates/rtfs-properties-configmap.yaml create mode 100644 charts/jfrog/artifactory-ha/107.104.10/templates/rtfs-service.yaml create mode 100644 charts/jfrog/artifactory-ha/107.104.10/values.yaml create mode 100644 charts/jfrog/artifactory-jcr/107.104.10/CHANGELOG.md create mode 100644 charts/jfrog/artifactory-jcr/107.104.10/Chart.yaml create mode 100644 charts/jfrog/artifactory-jcr/107.104.10/LICENSE create mode 100644 charts/jfrog/artifactory-jcr/107.104.10/README.md create mode 100644 charts/jfrog/artifactory-jcr/107.104.10/app-readme.md create mode 100644 charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/.helmignore create mode 100644 charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/CHANGELOG.md create mode 100644 charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/Chart.lock create mode 100644 charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/Chart.yaml create mode 100644 charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/LICENSE create mode 100644 charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/README.md create mode 100644 charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/charts/postgresql/.helmignore create mode 100644 charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/charts/postgresql/Chart.lock create mode 100644 charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/charts/postgresql/Chart.yaml create mode 100644 charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/charts/postgresql/README.md create mode 100644 charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/charts/postgresql/charts/common/.helmignore create mode 100644 charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/charts/postgresql/charts/common/Chart.yaml create mode 100644 charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/charts/postgresql/charts/common/README.md create mode 100644 charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/charts/postgresql/charts/common/templates/_affinities.tpl create mode 100644 charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/charts/postgresql/charts/common/templates/_capabilities.tpl create mode 100644 charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/charts/postgresql/charts/common/templates/_errors.tpl create mode 100644 charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/charts/postgresql/charts/common/templates/_images.tpl create mode 100644 charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/charts/postgresql/charts/common/templates/_ingress.tpl create mode 100644 charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/charts/postgresql/charts/common/templates/_labels.tpl create mode 100644 charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/charts/postgresql/charts/common/templates/_names.tpl create mode 100644 charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/charts/postgresql/charts/common/templates/_secrets.tpl create mode 100644 charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/charts/postgresql/charts/common/templates/_storage.tpl create mode 100644 charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/charts/postgresql/charts/common/templates/_tplvalues.tpl create mode 100644 charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/charts/postgresql/charts/common/templates/_utils.tpl create mode 100644 charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/charts/postgresql/charts/common/templates/_warnings.tpl create mode 100644 charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/charts/postgresql/charts/common/templates/validations/_cassandra.tpl create mode 100644 charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/charts/postgresql/charts/common/templates/validations/_mariadb.tpl create mode 100644 charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/charts/postgresql/charts/common/templates/validations/_mongodb.tpl create mode 100644 charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/charts/postgresql/charts/common/templates/validations/_postgresql.tpl create mode 100644 charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/charts/postgresql/charts/common/templates/validations/_redis.tpl create mode 100644 charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/charts/postgresql/charts/common/templates/validations/_validations.tpl create mode 100644 charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/charts/postgresql/charts/common/values.yaml create mode 100644 charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/charts/postgresql/ci/commonAnnotations.yaml create mode 100644 charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/charts/postgresql/ci/default-values.yaml create mode 100644 charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/charts/postgresql/ci/shmvolume-disabled-values.yaml create mode 100644 charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/charts/postgresql/files/README.md create mode 100644 charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/charts/postgresql/files/conf.d/README.md create mode 100644 charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/charts/postgresql/files/docker-entrypoint-initdb.d/README.md create mode 100644 charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/charts/postgresql/templates/NOTES.txt create mode 100644 charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/charts/postgresql/templates/_helpers.tpl create mode 100644 charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/charts/postgresql/templates/configmap.yaml create mode 100644 charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/charts/postgresql/templates/extended-config-configmap.yaml create mode 100644 charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/charts/postgresql/templates/extra-list.yaml create mode 100644 charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/charts/postgresql/templates/initialization-configmap.yaml create mode 100644 charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/charts/postgresql/templates/metrics-configmap.yaml create mode 100644 charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/charts/postgresql/templates/metrics-svc.yaml create mode 100644 charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/charts/postgresql/templates/networkpolicy.yaml create mode 100644 charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/charts/postgresql/templates/podsecuritypolicy.yaml create mode 100644 charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/charts/postgresql/templates/prometheusrule.yaml create mode 100644 charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/charts/postgresql/templates/role.yaml create mode 100644 charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/charts/postgresql/templates/rolebinding.yaml create mode 100644 charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/charts/postgresql/templates/secrets.yaml create mode 100644 charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/charts/postgresql/templates/serviceaccount.yaml create mode 100644 charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/charts/postgresql/templates/servicemonitor.yaml create mode 100644 charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/charts/postgresql/templates/statefulset-readreplicas.yaml create mode 100644 charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/charts/postgresql/templates/statefulset.yaml create mode 100644 charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/charts/postgresql/templates/svc-headless.yaml create mode 100644 charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/charts/postgresql/templates/svc-read.yaml create mode 100644 charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/charts/postgresql/templates/svc.yaml create mode 100644 charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/charts/postgresql/values.schema.json create mode 100644 charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/charts/postgresql/values.yaml create mode 100644 charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/ci/access-tls-values.yaml create mode 100644 charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/ci/default-values.yaml create mode 100644 charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/ci/derby-test-values.yaml create mode 100644 charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/ci/global-values.yaml create mode 100644 charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/ci/large-values.yaml create mode 100644 charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/ci/loggers-values.yaml create mode 100644 charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/ci/medium-values.yaml create mode 100644 charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/ci/migration-disabled-values.yaml create mode 100644 charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/ci/nginx-autoreload-values.yaml create mode 100644 charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/ci/rtsplit-values-access-tls-values.yaml create mode 100644 charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/ci/rtsplit-values.yaml create mode 100644 charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/ci/small-values.yaml create mode 100644 charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/ci/test-values.yaml create mode 100644 charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/files/binarystore.xml create mode 100644 charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/files/installer-info.json create mode 100644 charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/files/migrate.sh create mode 100644 charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/files/migrationHelmInfo.yaml create mode 100644 charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/files/migrationStatus.sh create mode 100644 charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/files/nginx-artifactory-conf.yaml create mode 100644 charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/files/nginx-main-conf.yaml create mode 100644 charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/files/system.yaml create mode 100644 charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/logo/artifactory-logo.png create mode 100644 charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/sizing/artifactory-2xlarge.yaml create mode 100644 charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/sizing/artifactory-large.yaml create mode 100644 charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/sizing/artifactory-medium.yaml create mode 100644 charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/sizing/artifactory-small.yaml create mode 100644 charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/sizing/artifactory-xlarge.yaml create mode 100644 charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/sizing/artifactory-xsmall.yaml create mode 100644 charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/templates/NOTES.txt create mode 100644 charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/templates/_helpers.tpl create mode 100644 charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/templates/_system-yaml-render.tpl create mode 100644 charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/templates/additional-resources.yaml create mode 100644 charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/templates/admin-bootstrap-creds.yaml create mode 100644 charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/templates/artifactory-access-config.yaml create mode 100644 charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/templates/artifactory-binarystore-secret.yaml create mode 100644 charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/templates/artifactory-configmaps.yaml create mode 100644 charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/templates/artifactory-custom-secrets.yaml create mode 100644 charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/templates/artifactory-database-secrets.yaml create mode 100644 charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/templates/artifactory-gcp-credentials-secret.yaml create mode 100644 charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/templates/artifactory-hpa.yaml create mode 100644 charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/templates/artifactory-installer-info.yaml create mode 100644 charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/templates/artifactory-license-secret.yaml create mode 100644 charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/templates/artifactory-migration-scripts.yaml create mode 100644 charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/templates/artifactory-networkpolicy.yaml create mode 100644 charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/templates/artifactory-nfs-pvc.yaml create mode 100644 charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/templates/artifactory-pdb.yaml create mode 100644 charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/templates/artifactory-priority-class.yaml create mode 100644 charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/templates/artifactory-role.yaml create mode 100644 charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/templates/artifactory-rolebinding.yaml create mode 100644 charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/templates/artifactory-secrets.yaml create mode 100644 charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/templates/artifactory-service-grpc.yaml create mode 100644 charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/templates/artifactory-service.yaml create mode 100644 charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/templates/artifactory-serviceaccount.yaml create mode 100644 charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/templates/artifactory-statefulset.yaml create mode 100644 charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/templates/artifactory-system-yaml.yaml create mode 100644 charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/templates/artifactory-unified-secret.yaml create mode 100644 charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/templates/filebeat-configmap.yaml create mode 100644 charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/templates/ingress-grpc.yaml create mode 100644 charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/templates/ingress.yaml create mode 100644 charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/templates/logger-configmap.yaml create mode 100644 charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/templates/nginx-artifactory-conf.yaml create mode 100644 charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/templates/nginx-certificate-secret.yaml create mode 100644 charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/templates/nginx-conf.yaml create mode 100644 charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/templates/nginx-deployment.yaml create mode 100644 charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/templates/nginx-pdb.yaml create mode 100644 charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/templates/nginx-pvc.yaml create mode 100644 charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/templates/nginx-scripts-conf.yaml create mode 100644 charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/templates/nginx-service.yaml create mode 100644 charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/templates/rtfs-deployment.yaml create mode 100644 charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/templates/rtfs-hpa.yaml create mode 100644 charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/templates/rtfs-properties-configmap.yaml create mode 100644 charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/templates/rtfs-service.yaml create mode 100644 charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/values.yaml create mode 100644 charts/jfrog/artifactory-jcr/107.104.10/ci/default-values.yaml create mode 100644 charts/jfrog/artifactory-jcr/107.104.10/logo/jcr-logo.png create mode 100644 charts/jfrog/artifactory-jcr/107.104.10/questions.yml create mode 100644 charts/jfrog/artifactory-jcr/107.104.10/templates/NOTES.txt create mode 100644 charts/jfrog/artifactory-jcr/107.104.10/values.yaml create mode 100644 charts/kasten/k10/7.5.401/Chart.lock create mode 100644 charts/kasten/k10/7.5.401/Chart.yaml create mode 100644 charts/kasten/k10/7.5.401/README.md create mode 100644 charts/kasten/k10/7.5.401/app-readme.md create mode 100644 charts/kasten/k10/7.5.401/charts/prometheus/.helmignore create mode 100644 charts/kasten/k10/7.5.401/charts/prometheus/Chart.lock create mode 100644 charts/kasten/k10/7.5.401/charts/prometheus/Chart.yaml create mode 100644 charts/kasten/k10/7.5.401/charts/prometheus/OWNERS create mode 100644 charts/kasten/k10/7.5.401/charts/prometheus/README.md create mode 100644 charts/kasten/k10/7.5.401/charts/prometheus/charts/alertmanager/.helmignore create mode 100644 charts/kasten/k10/7.5.401/charts/prometheus/charts/alertmanager/Chart.yaml create mode 100644 charts/kasten/k10/7.5.401/charts/prometheus/charts/alertmanager/README.md create mode 100644 charts/kasten/k10/7.5.401/charts/prometheus/charts/alertmanager/ci/config-reload-values.yaml create mode 100644 charts/kasten/k10/7.5.401/charts/prometheus/charts/alertmanager/templates/NOTES.txt create mode 100644 charts/kasten/k10/7.5.401/charts/prometheus/charts/alertmanager/templates/_helpers.tpl create mode 100644 charts/kasten/k10/7.5.401/charts/prometheus/charts/alertmanager/templates/configmap.yaml create mode 100644 charts/kasten/k10/7.5.401/charts/prometheus/charts/alertmanager/templates/ingress.yaml create mode 100644 charts/kasten/k10/7.5.401/charts/prometheus/charts/alertmanager/templates/ingressperreplica.yaml create mode 100644 charts/kasten/k10/7.5.401/charts/prometheus/charts/alertmanager/templates/pdb.yaml create mode 100644 charts/kasten/k10/7.5.401/charts/prometheus/charts/alertmanager/templates/serviceaccount.yaml create mode 100644 charts/kasten/k10/7.5.401/charts/prometheus/charts/alertmanager/templates/serviceperreplica.yaml create mode 100644 charts/kasten/k10/7.5.401/charts/prometheus/charts/alertmanager/templates/services.yaml create mode 100644 charts/kasten/k10/7.5.401/charts/prometheus/charts/alertmanager/templates/statefulset.yaml create mode 100644 charts/kasten/k10/7.5.401/charts/prometheus/charts/alertmanager/templates/tests/test-connection.yaml create mode 100644 charts/kasten/k10/7.5.401/charts/prometheus/charts/alertmanager/templates/vpa.yaml create mode 100644 charts/kasten/k10/7.5.401/charts/prometheus/charts/alertmanager/values.schema.json create mode 100644 charts/kasten/k10/7.5.401/charts/prometheus/charts/alertmanager/values.yaml create mode 100644 charts/kasten/k10/7.5.401/charts/prometheus/charts/kube-state-metrics/.helmignore create mode 100644 charts/kasten/k10/7.5.401/charts/prometheus/charts/kube-state-metrics/Chart.yaml create mode 100644 charts/kasten/k10/7.5.401/charts/prometheus/charts/kube-state-metrics/README.md create mode 100644 charts/kasten/k10/7.5.401/charts/prometheus/charts/kube-state-metrics/templates/NOTES.txt create mode 100644 charts/kasten/k10/7.5.401/charts/prometheus/charts/kube-state-metrics/templates/_helpers.tpl create mode 100644 charts/kasten/k10/7.5.401/charts/prometheus/charts/kube-state-metrics/templates/ciliumnetworkpolicy.yaml create mode 100644 charts/kasten/k10/7.5.401/charts/prometheus/charts/kube-state-metrics/templates/clusterrolebinding.yaml create mode 100644 charts/kasten/k10/7.5.401/charts/prometheus/charts/kube-state-metrics/templates/crs-configmap.yaml create mode 100644 charts/kasten/k10/7.5.401/charts/prometheus/charts/kube-state-metrics/templates/deployment.yaml create mode 100644 charts/kasten/k10/7.5.401/charts/prometheus/charts/kube-state-metrics/templates/extra-manifests.yaml create mode 100644 charts/kasten/k10/7.5.401/charts/prometheus/charts/kube-state-metrics/templates/kubeconfig-secret.yaml create mode 100644 charts/kasten/k10/7.5.401/charts/prometheus/charts/kube-state-metrics/templates/networkpolicy.yaml create mode 100644 charts/kasten/k10/7.5.401/charts/prometheus/charts/kube-state-metrics/templates/pdb.yaml create mode 100644 charts/kasten/k10/7.5.401/charts/prometheus/charts/kube-state-metrics/templates/podsecuritypolicy.yaml create mode 100644 charts/kasten/k10/7.5.401/charts/prometheus/charts/kube-state-metrics/templates/psp-clusterrole.yaml create mode 100644 charts/kasten/k10/7.5.401/charts/prometheus/charts/kube-state-metrics/templates/psp-clusterrolebinding.yaml create mode 100644 charts/kasten/k10/7.5.401/charts/prometheus/charts/kube-state-metrics/templates/rbac-configmap.yaml create mode 100644 charts/kasten/k10/7.5.401/charts/prometheus/charts/kube-state-metrics/templates/role.yaml create mode 100644 charts/kasten/k10/7.5.401/charts/prometheus/charts/kube-state-metrics/templates/rolebinding.yaml create mode 100644 charts/kasten/k10/7.5.401/charts/prometheus/charts/kube-state-metrics/templates/service.yaml create mode 100644 charts/kasten/k10/7.5.401/charts/prometheus/charts/kube-state-metrics/templates/serviceaccount.yaml create mode 100644 charts/kasten/k10/7.5.401/charts/prometheus/charts/kube-state-metrics/templates/servicemonitor.yaml create mode 100644 charts/kasten/k10/7.5.401/charts/prometheus/charts/kube-state-metrics/templates/stsdiscovery-role.yaml create mode 100644 charts/kasten/k10/7.5.401/charts/prometheus/charts/kube-state-metrics/templates/stsdiscovery-rolebinding.yaml create mode 100644 charts/kasten/k10/7.5.401/charts/prometheus/charts/kube-state-metrics/templates/verticalpodautoscaler.yaml create mode 100644 charts/kasten/k10/7.5.401/charts/prometheus/charts/kube-state-metrics/values.yaml create mode 100644 charts/kasten/k10/7.5.401/charts/prometheus/charts/prometheus-node-exporter/.helmignore create mode 100644 charts/kasten/k10/7.5.401/charts/prometheus/charts/prometheus-node-exporter/Chart.yaml create mode 100644 charts/kasten/k10/7.5.401/charts/prometheus/charts/prometheus-node-exporter/README.md create mode 100644 charts/kasten/k10/7.5.401/charts/prometheus/charts/prometheus-node-exporter/templates/NOTES.txt create mode 100644 charts/kasten/k10/7.5.401/charts/prometheus/charts/prometheus-node-exporter/templates/_helpers.tpl create mode 100644 charts/kasten/k10/7.5.401/charts/prometheus/charts/prometheus-node-exporter/templates/clusterrole.yaml create mode 100644 charts/kasten/k10/7.5.401/charts/prometheus/charts/prometheus-node-exporter/templates/clusterrolebinding.yaml create mode 100644 charts/kasten/k10/7.5.401/charts/prometheus/charts/prometheus-node-exporter/templates/daemonset.yaml create mode 100644 charts/kasten/k10/7.5.401/charts/prometheus/charts/prometheus-node-exporter/templates/endpoints.yaml create mode 100644 charts/kasten/k10/7.5.401/charts/prometheus/charts/prometheus-node-exporter/templates/extra-manifests.yaml create mode 100644 charts/kasten/k10/7.5.401/charts/prometheus/charts/prometheus-node-exporter/templates/networkpolicy.yaml create mode 100644 charts/kasten/k10/7.5.401/charts/prometheus/charts/prometheus-node-exporter/templates/podmonitor.yaml create mode 100644 charts/kasten/k10/7.5.401/charts/prometheus/charts/prometheus-node-exporter/templates/psp-clusterrole.yaml create mode 100644 charts/kasten/k10/7.5.401/charts/prometheus/charts/prometheus-node-exporter/templates/psp-clusterrolebinding.yaml create mode 100644 charts/kasten/k10/7.5.401/charts/prometheus/charts/prometheus-node-exporter/templates/psp.yaml create mode 100644 charts/kasten/k10/7.5.401/charts/prometheus/charts/prometheus-node-exporter/templates/rbac-configmap.yaml create mode 100644 charts/kasten/k10/7.5.401/charts/prometheus/charts/prometheus-node-exporter/templates/service.yaml create mode 100644 charts/kasten/k10/7.5.401/charts/prometheus/charts/prometheus-node-exporter/templates/serviceaccount.yaml create mode 100644 charts/kasten/k10/7.5.401/charts/prometheus/charts/prometheus-node-exporter/templates/servicemonitor.yaml create mode 100644 charts/kasten/k10/7.5.401/charts/prometheus/charts/prometheus-node-exporter/templates/verticalpodautoscaler.yaml create mode 100644 charts/kasten/k10/7.5.401/charts/prometheus/charts/prometheus-node-exporter/values.yaml create mode 100644 charts/kasten/k10/7.5.401/charts/prometheus/charts/prometheus-pushgateway/.helmignore create mode 100644 charts/kasten/k10/7.5.401/charts/prometheus/charts/prometheus-pushgateway/Chart.yaml create mode 100644 charts/kasten/k10/7.5.401/charts/prometheus/charts/prometheus-pushgateway/README.md create mode 100644 charts/kasten/k10/7.5.401/charts/prometheus/charts/prometheus-pushgateway/templates/NOTES.txt create mode 100644 charts/kasten/k10/7.5.401/charts/prometheus/charts/prometheus-pushgateway/templates/_helpers.tpl create mode 100644 charts/kasten/k10/7.5.401/charts/prometheus/charts/prometheus-pushgateway/templates/deployment.yaml create mode 100644 charts/kasten/k10/7.5.401/charts/prometheus/charts/prometheus-pushgateway/templates/extra-manifests.yaml create mode 100644 charts/kasten/k10/7.5.401/charts/prometheus/charts/prometheus-pushgateway/templates/ingress.yaml create mode 100644 charts/kasten/k10/7.5.401/charts/prometheus/charts/prometheus-pushgateway/templates/networkpolicy.yaml create mode 100644 charts/kasten/k10/7.5.401/charts/prometheus/charts/prometheus-pushgateway/templates/pdb.yaml create mode 100644 charts/kasten/k10/7.5.401/charts/prometheus/charts/prometheus-pushgateway/templates/pushgateway-pvc.yaml create mode 100644 charts/kasten/k10/7.5.401/charts/prometheus/charts/prometheus-pushgateway/templates/secret.yaml create mode 100644 charts/kasten/k10/7.5.401/charts/prometheus/charts/prometheus-pushgateway/templates/service.yaml create mode 100644 charts/kasten/k10/7.5.401/charts/prometheus/charts/prometheus-pushgateway/templates/serviceaccount.yaml create mode 100644 charts/kasten/k10/7.5.401/charts/prometheus/charts/prometheus-pushgateway/templates/servicemonitor.yaml create mode 100644 charts/kasten/k10/7.5.401/charts/prometheus/charts/prometheus-pushgateway/templates/statefulset.yaml create mode 100644 charts/kasten/k10/7.5.401/charts/prometheus/charts/prometheus-pushgateway/values.yaml create mode 100644 charts/kasten/k10/7.5.401/charts/prometheus/templates/NOTES.txt create mode 100644 charts/kasten/k10/7.5.401/charts/prometheus/templates/_helpers.tpl create mode 100644 charts/kasten/k10/7.5.401/charts/prometheus/templates/clusterrole.yaml create mode 100644 charts/kasten/k10/7.5.401/charts/prometheus/templates/clusterrolebinding.yaml create mode 100644 charts/kasten/k10/7.5.401/charts/prometheus/templates/cm.yaml create mode 100644 charts/kasten/k10/7.5.401/charts/prometheus/templates/deploy.yaml create mode 100644 charts/kasten/k10/7.5.401/charts/prometheus/templates/extra-manifests.yaml create mode 100644 charts/kasten/k10/7.5.401/charts/prometheus/templates/headless-svc.yaml create mode 100644 charts/kasten/k10/7.5.401/charts/prometheus/templates/ingress.yaml create mode 100644 charts/kasten/k10/7.5.401/charts/prometheus/templates/network-policy.yaml create mode 100644 charts/kasten/k10/7.5.401/charts/prometheus/templates/pdb.yaml create mode 100644 charts/kasten/k10/7.5.401/charts/prometheus/templates/psp.yaml create mode 100644 charts/kasten/k10/7.5.401/charts/prometheus/templates/pvc.yaml create mode 100644 charts/kasten/k10/7.5.401/charts/prometheus/templates/rolebinding.yaml create mode 100644 charts/kasten/k10/7.5.401/charts/prometheus/templates/service.yaml create mode 100644 charts/kasten/k10/7.5.401/charts/prometheus/templates/serviceaccount.yaml create mode 100644 charts/kasten/k10/7.5.401/charts/prometheus/templates/sts.yaml create mode 100644 charts/kasten/k10/7.5.401/charts/prometheus/templates/vpa.yaml create mode 100644 charts/kasten/k10/7.5.401/charts/prometheus/values.schema.json create mode 100644 charts/kasten/k10/7.5.401/charts/prometheus/values.yaml create mode 100644 charts/kasten/k10/7.5.401/config.json create mode 100644 charts/kasten/k10/7.5.401/eula.txt create mode 100644 charts/kasten/k10/7.5.401/files/favicon.png create mode 100644 charts/kasten/k10/7.5.401/files/kasten-logo.svg create mode 100644 charts/kasten/k10/7.5.401/files/styles.css create mode 100644 charts/kasten/k10/7.5.401/grafana/dashboards/default/default.json create mode 100644 charts/kasten/k10/7.5.401/license create mode 100644 charts/kasten/k10/7.5.401/questions.yaml create mode 100644 charts/kasten/k10/7.5.401/templates/NOTES.txt create mode 100644 charts/kasten/k10/7.5.401/templates/_definitions.tpl create mode 100644 charts/kasten/k10/7.5.401/templates/_helpers.tpl create mode 100644 charts/kasten/k10/7.5.401/templates/_k10_container.tpl create mode 100644 charts/kasten/k10/7.5.401/templates/_k10_image_tag.tpl create mode 100644 charts/kasten/k10/7.5.401/templates/_k10_metering.tpl create mode 100644 charts/kasten/k10/7.5.401/templates/_k10_serviceimage.tpl create mode 100644 charts/kasten/k10/7.5.401/templates/_k10_template.tpl create mode 100644 charts/kasten/k10/7.5.401/templates/_prometheus.tpl create mode 100644 charts/kasten/k10/7.5.401/templates/aggregatedaudit-policy.yaml create mode 100644 charts/kasten/k10/7.5.401/templates/apiservice.yaml create mode 100644 charts/kasten/k10/7.5.401/templates/daemonsets.yaml create mode 100644 charts/kasten/k10/7.5.401/templates/deployments.yaml create mode 100644 charts/kasten/k10/7.5.401/templates/fluentbit-configmap.yaml create mode 100644 charts/kasten/k10/7.5.401/templates/frontend-nginx-configmap.yaml create mode 100644 charts/kasten/k10/7.5.401/templates/gateway-ext.yaml create mode 100644 charts/kasten/k10/7.5.401/templates/gateway.yaml create mode 100644 charts/kasten/k10/7.5.401/templates/ingress.yaml create mode 100644 charts/kasten/k10/7.5.401/templates/k10-config.yaml create mode 100644 charts/kasten/k10/7.5.401/templates/k10-eula.yaml create mode 100644 charts/kasten/k10/7.5.401/templates/k10-scc.yaml create mode 100644 charts/kasten/k10/7.5.401/templates/kopia-tls-certs.yaml create mode 100644 charts/kasten/k10/7.5.401/templates/license.yaml create mode 100644 charts/kasten/k10/7.5.401/templates/mc.yaml create mode 100644 charts/kasten/k10/7.5.401/templates/mutatingwebhook.yaml create mode 100644 charts/kasten/k10/7.5.401/templates/networkpolicy.yaml create mode 100644 charts/kasten/k10/7.5.401/templates/ocp-ca-cert-extract-hook.yaml create mode 100644 charts/kasten/k10/7.5.401/templates/ocp-consoleplugin.yaml create mode 100644 charts/kasten/k10/7.5.401/templates/ocp-plugin-configmap.yaml create mode 100644 charts/kasten/k10/7.5.401/templates/ocp-plugin-deployment.yaml create mode 100644 charts/kasten/k10/7.5.401/templates/ocp-plugin-proxy-configmap.yaml create mode 100644 charts/kasten/k10/7.5.401/templates/ocp-plugin-proxy-deployment.yaml create mode 100644 charts/kasten/k10/7.5.401/templates/ocp-plugin-proxy-service.yaml create mode 100644 charts/kasten/k10/7.5.401/templates/ocp-plugin-service.yaml create mode 100644 charts/kasten/k10/7.5.401/templates/prometheus-configmap.yaml create mode 100644 charts/kasten/k10/7.5.401/templates/prometheus-scc.yaml create mode 100644 charts/kasten/k10/7.5.401/templates/prometheus-service.yaml create mode 100644 charts/kasten/k10/7.5.401/templates/rbac.yaml create mode 100644 charts/kasten/k10/7.5.401/templates/route.yaml create mode 100644 charts/kasten/k10/7.5.401/templates/secrets.yaml create mode 100644 charts/kasten/k10/7.5.401/templates/secure_deployment.tpl create mode 100644 charts/kasten/k10/7.5.401/templates/serviceaccount.yaml create mode 100644 charts/kasten/k10/7.5.401/templates/v0services.yaml create mode 100644 charts/kasten/k10/7.5.401/templates/vap.yaml create mode 100644 charts/kasten/k10/7.5.401/templates/workloadIdentityFederation.tpl create mode 100644 charts/kasten/k10/7.5.401/templates/{values}/prometheus/charts/{charts}/values/prometheus_values.tpl create mode 100644 charts/kasten/k10/7.5.401/triallicense create mode 100644 charts/kasten/k10/7.5.401/values.schema.json create mode 100644 charts/kasten/k10/7.5.401/values.yaml diff --git a/assets/codefresh/cf-runtime-7.5.5.tgz b/assets/codefresh/cf-runtime-7.5.5.tgz new file mode 100644 index 0000000000000000000000000000000000000000..66ee89a4d47210c5a1b70e3b05fa024056e2ce7e GIT binary patch literal 48972 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 literal 0 HcmV?d00001 diff --git a/assets/instana/instana-agent-2.0.13.tgz b/assets/instana/instana-agent-2.0.13.tgz new file mode 100644 index 0000000000000000000000000000000000000000..72f6262dcd1066997ed8c8b03c51d209372e1500 GIT binary patch literal 134699 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�ToflTF4X$`rU(lU0(>eb%V&Q`OS4FW3QIrVRBIxDEECX;B5LP?@}ZUnv>dCLPYCdyy5=IMkk<-ia&Lu661UNrqs$ zb#d(NJc5DB0~4n)XPes~eG_Z7A0|+;wuFoeH8?JpSt$#o7%byMzFK%>)xk(V zZ(UKlShs!r?2LUt|A3zbmqE4)XM{6NJwvtHG=dn}%orNEQeru;0#^B7;eLfWZX+|| zr4qY#xPRq{8920jxzcUoD(bi`H}Kp%DayXU&5tlnDoab433ISee0Fd$$>C;NoI(f? zu}19}{REH8Mt9+{7lx*r-Um}K;|Cv|EKY<{wAj(yX zdgaXR(IIGde!Q-|9)Xg{SGwBBVi_3Qu%&Ig_3MPu;9f%An&UMNrJUacOIvG80*LmM)bhIBuZgd(#O1L1K$ zAb^NQUpvejDG(1;g&-1@)Z`?pj2@Yyf20R#ie# zTMxnqR$UzDWwWsQLR?3#cO8V3{y%q;u<3)a7`oM{5i`MS#HE?(cY?`($S)f8R#-O}}__32;UI#PCDdgDHR*V`|^>IU!HAYv-_KYZlo3;hk zQ?>rl9cm$?qebU@Il<~lYn+h}8{vIRj`Ky~TkPUWXR4(t|;Yo$e( zXaTIIY%#D}8P#=2KxIbJI|XwLv~u`dHi*(;gkiUrWOSJhJ?Lmi7{S4beOXg>z;&y0 z4t3RKf{Djxj%sqoPP~`wREqhhZ~A9f?I7j#J4v6guOm2AOc)F?@`$MRjb32Z5Pf`s zbgdb&%qrcQN=$$|f= zkcn{F=IuP_g8}6y}xO8;{`&=_q zXvmE2-B;zrHyBFR&(WStWA#Cv)SvHsB!!PnJ4Z4^l!L3-X9v8CJ)E;6QK@B#Qnjrt-u9YgloLH2qv-T}r)n zZ0;o{%WdF9T|-Tcrm2NWG>sa)#~9U4Su7ku&+ktP}fnn(>SSc ze1Lth0U=3mZnO1A^xg+^i!Tg6>(05kbgKD)u{^Dei?be%&!&5gDz%%e7&n9XJOf_VNti@Fy5-Akqf8&*{Z>_yj}a%XE_8OYO0j0)n*lrhcxlAu-~3Svi2%bFhlAdK1Eksp}y+YbUNR+WS7xe^}w0B`!^+~ z6l}(|vyPQrvT-O6Iih^A#xwz3?p&(Wtx^V}U1m^Ec5T1V+e5*CiE&}M8(KM3Y}{o0 zNpfN8-Dv@c9q%*z#T#VpdTuQAno~L1CIl~MItaJ8TXO8lfYCIJ&#+#SsK^+qQ5%k5 zi@_g}EsUFz+rEre78~pjrDL>@_mG`@+BGf70F(1cU5W~|oeMpZ^}lqzz<*cm=iQNU zCJL`w(A}PW(l3anrw$X!OpM5sEpR?e&f;hv35n`R_pAb1w>){g+Avkdcp;sm6HTm@ zB@}GwL(VEfsY%}3{N+q%*b9609f0o$vPYTtEc7J6X`pHQsYgQ1iL4NoqYXS8&eKGE zh)R#!`m!tN-+ZW!?;wy9ysq#Z+~k&{^&cIyIt(6xASxwIDtlg)XIYkuHoqONE zXhM9}Iy$Q8-eG^o^t?E0Zx)V){$u>xm}JRkxdJDlIh(IER&Di=84mpM4-cxD4P(6D zN6^b{B1udry4Z?N1<_tawa&Qj5g-=5=LmkO40}lh`emN5VCZa-IAAxGniM;&!uBvU zdkERwfMQEYLNkYaCA+DB{=5nrvCq#AyUml2z~lF3h?*6x|4&>opaeblqtwX@u*!MT-g=!;KXzod+GFd zhDuXfvl6Uqteal&j|mxD-du$nL>TqaXUDxK+}n~S8*PUXr)gEEXCLz& z(giBz%;Yo|F*&RbGz{30d`Tce0UoT=9ksFnY{m-|q)g z)5xfo1r;H;h|OH79}vFl^$z~W*8e0Z+`ZSi`{Ti3>9g64>tHO@gl0`Hw?Ef0(m!st zmxt)HLSV2^$?);GY1T<&nvb5Zs>6`V7%_fIXhsQ@AGOG{*!zt4&K%0&ubU)x3l9SN z z0PI?gMkjcU8T+nuI7)@@I( zPKu-E3{KLXyj?O!Lv1&ot+FBTg+ABsR6

(0<<-*4~m@$%(NSC&FhSzNo|rqF5!@ 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 literal 0 HcmV?d00001 diff --git a/assets/jfrog/artifactory-ha-107.104.10.tgz b/assets/jfrog/artifactory-ha-107.104.10.tgz new file mode 100644 index 0000000000000000000000000000000000000000..912d01f24e0720de724ec4d4a2a24d8db926db6c GIT binary patch literal 224816 zcmV)SK(fCdiwG0|00000|0w_~VMtOiV@ORlOnEsqVl!4SWK%V1T2nbTPgYhoO;>Dc zVQyr3R8em|NM&qo0PMZ{ciT3WI6gn?ufWLJwcVPOlX{xO{#tH3J+pHX zh=e566u|#0;c-2wDhDiNrCM*q?mwX}H}&hz$kd z7kEe$DrfF{FWl*cyxi?lnqmtfhmc-3+)+yx6A^Km z>CS#ksj7ezbn3vNH#o&(!jZ_cjB!cRF~UiLrubLJQHG;)JXU2R#-~_{trkMVoF*}n zj3ol$G)4zUNMVR@5w<2w4c(6L^77IQFJjYl-DNA z(sApY%q|&^0eRl%R)7F{D?l;1V3}yCUGY=>rkG-yN=#G20SuX9n)DG(g~UlhxcFZ; zP`3g=8_}DhA;MDc;a2T75-jJDUSf-ZF_n{i2-64nX3<)5mZ|nxme2?eW6N7Qw{bqo z`LCn@35(9}v5zJ6zq_;3o1_2Ry$AaLDSlOAbf3hAj){i_sP!Q z&Jg3#H{1Aev^Uz`9d7S_6YmXob{K{-Ea1A_nT3?x6>Mv zlyEHdHPGGO4!S!*_vu;pX@76Gzt;<&cK3SQkH7Bj^>@1Mk8)W41AmqCe}R*nhWp()47L!2n~bq&!e9+Oitra~%u zW%xW1#4I`|+!3sz?n#~`0}^o}g@%AofTlAG;s-L*U*F;>QQy}z9}w<4)(%BDMMHwv z1>rP~Ns4lzrmlw-8oDS#52=W8Hy5usgb{@jk9ALPnIwI5%wxh48=;Jo5#c0_h+2r= zX0->jy}1}F+S!~Nz3vHC7x)h%5KaVxZJ?I$N~1}j4GS7d3!s|??H7IY;Tn+Wb#K-s zrSgCkESD!YL5TP9C95hRtsVY;`H?&7zc2x0zb zWqBX9d)=unNa&Q-wCKI6HfXopBTvw*y)RARJq%GDVOZS@oJtg9iCb&siT2U@hwF8Ea?SF zW?TL==XjC}ifeBV!>o`ifRrF}5>Z0W75kMZQfw7p-RW0kmB=VWCz`4OYf{&Nx+vh* z8Sat3xx&-T@Qus3oZ3-uZ+f#muHsa0GsW&0+@fYQ&VwKP1 zl9|hII71>M5gpAm3jNMos8Z0koECwT_XC+(UU;a_IgoTVf`6M1R7Iq?+=vtP@(o+a zHU>g<$`XQ}W5snCrdgqgv&3xWVk4jDL&8%ci2yk)i$n*gE+>rBe*F1Ykfd?OXe!I4=9bsL3&MvLP-9rF;_pi=qe-bGp+dq9tvgl7 zR-Dx8ST>^#ux=;mH3z$|Mp*-%A>rI!lu0 zWD8*phzjHcDuxCq!1KI?5r4K2@^uA<(O*M{SBkNvGMS+%Obx)G{gWdk**Qt!#Op1s zafgdB!O_<_6&_Dm>nv8Ow8j(I*30yXC@f;~4rmkGT%D`-Ph1-awg2sfC7-98j zJ|$yvh2E&0rB-DNmI$L0PDb{yiQAi}=96N`%qN+63ugEaG|h!nv~`NpT=Q(2h$6#~t8FbZ z9O-idB*ncS5C}K}b4>*@5k@wkH;4HH2$*+Q$4qhjp=o8_Q2Vjg?qIct%wAJ8um$Xx z`2@tYn1U0wsGBk)oWf2>kWqwf>8-8Ltm2Jyv1Tw}gMiY;pbNUija=d%n8VUH7V7MhSV;2HH5Y{%cSH?lsCYQJa_#fg>>r9_`HK{Bie4Ep};?4%d= z+=%q+`1IreZKzM%o2vGZq+~>asP;m2>|nre;h}a;NQM)7L4ceXaH{5Zh<+d>)4&wB z1C0QvI)kZZJ_KTEGP4Ba70;qtBBF)gW1dBSCW@lwYY0TdrkdTYAqZ=8Z-Q3}=NV^` zMXcCuf)vDksF8=LR5M74Vo@-k0lp?U9g|Wm3(8(BaEkg1Ns4J2*rt6L)Zl@BeKz`) z5_!q^d1hYv|NK|W5fep|EC|J!5KT?g(B~AW@OaEv?8GuHJz&_^uQkR^5kff2=CrTs z8!`NXeu#d!hJQFNq9|SS-_A^plU)MED2a^}rvf{dO{aMZ5+^N~F0^H6gN|yH_?G$F z^yccR*ls-4O54he6mhi;f;zyBz?*QXF!TaTJX9||`*_x8tEK4~Y{*MYWiVnqh=(BW zEksWt8W2D&P+GVe&S}@G!-NexT72w26)-!ehx;$y9EQ_aG3P~j>B2DN|8CN=`MwMJS#K|V9Cm}43@J{7 zUN7Li$BLS}sy_-vJF17quW^EfxMVyw1vH9C{oRR>CZgNj?kj49x%S0;kYGAh%?y{SA*g$& zkM?$VwiSF%r&wzjY6Q5jlSd=93umrCvV?GZB>=VM1jnZYB=h2~knAf}*RfJ=^&Tfz zvK|HQd<<5p4Jwn8XmqV49PMpe>FAm#AGHF~wUUT*tyV}{wA+Jr`Z4+T_{@7Cp6FdN z`2Sw(dq{C6U>LJ8Pxqd|ao^lhfMzU5Z+;w{S&50|DPv1lVj?k3w5kv>UXl$0W2?p? zB<8YZ+gOnbLp)C_4W#0VW(9aAC%_!y)Sm*7`nN1SDQJq&lu998U99sp3VzWFk7G_R zv^ZH~OwAR*ObE((vV{_YF9_VdaPg^)jL0;T>e%HxDYv`?p%BR5JoFYUrY9dV8_2Fr zJk)?PbM&`Bb|oi7SWT+xPw1iAO6ATcYM@Te^@HPoOpn6vG9IcS#Uzy!CjvUtuqr*8 zrjA3hBT(B-)37#=Mc`4Rw)uKa)c%Zzfoh@)OT%6rfNiEfSe)Nr06c;7uv{nzTko9A z1WIT`QbFEnIfz<#il^^DE>@Ntnu_|?^F-tY7mWJoz&tRt*ajGF=uaQb_li}GWMVgo z`Dp9@P5%w{j^CjmP&`7AP)RsWo|VS=?9qp_;~x&+URP_`u|IqC;q>_R;k*6Qx7Y7m z_!20J0nwnRYnWfZrSDCa_E9bZf`trv=K43nV@nv3p^S6ae2v6SVsS1ymQ5i+#^S(H zTBm-5B61}=Yl6r?PDoJw0;~)!nySA4S3PK_jfC4Ve23=0h4O8^b}q}{@jTrZZ&`ZE zm;@fm)m#^I0FhsN73dkK7c?Pb5-*2c)LjKKP|XwDZJ0}m z2xHDN^qLBZ*a-Pww5$QC@7Zdz5hRM(G&|u8j9nDjUp^7jNWE%RE5%JNy6>yEy}y>O zuvmoRa5W*Us8JriKBM{xdYY$_PKmJq0!!%vPr9N)Qz5S3fBo7jNVTkA(HjnwU*=_IiLW2zTlac=KfHH8?_rb+>xH0p;-o{#G(@0u1ld z?$d41Ce;+mL>yMBE!-NCV?@#m%2^7}+6&C7swWf+Bun-x3hQ}F>@rHG7y%AKJYiG{ z#q(I*Vrr@2az|J|HJAudeI6EF{$zXX8}Wo*%QwHgJAC`oyPx(?%@qrf%p;}BMVA@g zGSk{M#sKrouFvY$F}iEMHGmUxpoES{G>a0&sIhbX-ZVHV?f_I&b6emgXVVh*=?t=- zgnQfJ)fW00Qwi|=T3VElB?QEVGsH5IqC86&j)h`?V|JP9KZaz))R`iDN>i*3ncl6O z3}kUS$rXtT#gfPVcsYg+N4TbPdNjns!5d|gWc!4PN4lQiZ_?4ZG^4* zlQ91c#l+PT`B2yKKUu7va5ijwQfr!PN7J+?%8Jyosxyd6f9g#lP;g#sl_|c0-Qing zW@=O-G@E8XB9_JiU0wuxCa7JpV-nGXOi3Gs==!>}zdX450ERwAMAz4bh&Svz zGXeEvr#N{*5cenGNRa6T;RkG*sk^HEw~Jo5-3_}zw~fLBpNaUP@E{4Tx##tD z?`ikL2SgG<(DgO^GmX*pwdpq_oU+*U*z20|GH&{-V9&Hvc+WXGn{Xl~EQ$MQ&wLR% zTsGd9o)K%%hrP0BuQ(Ka^v&|km%Ud&qLOD1JLOJ0#Vy=C`oHPcul3%YmU>RS>fJ1D zu(f5G?D!mSyJB$Hs@P~ZxI!8OEzLF#XU?GNl4u!7VFoW58WX8`bj4bC3>#!vENvkc z+}N}ZHtp9hNo*vpYr3Q;2o8nB!-R?nO~-OV4k(Wj!bWF27t-{tp3gPL%s01Q#?X1$ zc6r2-SnJdy==xZ9?He$cOu+IKTxE6Hkv6~7FN9Ho$L9PABxpKLh^=iUI%-o{uS~_i zlo!lB9X3E}xM^i?Tj)rlOO~#KMiGxjB$8#bKV2{ME$EkwlL|{J)V@%}!rvdjU}@2% z*$3dt_~fbLhkq2-XkJ`W8BI#6A;oOdG&jmMHAj}j=n~J=L*5ELIf0R=@iq?ASJRg& zop8__o$80Bk^aqhD*jG!=b)U5=f*hS{XJ=v z^l|rhM8=unD%D$e*&BCd;Tl-4De}XED9J&SR><3?c_L|+kU&x5I8O)%10(mBXQ1z7 zni;1Iq$gl?;8cD4RrM?5n(Cy5jGCopiQgWd9ijle!B=#ePqkcx#O9^SMhJu8S#407 zlDO!S3e>Z%s9h~&fe#02C>m!~i#+#gEj5HuI9pVE5K`?p1jYqs=GF?NG>X^W93!aWz@UQNQOc5L+z#|LM6z!j$9lhK?I6FT5MH4hdz)DrY%tb+Y@%+RU9;XA% z6S!UJ>svtOcbt*5r1pEN<}cYa!qQd(dwHBj#8hP5atN7`V0_cF+(r5O6knB zyKApc0Y?T4mYz&Fs4t7vFw%zu!>6n(DxJv#btOJ)?{>TG_5TQemVvgtf#WeHlG6yR z#I&Wt1*VCDHz#L!5ClR}-z(g_R~zKL ze`mZP8Hd5g=sn9w`bM{UkFWqo8g&q#giqCo)EGEXX9Dp2-gIEVo!*XJP&@N$)2R;R zp#Ig2?=?mFwb)YOEz5z6aM+a$9i@VfClaiSHGXMAh6ESlO5poUp%cc;_k~cx#s!yL zTLVV)nvIQ*m>QCW9HJ8@1XUx@nzcTP$uJ*pA)1cZ7P`bdrRjJ}(MZBM1BS%(tAO=U zTUE5~Fl%e*Fu_97ND$1UiPOLOeVXf79Zx$izfCcjoGbWcfQrZ0Mr}IKheag#(rBNP z7);8Hz0sBxH2sZc@31yC_^$u7`*gcq^yADP+C^bjwsfc*lmo>**zk&(0Q)>72x|;X8THAGfxT;4A#-B!pD|Q~NM9{I8&#CRM2s3nUYu9h=VpC5@l6AN) z2&_X=VDDPP?8Yq0ofTil$lQZG3)DQE3NsLHp5FW#jzI-smyDkiE<6k3i zc5$rU0}40lshBHjYoH>Qma38qqGf9Od-DjvB-T+?=a3Zx|E;+K;KJU%9B809PRh1Q z+H`bZgZp_x(ux(Waa7p??S_8*frp5`6YO(yf?r^XUr^`R5*8Nv>a7Vqu~BKes21MX z72)$gzoZFK>qXq{Gi9-6`f(I7uK4{#H?tMQ1cM>yl$obM%3@-<_anKktxu;@jUdUa z1%eyAvHp#>obQ_DSDw=p^BIiiA9nKAn4uV{n<^HCq6bV9{X4^ZrH1K9gt6`&Y+~(D z8fkN;9jUR zMr;B6n%ebJp;vfy@whG>QwGIVK za!Fv)*7Ot9CS?=Z-^xa-s%hmu&9~B0p_(@4 zLt_icF&WWw??X)Za0Y+3ECVx!2Y#^`oMx|B`O}*y`{s3hOH_UH>cKtLWToAD1`ipu z0dd1W1h#^Q9P`D=Aw4|R_A{=ctJ3LbMB5uWHdm>z{DPv8ia1J! zo`hG5uupBJMq*u+cmA$9-~=v0gaE7bFcCA9vti+q!uz zkd6kjtb_KP618j*E(~X=J%d5LopEAiGnKmT4c!nL1C=zdZL4}EGS3RbRxMgvq&XN9 zd88^Gr6cxy22bEN+DNF7C{&Lpzwh>@Rk=9WC!S!<6XKWKnYby15Y7ivYc#CY+O_6D z_TzFBAg3Llv+uFC|An2;ny09FdwBNlbpPkVI@s6sSQmqJFUh2-*h1{^ub_oS~L+Or|ZRi_pcs!DX-+aF>*y`|FQIC~} zUH=8`dP~S}yzgby@gJa;SpQ8?>mcXJcb#IzznlXh=C~?dC4k4ViOLaMagTbk3xvxy zHfW<1#AtIN05M^gLhTW-Mt8lcCn+fWHfW5xo9-~x#yP5-Co_dWZU2DC=;0uJ5eG?3 zYRsiR=fe|Zn9nDv&_Q=V~9r{%px~o)~ z!?`=+(q1zrR7rsLo!ZQ}tdmqy?m1VxB|33H^kQirMf(O%i@=0#7VxK*FTG(g@#0Ii z1O3JZ4@mtlEulYfPWRR@xfC58PCVssZ{{ieEhi=pB049t5FJ*9nkkEk_9F+sF7O;v z=c#D)*H$UkkqrrhSnAndIGfrw%Sb9jg1CB<2NQCIcD4-_IKe_73M!)sNv68>dJsSy z8eflS12;Z<^x@6^;Oy}9-4BPqTpJB-C8E`AHFql$BuT53bO(VdDZ5$Kgp4$V>dndV z>Dm6t#?V`u)I(G;oq6`DWwPwhQvV=jS|*jv)M`5K+7 z)~gr;3}Y7|3_i^&n&_T4T?f-_=_ak|?&4n@bhc~|t#?M6Gi)Sv7 z(%)YmICbnA`gLUf(qWSBZwe#l1OPo(W>V_4I-!P-{nq?*!FZI~z!i#2gXZ}U!#x0D z)RvDpaW-_tgToIiT)oR}kT~aMTKb?v)9J47ZvGaHeF z_xk$m_3A!un~&SgquXvA-gcaJFVv|EU|~LR)l# zfkc9~)M1+lHNN%~4EDShq1g$>Y@wx}9~Qjp@n}@y7%GjafEkh)l&OWvJcn?sSXd~v z=5Gr$p`Zy*7>*SbaHa)KML37W`-tLLT?=Cg>7EsaD*K~_PoY*jhF8k5o)6s&K_nz^?C&w{3rX|*i7V1oKR%HPE_TKZQ{H_Saq>p|N9QzK=SnYx{ zQnj@f-aJ&=RWKxtR{}y5UL1(EHRlCvZ=9+*57>ofNj|2jwV^mpi3!96*OLCYu5Bx! zj%srPr4=%dc{HIHB=F5~g!u-!a)Kx!IMt>GC_tBFsAgyWD&J7kSpZfCF66Vmht>@R zZBmxqTp{+L4*S^Zu*H6+zDZQQv#EV(mRuoyC&+tsfAoDzSL+1&sP969@;YeU-buOQ zK>IO0l?R8XKOLUF`^THt@BFJoJy8rMtn2dA?$cg-m9}s8|MBkZ`_sey7lR6jZ5=hK z9>T%F;b5?!@!o>AUhjwhI6K{ccX0gn<t z56+H$I;>RhRq^AuIC}AVzJ9s-n_u1y4i8Qb&+5F-UqZw*ySw!*@dKHil9A6>Ye3Uk z;Kfq$J_DCrd_5;KYit6l8c3UhNDAT7QOYh6CxXVr#Dv#@-94pGSQgB9G0k02Wg8gH z8KGWc6TmKx731YXjq5JfeQpBIz`%vRUN_x&=D3Cls8lqi$XFn{KXv3R?@xqi4EQKp zzn*Ck8z8|cbXFK27}!n;A6svmIfPXrB#kJzHO(Q^k8^|*PH;Rc2I7N*L2Z>sRm2Kk z-}3X*P;2d)N=29OIZ(Zb#1O_D!jjI7x4gb!@i{A&*5U}}3RI3=nn-vyi@*YOz1K#p zW^}rKrAkF0Q>xlDXP~Kd6RBP%RTr3l z_whUxaH+900A(kH`i7vVXh<~=sNjeypuaYS5?L5@#OvC9?|F@yzPpeWEPcgsL{7>+ zusdBNJ7)2Gc%&Qnqpck5NdqS6|53eS z;D3%@c)>E`n*thui~{NZy*;RrSOTYIqMoi4`mjNP?Izwwz3xu8D1%<>Xc9=1u{HhGBe@rRD@@t?Lyb{1`t&lWReekTCJ zeY-DC;yjIUs;&IYC{Ac47Z`DRd{;I<@7V>@l z9I7~0SpaX3&kp@`TXdWZz7zwb1k^%IOfT+0FL*%c!iw=tD2Jjd+ zCeT~+7BJ{`OVLxeWQ**0P++W&c=W`WZf z^B9@s14yraINVa7(SM`kjHCmJqw`Jk*)GZa?1Q#jmVwoE76;$9fB>MOcTjMaB{QfE zsR7j3D~F<@^g&Jq?0XQC7$5^D?2d_gJR2MJBHNw)n2sFs#*B?sP3OB@QyN@sH+~3= zl6d3PbADCNj#mc*6rfj79~~qtkL`YR!@!p!5Ie!ejx7*71>~JZ$ZB~8JDc^5j4H@= z@o)7)9VTozU*ekSjuuo{FwK2c8$D0husl5-?A2QZpWF`1{=Q!RL`R4lXsuPdEfVW! zT01A$VWVolRMZH08aggFEy&QzvL>Zu+6zH@t0+)^Yq6E(ZqyZGvKKj%xzan&u}CJj zABaI+z5?^HKD0xcPltpbj}HAT2ST%HuJ?h{CSw#$XySzuQCPd75wnAAJg;wV23-m# zq@WaW7lzI)dl4rP=xS|$lE1JVP4P@;n$Cr14`v+OBS1}Io1yL$*iHb;5-~%+jB$sh zY#VR)2hh?8vQ2U+tw&PjFDx|eSFR8&%#ER#JAp?!MTr^;!VzaeARE-Ebk#Yh`?-nP zBnS0Nt`)SgP9Za4muL#{Uq>czegiTj(@ao^Hf+lFXQ;R7cW-0yT|J;~fSyGv{InrhS!O^3hv8N%z%SZ;ElT{5*8+Ibt;0Voh##eO>Z z$z>16QYtPiwx-Bns%v*OP4zC(d2g|$NUE!1ucn%MCSa!rTK{lm+UVWGiX{RBzkNBd zkxa~9G{ez$g@t7?c5XPTi6C5OV#7Mv0lWera?Nn2x%VQmgkz<(F^+$xR-?t~NO;8i zjT#adzu*W$fE=BugXN9Nw@Y4xlyoFQnmv2;;qA-8yQ7n9n~{{dfXh0B1sLrs3g{C@ z8?(ZFEEBf=f76o9%W{0Yo7V4De{>a{jn5? zrU=BMciXFj6Ew`Db0S6MnTK@Hk&VJyA68MZ89d`u6eejbO=kA=kE3ibmt1w>4Q*H= zW5DJ|XR{`%oMhMrI?>sv5rmDBHdXhCj?o4uS%RZd`tD7ew#}tQaa}g$t(tqVqHzEy-kTC@G3^cO;?t#(7ZkJ`py^PS1(+TNP0nQ`_j ziR3#PtNMQ{{`{`CGUOD0cTQ%e!T+e=;+m>jpnlH}Umd+gC#Oe0?VlZ@9}a(k(*H~U z$NZi>`@a;54&T10saDfVlu(k&cVvobVn*}7zxs0sDV~O7g9U>GwQS9>ylK@=4Ciw4 zF6UI$vfIHX3=Z|0!oFY~rmAj3?&caeV6s#$fHg(Xp6mAextyp+l8KrGA+=DQdee7GESnxSQNlfQ{SVWMgAohTv1ErDzsr_12?rxlK{p^lo5~%a%4<4iZNB{ zX!1Frp*!qCv!Wa>(yAWcMysamd7s@WBmfsmiqTu@$CmPJHeC}6D&TR=zGK& zp0BvAbtUyW9>`fjbTsO66=3d9FqfYA)RbnJ%R`-qcVL|VRy+t!613GUHmdzp=EI=E z^MnoUu*?nPqt6tTFjL;F&cwoRR+)AY9Cr}{oNRi@F)d|+tjm09#asvaA#rqKQESW< zY&%8*xO5 zSb!h(^;Wp~XZz^nVz>RTE&Z>@?q9p@Eo4d*j?7nGw%h*Kr^FE@bI&6$^Y!I~N}}B& z(z!s@){|)?H+jBCR`ulYt9I(M*9^zcaiS9zTL=nzuE5Puy<#BrHfK}qz-zq;5}J~^E}@2X zG6=&E=?Aq=pIwBGs~^|1^$(?CZI#AkUfK&Lr3XEq;oCeYX*AJd1pDN&vW3D^$9!<% z?^S1LXnI2#V+qQ6WpoMh+Yw$M^v|i`D)9%oo#KpXBNRGyRT%of1s8R)bfE^ zK2XaCYH6sY#8T})VXZsn@g>BhF{EYLc)GW<^Np@~RZ~0A#@ zrQMxivD6V`QAA>UPDuJ=DzKzt1m0GC^n$%*a$wSem}&_g zlEfuockVTyw0f<+3Ew3LXIa15sXv(D(07_F+Ht1(zfD6O62PcYO@M$93_-51t&`uG z+KtYWqihOPL<{FcyR61^G$I^qGloQ75|YlX5?E!x#xKSBJ^*PhwRR?y7g3_1*{}El z7f!yQs*hQ!9Uj8D6#6ykp8`|Ep0Le3Y|gOFT@J+84BCVZ>g-fReu|Fp+*fNENk77%z;mH+z@?gmWAt7}5(=g> zoA#$3cR}~PeM9?WDcjh=Aw1B!B4dDprZjcx4t3z5IL~4%$v|=}$=JQlO)_7WBw(8r z>(~LtZCuSO zX_I>G?{Bx6-q)N(L=@rwN|e)^qEjV$5oJsah-ndC--0d<5ZoL?W@L$~%i~NInnrXr z6>0|KwpVhcrsZEZJW$v#o5HG>UR@4ypTWDtRK8^V zL>GBEM^LS+fIa=%u8B@f_8RAOt`>Wk(?r7akqB8Nu=!OTebt?bk|UwLJ$}?`6zZ-8 z#rE2y>6Qk%oI47dcP*F_f3JS4ZVqYeR#TP+aBpl`(Cy0Q(e`hPlu5XJ!av3RWb=*m zU20fc(wg55Ro~$!-8+Q%u>E}O_#SV5sfz_+&QXJd4IMImLT@yzK3d=D zt}hN%i`Adw|h8dw032*ynT#zIPWsyNBj7aDS1$zRfO| zw|C=gat!_AoH#$%+OOoBx$|k$u4|^OxDoZ=HTR*q`gGKYv)2POoS|rf)3NmjlZh~q zVm7q1i-{GeV?mg-g3eylBq%fC|6JITuqHmnioq%0;1sLDe0}zM@U@?ocFle{dp+>W zTZN^ri#MnyK}$qx%jEgg7BR-FERrr2W&sea;dHX&je~uijaJaHp0C1KDNipj2g8}m zcy0xz()4#?GG&%zqo7D5smQryHQ-&zVj$Y^Uv(t%)UuCOI10gf&R!1`9F6(|7h$zX zvEbf{zGa*j&ZjxrZaJ3jd}`ooY-;QB&@Yl@E7Tik+Hm^4V&Wp1*!V~0qpJ5eFId%g zC7ZLwadm(|r&1qQVonK(Z->xV4qtAr^yk8%6_!KS=C> zZiZPalPODAK$6nycDvJxxMdsM-{wU)3mde`GHGATdI#Z_ncU|SZIKHaTNRG3Y)e`- z_g7W0x7Vu)AGo)D2ZY$(zc6Xv++Jyr_st3XDtoe4X?u{g-BQ|O z_t1l+?awD^TXF5bL`j>?TVLE#wqynE)6>UyxQ}cvVFNPGjMad|Am;ikx~~<5ISUk1Zsi`X z%ZKaomL${ep@-}8&v#v}xb|P-bxAIW_RX~~I(TuC#|M?bfdVlFEns?9X?f zt+Mc6;$`Nt&lVS&D_Y@F+qsMFq3h`367nyh0xf@*i|CJG$RQKqS#qeqZe+soaNFKH z(RC-~;l};*-MFhR{-?fm_gX9^Qx=n?xKeFNo%JHx&lFWar!1F*Bb&a>-joGl0E4-a z33EBkSPCj}Hi8cT9&MChZ31{dYO@Jo3kH(JP<<{Dkx}Rr^d%4lgqRVLu1ja!ZeXZZ zU^<{<<+c#Q(B7plw4@mRE%J=;F~`}YxIz+ebL%6QZM*-p`{bKCKZo8Ocn!M(53GjI zYB;R?SvZX^_7Yx0&z*_QR?!m}bOPnn)+CR+icJCX$VTu%&BKv6v;i3KGAC{the6~9 z{FeeYE%@wtj&gRku5`O4RRsQPz_E zUX$fq^L23l37{HKxA+$O?5Jz9f%~Qzb-nF{xVv4BwLd2I?swl=C&Pl_@Iurqd%|yb zS62vr1RE_o(l6{{{t`@*9BLkJ=P&+=+;sohHN5dfXd5=rBZNdWA!=iPe|C1V(jE8t zpP%1j}XyiynIr6j@qFlA9OAlI8m5Smvj*aX-nL*cE;8Q7_X2uK= z95U7H)PU+IU&4=xV~j!BZk;_ZiB`&InY~LSLt`Q}_o$e;&V~L^<|uE?Gy<-Ig{QdU01D*|9rx!4u}D%oOJ+1FKsDJJuvF>HMzNB9mW@$7D=F6en@E;{%wOv|EYpP z+t|+QHL;!jicZyOs+P`FYU?lN?I9Y8TbY&%n{SegZn+5NIF1oU?+v&79zf_bp)M6NTk;6qep+p^ zr{1k@fWk%v)dMnjsE{$?+`ngqj9FgFjFpfJlJwCf=Be5GE+KTB)Q(c6EhV-TX3OdAE5yqOm^t+*Py**_{jDp5gu3MqUd-aPHDv8d*ahL2oJt#X%jV}O6JNI{42xvB4A`dsMC(v%eG9Dz zWA(o=CQ}GCI{J1U0o5p_B{MX9YQjBinJ~CIKZ(svu?275OGp5q7#TmYNvmK(=Zb|$ z*l3h6T;iJNv(XgwUNRm*9zmfalNtuy2twwnd*Qs!cwWjJ9TAAW<-#`reb?vKg!Roc zAvrO_fRNKBl-BoWXD5R#U_ErlgTZTLdQ@uzVY*;kuER4EpVelBsYcw;xf9Y#su74M z%?TV=2-9Vf1Vb1SU`@jc9t->|#^^&1q=y_x_nreuGZX$Px45(r9+DfaoZJW=Q1Bpv zL_Rb^G*g^2w}LJo!qG{;*lLQDfN1%WA?&h?Gn*j2ZJwoVv|(-r9j&nanngHi8&f0` z59Lf#B70U$Q{8_#l>VA72+q|DN&ge{Aa-2$meyzqBc@Im@M5vrmikJxD|Rbp%x3d) ztXLg0rrEK~=fcdx5%V#TC-WaN&LoRi(nn_pC$6;0wlzGhPl>8%AHpT^=+o}j?(UAl z#MAE6?$%TDhh=EXft+2MF}3 zTEbCP8dR#PeXc8ZKC{NBNq^I%@42ZRG?0lApXzjYNGs^x;0zcN zjSRK6p1ipn`Y_silA2WT!k(v;fZd(lBBo-6<*d;NXK~D*q zah4ID%)*xKvDVOGKHrbsVvGEzVtvr7N*?AqMVVg-!U^PA)iTZ%BH9h6FEPVS)bkj1_f##X_(`Vi_9^7cV!6z6`AH`HaV;O4+O&!p z|1wQ1>y@zan5N^f1 zJUHwFwQw9VmJ{J|pTVtg@KsohVnj(2dmybQ1GRr0ahEI%mgAkVjPd%>-}Mf)FdS_u zhG{LB+I|8UizSaTq3dgq);3!a+nu?BA+chG&Rz~Yi-{JOBQI}@`l~kD@jY0Jl=c*f ziPMxVv1yig+(*4|H|#xaAq`7Y>~^j3mld3x65#}9+k4>aOs!RxzrU?yyJO~l6Lz(j zhFWXw)kYkaVe)~-f_mrlmCJ(yJ>p8_&MbVB~AU-DY z;N-_2B~9qR_52BsNGjE?K~FRdm=eipB#L+Bnxg<#v+q6>l(+Ad6%kJr{VaXZV<=a) z3PW#LmGAh1D2C6g&__@9z>OZ@nVwKC6r)WAdb0OlR%nJ3^~i)bfv%K+qCIZ;N}#$8 zVct`oo!XEf9LGhHQO!4@DbBWR)=0HG1{sHdDt_~FlzROTds)+|?h_5PO39ebfkh4* zA#BCpk$dS$1}a#^&k-EkIa|(~r6ly!?goqws$T(QUq zBj`SM@hZr^w{5)r<=}f>=5(L5o`~ zj3DtRbZVWyGMBrARG5YvdUnnGT$LQY%*iZ!e3`;grR1J`zE<(wTI7{gcFNMkxXIPa zlfLf1etj$T!l$xeE?B`v$yc-E{>h8Cz%7Ey&b-&Y#}uxuE%DD^18mhv*7Paxz4RBz z=>GYy*4i4{H+Ex8JFgT0yb7Mvb9`7^Lq7_Ff*{a@TM&g1$v7Gkc}Yl$rgMTa2wa~8h=^Ai{Y{NjaU2Jx z=Rh)$gAo}8&}pD6#TLV*dJWUF7E~EMe zW7Iqwa~uPnK`>+74{?&9DUCR@2vlpNZg`q{Nu@pS959sAeu^N)-0<8GskEt%H(QNC z1q=ZMXFPrDtLucNHY5QYeIf}LEnT7xq~RBd^yBAYutMqn?0 z1K^Lqyq!z*W`A&Yc>3;#!(W~~+5od|ksz+NV=y6CXlHx#<|BWE;05sLLjnBy7D%WE z{?UF4_z4!qtl!ytxveOfzC+=ayRKb1>UURZ;_IJg2+3EPn7rg{s^z@aY7+IjPr|3W;nfy; zKR>SbXo3YA5|ScrT*@Rv@8?Bx@3#Qc2xVY7WX~j=MNgJsjjPRT6>VwfaaDAbju^5+ z0m|Np4`Kc!;ES7rHKy>Y^ZIH`XX*g9H;@(Z3O$J&5| zgq8u&)i)cvp3jeCP35a2Xs)?gj09hbFrg7i1<^(tb;E74)6+dXrzz$$AsHwCm?n1o zuC0zPvM8<3jqp-8CNqIhEU1$U!cUTXOj8X>Y@&^(piwxU($qj(P$9+=|3GG&3n2N} z8Z!ot@?gatj-$*-UiPDi<*A%o#@oU%V%`G1&6~*(^yXbWS7EaOyD1$}66<z^73Bv-_{m1{%0- zBPUB=7;p_nI?9uz2)(vh9qG|YWt=)T-EBroC7HrNg~T!!p$P>+pv?Sn-PR*ei)Ny$ zJ~HFzKXRz+xWxe9)zXUi#$+q3%G=|!!#+BaXo_b@;&Xy98j(vhrD?9{pEXV>#RSRn zVL~IdIgACZjZyJe7?@2{c!_1C$(o+AHB_}Wn8TSmhY%fdy>x6YA{1QSc(I3kuv9ZmIPXvo>6&?cNlv9Fkcn52^8r0SXnYW*B67}O6~#}=4eX`0-3 z|MKbQayA!6xf7PX0CkNGv8gy0rvw50v={xP;8LhSQz9n}f@pD)kPDoe_>0;O%Mahl z(q#5mHAY}jtiPw}(+YBi1Y#4S3yMoa`|${ZK^+dx(EiDhGcdB&MG&|Re1)0G0XmxsNdZEsFS(Ak1 zgtL4+nY$E!JSr&XA&w@ckLrFXG{0C4h{m?N5Df@%>h#OYOXIi`u_+jjC%JkAbYd#9 z1kXCBXD>V7?|-1{j zGeym;zV`KX>+f&=QTC4LTdpl-5^XfyPYc8PDl!IYu*!DQuTG@81RE#VA#9pKUPxf2 ze~nlwn2lI!Hm@KOwU_nxtUSKI{`Ie|A}&M|mt-%rqm%w3snSA>Ute1m^yKvD&Hm{x z?>_DwW(RY}8>}K+Ynb`Mtz)%ehi{%Ao_^$6-@~0zi$W|7)Y~zVn9JzK_+d9q!y(FCA@p;TDcmQTf+6 zPoshk+zH_p$PKaCZ}Nq$>n=04)AxYzm4;hJXVXyU`)L?wRi-{BXrmFJ?>CBmM>Lh2 zsCOe#(#Me`wZ1|JP8>8P(;?B3$Q8qdIq@V!#;-0W87JBgk)|lmA~w}oMzwG5jpZ=v z83vr1F*Db)d@G5EtIlkmKo1YN7h43uMDHXAQvDl~PT=yuuuL(CE?h0>B{tA^&wAY_ z;odjl)n*~OO39_=zR-Rm*p}5aIE6u>CtxFFAsC?0{~N1jDEw^;{%AsytiyFdKs}Zy z5Q-fNVix6!lhy9G+5t~1<%0|l|bAR9zg4o%1qU?YD@Gxk~~i%^mqsUNvDH+G@@6i9TdRS@eMR7 z?|jwLfuG=_))E59H?nMaWu`KiX9`QBvfndKQ#nHI{}O=#(7wYo5>Y^Y1Ssx5t0W?^ zaJIjx8-qU#4Zz3mO;A~nR*zF#4XeO}-WsL4yU&eLJBs^8X!SUiqc1Nf)M=)wq=z=va(664U5dssI=z5LWb<#ywq@Hedfw+&nUyN#ib z+F!R(`(67MSLQ2Yc@1%oBhPSe>9EHn4O9bBfPRD%*ajXJ_=ykn9c?3ckrxQ%um?|C%|m!|H3j#CmJ5RikdFbK4XQkr-uQh-(0 z3b;%}7@4pd&`Ah@e@DZ|yCjW}sRG9FuF4N&R;n?-xC02hfi@;mW*rcuZGv484UMGw9W=+KvcIg7}tJU_e@Kwm_EjRqlaoy0o7Uj+7U{(AqYxL33ROj??!!x0lRPm#6=FqfSDP(D^;{R%*R5)q){H~mwpE%J0a-qL zD9^TGU2Z#u+jYJa>vaKio|7?M^(Jg=zi1trJ8jtb%NzJJZ8!z1_j}w3t=?A z%p0Gc?%BD~Hjo2tiyP=nHSJCRq23^Fn2_jPi}G?8z80~OO?Ct3Zzids_A1gTUz+@! zJO#X*5bg6)*aSzaH=2$Cpx?t*i)ty6DFnA92tDMlmj(#|qa?>gJ|1Hb0#>{rG6Csg z${?H1Sm(zxQMYNjVDOQ8x-S=_km%eZi~Q&>sUBt1Beiyquf9beUEl=upP>zzCCz)H zavsA?^?$!3$^I@_K~kN}u@TOatr~9X@hHdx38BiDKb9fJ-A!K0^8w>-L6vmFRY{ZS zW(E3Gsd+;p)zHt1)mKH`Ty(x0;cu?>awN`oxDb=g)mKL6@*Z!}W)Wg#&QlvvBO|<= zdmaR_z(Z<+wt`qw9;xa_>4-g_!B9N`XUstUN7I;Gp%8T~!}$nGP6|;R$$tt?F0_>-$u>G?>bA-f1u87N0Eae7-mg6no9jhgH_lnvQ z6Om`xQREx)2`$6Ky* zt8rJlxxevqIcgZS`cmHNtKe6GTYYqkK>2)A1!4NZv~ThL%$36 zj&GM2FM{cKQ~CChhcbU%ADT7CdiGG#GI?&s`Hd3}eKs`Z?q@wk~c;hd*;Cg{_= zDQ4NiT{SOFY)%tYF`HayUMZGnjQm+NUfz0O=JSG~o4}&Fo))MhB-t-`nk=tbYZ~GY z4}uwNJncU1cAoC;?s!JB1=_9pv}q0*^XdC)xUZ>FN?BOD5c7>hL#vNvpHaf}3MLxO z)f*=DiR~pyy)H7DsHwMb1~<26sL?IzEqbe2EKspFRbF8}pHSyn>U}k}=fV~XXLmd8 zqn@H41S(^3*~M3wbd=T}M(*P!I}>lo@-A&zwwe+L~?D$#b6(OzA+n&o{h zZB^6pjWMjwx+0Xh4$C7X&m)<05`!WjPrU-j9zfG9my0*)h^9ChIKY<5A+Rr+5S4HFa-w?@Zw1A_V>I#-$vr2VST$fwaV=~Jp%NJ*x z_KUG#DlK&@U>Qm6M1t#Uv|`-~4_cVA&&@ep5_`-CI-&^~5-e}g#5d|#J|2a6+p_Nt z#md9;x3)XqFz@71s#3^YO3|)EwF3*CBx<*dUDQR_*XaLk{LkMw$=G+{*PH(rTGx=p1{U>P z#Dbyv6+Y=eZy#=A%(KV_Hv8mpCl+oUGpJWQi)z9~tZHvx8zsO8ed9n1H$ki)@AZp8 ztQpkDKx|Vt9}CH!BUs8DZn+$ZKS7k8>c&_(yh*V=08?p$=VUc{)lEBjREic=)73J9 zsrQ_s%Y|DoXX77v(sa{7C$`c;|7r`Uy6{ckt5;8{r#UF~Uu$t|QXl-6=s)c~-M%q@ zv6RO;J$pI0i%<`yaub=}q6YPdy1PWro8-*}k!V&RX^;!_M_!+c)kjSg7if{-F>{h z_YnX0Q~ValSv;|`r3o^t*O7Qt!#d8%xbC7y;Y*qj5xyc4wMXio9o-bN2(=OT>^{(+ zPxxy*|2U4Rq%6hi^b3~r$mDvuc^}Kpe{ZLH{(IZq#}DWKQ~Y$^DtD&cqMzC|M}=x^ zQLQ;e(C41m!*8YEg7ZJ6X)t6=3dwO6M4ZI8Bmhg!e{Z|DH+TM@JjDP1B)<<{=D2%G z_mTeC!WlI_qkVMIYgv=)ehU?WtX9m?RNeJ0 zqyx9r*;EBPcQ=&~ zrUI2W3K8M|{+Do6fDHnv7WP;g|0$YZrvKZ$o!tle|0#ZTgnB-t@NFq{g(#N@_MIqnX-EDM>9d?=q>qj>{98_1 z0vuak7-Sgyo70wI8BN03H0h(?oiRw&TycKIN){ly&2)C9^FO31<})D~CnnC?jflWf z{%@yx{&%)_ALKuu!xJ75)Qmfs(ss?>+7H> zv9YD@`LVr#rh2#x=X@bPtEOYM^ADGPVB_H4lm;y0|9ZVA)%V|SxBGDZKgDl}pf2*F z;ELNac=IN{&0Snyw}K$}C=bU+mI~N(R5iq=y__jjVA?*ZS=h#~gEYi{&DqVl}Yn2=*m#BVN;$F1N0 zALio4yS{Ut)D#L*Q`ISxKBm~w9fUuuUZ_O*V@LA?olBs00RU_~404|6(JCevJu37$ zr7OsLzApr<7STZ#gx?=%e$3(%;X0GX5ODmFPd!M~U!&rXzOW@}KULz1=GR zzq9wC|NkVvsv!41>0)`?{1K_6oqFW}7_vC;hHq*er6X2rGheQu$Y44zR+%@OpI52X z45(d#R%1zD%e$_0{u3IJRNPAU|Cbf`ziw~t{lC+D;Qv0!PwRgcs{a>oRgG=TjxoXm zw-ZYbwz~S}zdwKg^5aQ?O*gkANsn$$k{;EPqlee|*k^AR(*FOAK z`>jO(r*zEW#0MhcG`pz)c&YxcTh;&Vbe}xX|4;H;6vfJ}x*xvkW7+}CZL>cz%cv(q zLNT#=v^gfOsM4-Hh;4Jq((g$!J+iN^C7`O+7l0i|EOW7Zbbi$;xQFZGkN;bV{-;D< zGJc-1ghsP_xBu_$?#|i&@9aF#|4;I(3BTqqc9rCtk2D>J=TAjQS;t)hZ}q1WXm@W$ z8s)wB=^6ILtoUjyy~s#Z#Olph?1#@q$SVLNvQiBj(XkS$>HpI9yh(<$#Vx}`A4;TJ zeN?!F%n1D=x}J^art1XgNdH--CDTj4?R))WtB;8*9BLs4up8X$sJ$^ zLGUMuL9iPAABiBlh;Hfrzl{Dr?slv6zx(8Y|Nk^UbBf4sOLaSPmJ!rWN20Ax$lBUk zDPa8f`$&=d`uD+!1!FSl-`xl!$S(efj{{YK#;cCgn<+wC>Z4BLg~p-2RSJT$*ivvEyKN2e`F5S9PU*+V>VaxM?FW42V&_CF&pq8!9u(R zw6)c8xG*<;Gl1W6BBc52uG>~P4o|_FVi`?ddsx3US-AOvEkUi?f;Gf_h@UC_7Ly>0haV^Y zUvF>kan=8~*L(8t{{Iv|!&;1Gy?rhuKjS1zXoL^oR#RMn+(ccpEKLhEozisw0@DN! zlTxgFL8cdkAFyeLIYI5eUG&23Uf2z~Z4@5hOy_5&M1()&Lt<`2-*4ByBegiQbI}_T z`Tw)`{@rccNWo0OeRs z^M3Yw2!JF20{kV~aZrZ834><8s({ju631s_492IpF^|zX5P2&tlXLsd=y@$q6SS|$a?w|B>C?`Z%I{iu*T2Ie zc9FTY`*fcy7uxoPFkkhWHiQ{0)i_HdztyEZ>Ao~GK@N?8o5^ybVb~&0FPx{?&K4D4xOb7vAnEEuZ`=K zTv>_fqF|FpfEf80g)3_F?7*x6R7 zMTo9}7S;@&FI_i|xum4g%Y2W`Hiq$5%Xc!#hmHJdA`9A)C7mPR3*n7sxg>G{rv3Q~ zWVpwkfe1QD)@8v(%qv_AauE;xz<2%GY8+r_IfuSSLTn)~6u8b<! z$VDUsJ3N{{~=jwjL;PP_8Zs~ihwTs18jree}4|bIrR8wgm?*`^Z7d$Cy9RkXXiPXqUU^S zo;(La8;inChqfOiwlx;~X~`~?3Sy*P@9t#1>t7RN955 zydN_vL!`@ILW^7RQp&dH3*Q0zdwaDM5eX_K|I9*n=m#qxB$bU;v>HuGtDmUIR|V7* zoIdk+>|enZ>4$hpLg;8DO$;{8$a{7TF-8td_Y^FVpS1ki0aXtB;Ub`o2?%N+@l1c< z)2CWJ5oV`YFsPV`Q`>aoG80J@rQ|RH5hXT8Bcc`jc~0MjVro*Ab(>Y;Wf^M(7yfmm zLA>{oM?-{X#$b&tM!Zn=W9ZK}43y22U13OIIA9sC20y_S(M{bbDnykwRuUWz04>`E zu5USDiv0zMPrN$M%>vDY1+!WmMmwV^vaJvjIsT=J&`H!Ps|0MJ)*6_+pW43XK|B1M z$QVqA3yXvhM@xlbD$IV+Ruk|y0m$zn<=wQgda@uP&gK|ChL~Zqt_L9p&lOx9a2DuZ!BrkJ--#_zV}GISw7!*xLwjI&z&O!ljAb5Z7PdMRTT2a#$0qe< z*vO--ZZX+%3=f6K%5SElRS))BW*xg_m-&` zN2#<#?mc&D=(vd0Xcd1-E9CN>LSFn(=!lyVGGr)-#B4&TyhBsx@(VgI)skIeju+l_ zZQ;jX8DoZY0GSMzcw|@qKBBXRRaXe9Q!bh=_@1D-0`1KxIFKQ-T0b>WfKQ~rDi<9 zke=}$vIe!r_oe4W%N4gUt2H>Pio1l-QO?|SPv>BuqQoOgM^f!Yr|$=0|NRvEGik$U z1wk}cdMody0KstTn4lQ9M*TiUOE3M>F&*!uv7n`vg!lP1)zHs6C~BEPh)LvU1GMCO zBX)7um&6a1I0fzItr}Jk^sd?6RAx>vA(T=Umv^xf{ijdhd+`0E5W+fr>Cd6NNMoA! zTz|F&cEA7kPD$TQ%>~E;!wtd zvAUnfDLiMX*+8jzQAFuU+R{vk%$K~qQZ6%`560tP&PGSK#SC)l)#?lp*I$n*5-8Oe z!-Rax9F>_P9?IN-&!=K##Bz3;Ivmok23!i5wOB4`GgHUelG&3dl}0DGCXi$#RLaYh zCfHq59AUSrG|FPz@>_Lbgxw0{t)tA-v!mg~4};<9;N{6MBOek)F87i(ya};2^sW*1 zy#@5bpDm0czLpFWv6j02HzoOA3sspl&I(b2fa|X)-jpPCC^`t$B&jmZsbnK60kub8@Hqkh$@4{P{eV83 zA5bG~#zFwa5v5CUT+vk3h$?iuv{RVzUzk_bNjs(v9`1;A-xJ@vU-TpZzqs`Mkc8L@ z`ZGJ|39*$}`fVLq=?W^eRdm;V?vrrfB8x!bhJ)}@XF3+^WDoZ={kHW1K@w-nz(paS zSTgbDlgG@=#I>-$3}gGIdMvlbc^?B?{-}?m@l~Jlna=sNkpRAzHj(fK+CVrEW5X4n zh4rBq;#J@yFYNMx6!r?4-5@XGqA9djwkyqh^3o7kvfxUo;i|%quP#PsZzsdi#pLY# z?BwkAuNNn0hl7)g(eQuY9*>4co_8hVUFcsSFl$Vc?7I0m_AfIbekh*`-;+2? zc`(>rWfOUt-6N7tSPt?aAx4bk5+H3~*{7mxnF9Mvg83p;pEvgtXxnJ#KHkzgq7vg&0~Esmvm+4;W~D!1Crk z&j+mKy1Ig?q$ikJ<7|ExANW`{TPN!LxsOBr&Ifr?M9mF#nCD>_B-(T}&`Pk*4ComS z>@4Wk6w#O|BAJP5rii54TBZm+l=Vyz!ftIXQ-lFd!={L2M^?rZA#LB9G)2UKTRX&E z+S=n$kL?lh?;?9dLI$lpqP0h~_t4~4+IwiNC8E8Db|0CewLyT^29Y=S?LD;5au3a{ zU$#C3_ju!;%u=~#3%>}Ys$adJGK3{M9~M4FZ7H+0 z63IB>pIss)vs6tYr7W|KMAAc9Pa+8%x|T#Tz-b7%wf);okQ(>8gSty`T9>=1WGgGb z5?M*7Gj+O)uq*VqiwNK=lZ%F4wj%8wi!?SxRgXraI%}SbW?k{Br|eC<+0V3AgG80YJ=OOAA!s!5Nu-dYmG0A@WY zA~M-plEeh4VQCWjf$#dWRViVT6mDh7S0YQ|8C7j`$xP=(Pe=-VW%5KKoYu*vb+Y;L z&NW*no7TyuWez=av`#jlb+XBu`_{?kemL2rdy~(3#V5g{)D@q&bZakY-d>Q*L^X3k zx|dnUTA+urp0Pma(3#){yj} z86D%% zh^DL4F_vM6m;)uVoh@Tj$%7}RblfmcRE0jD`%W|6mH{==a|NWX^JRMfA=(Yu&?E4W z8c6a}VObx@BIpL#cb0b8tl(PHXOLYuGi_VOz> zm9Mkz+*V1^SwVQTX*$1#G@WZK;!M|jV+CuGlpRAcO-a(%E-PvppL9$#w(2VV;;Loo zUKviKIT*f|&Mm_K{sdRI%Pk5@1@ckV%=1Iv&^$B(4;h4XTsP7M zlsVK}OUl!L(6~}5Pis_Ol(2{0e>V#KDdbxI2s#+~C;Zx>MN>fF;u_*Ze-T(1g3kY3 z@AP)Jd)wXZ4(J_P0dpomko109Uc#RvndN&u{ug}u1OPB@-Qu(0HJ$zRaBB3n#H6h0 zfT4v_5z}JS3yd$Vo5WqZ^)=w9klH2mT1K!s^M=Gd13JBL`W*=eSqNS^2!n1A zk%t0C_Jyzm&^^IZN=D3oBwAp~SOi2fS3jzrbmMsyW1tBAPJBdi8tj!pUN)VJYk=>- zDt%Z$YSN);T}c3KG(~ouj#0x=F50noi|Du_AfD^JBB>E~J%br29Co2%k^`AZT z)CyUP3+%fvPm9abAV}b%knh{c#6OIs56O-<5EJ7UCG^dy%9H2kfS}%$dKkl>ho_Tz zRo@IJgQLM@P_Oo@(b?%_czRT|Hmf{58jc2&2_#~q8S|EOWb$E7q zIy{`LJN^-~)vcYx&n#VAILWT*q-!GN!QtU>Jg$Zb^c=l=!sP7y?BwkAuNUL{<`k*2A^+aBV$YTMt(w z%eEe_t%qyt;aZZUS`XLO!*y*gXgypjdH1FBh8WF2+(q8huQupWjlK-Py!>d&mExlN z>26yTqDVCm6$C5FxBRT-#A^an&yzQE#BKCQ*#eK&ar@mWIXLTk{+fZU>;7A?-mK>Z zYy?%y6?ii>aU8s;WDgO5nEgv#e>Ri6ltjxH_zPedxRwn;=b{5T-H!OC$KEvMYrF|M z$U|XDr^W8Z$xd3XSnP7V*{siTA-G$JC0ZCn^dXspKCv+hLedW{(AJ9fBN(=-=17HBIKslTp3r6k(oi9E%cFx_XZ5YSp)I0uX>2Cd7}o2}4t zk!PK#yj9@pZK~F??=+E z_%kQ;>7>^_o@YHMd-$3VQ`~Z{m88EUxK9ru`f>T>AHCH=^zMn2;WN)&9e}9cI_}d} zyqz<*7>GtGmUmtWPEtveF09)d7~si8%>Z+Dg#xT9D@~dG@fbPKwyeq(}db201GR$=OixfGR4PwP-qC0c5VlGdlAd_!6B zS{nK;#PnE0*slsNE3BxzGSg0y*%1zchBwSfj{5^kt_c+k)`u{9g={N?Y4!rGOxBjn zY!z&)YeO6oQGdoT6v>Mu2b*-Z;(JxajoRrC;c&EM-}IGp{6q3g8VK7iJeyANl7wO_ z>cM|9Dzrk^pLWBjO4T7+3y0#KmGO!z=Mz`HCuQ2KVr8o8l2PQ4QEN`BB2jpambyJf z%f;C>#27g+b%8SUhgDfwb-Gfaxhhj*IInqofEo;t|1Q$(=dG0jqp5!~{RX z9R;V@&0wPiUfrRk(48e8+X^jxRPH_(xT)}ifLezIIR@Qk2A{#Mu{tKrYp@oZWaSoC z&@>Te02`Od*4w+ZxA$7U-huAIhd9SJtyf{9H{)w;-Qe0T<7{qR!SY>&ZwYhON`(7{ z5yADTR(8cMX@|brMybrTss=ArRUCx%np^)|&2$vf%)+jRZo-;+ zOqK9$%XMobTUA}!%;tFo)q?xKB0C$$TRH22iy&MbA;~O8(4X)GEs*(!qS#VYIz`W* z0|Q{boUCfR1sf7GphxhJoB^Lh`|4;JxYR=MR`hdavp^?-ho%Ag{F6<1&F1e>n!YQY zu>{w;JFeT&98Up39=)r;wwOq)Gl-rR2qlLv|tn$vV2D(BE z#txMs*E=!fmDYsP2K2-!clA2e8z#I3YQ`_Q2a;r1H%a0h_ah_E3PzP38;S76-Amj9 z5WXh3`ihf_0s|9=&lqR3zre(*>2>V$Z>Cc z2a*HuTdDTaDp5<|cMeH#%ZRBJ!#|dggwg>W$F`!1qEV<+?W%5=026_(A21*#$=WEc zN23*C-#*z^@2r86bLn^^sgdKNn~#YAPWG|np1X3;)(kcc@j}sX=`{HGpWRQNHvZ8* z_vDeqb3c~bM7;IYwr{D=(z7I?h5xa=c3W!azK|zRBjKv(0(paL3%PXP)#aRQNbImv zKC#1w6(IiW00h*xxPQHK2}5gVLtM(W#Y7L;E95x`;M{kP5W&kJ+Ah9aIx`qwoMvR#65Ntp9tv zdry`1fA{I$e!KqP#lzR~S!g)I;~aq@wR0Z&OE08mo> z5(b53fABqb_0Nq5;4Og;xLk>uAqWV4K<%ojb3X8=a{Ws<^)X}@pOH5M%Rmwl*E?Jw z@FlRK5YVnKU%3DPMShs^ps;a>Pa`remwTE`P${^U=lP-N;wii^xOeU;>vAl_9)NLF z(Y9Q71qci&iNthCk0e7M9I_x0$LD|`FM2;m1Rw$|55!>-DVfF9qtSrY%WOaE!?D!j?Y(d3DVs>VqTupj!i?;e23;rRxT zW13=r8B*VulK2>6rB*=A03jDk0ZsvtsS!*Ix<$*^8){RuQ&UIM$fp@nEnK<8*sn69 z89N)~^d=~YE{h~i@e>_qO+U75)Fo-qWY8{(l#bs5)CgK>Dfv%qM$X*^u((>Co5l5ajq|SV0iq zq8Hj(U;7ph-{Wt`(I7yb8B?9cjiE=d2<^)LGiD^f}OB#dzGE31tNZwvg_JM}|& z049+(ZUq5V+Nc`-VGcd8@|VEE5G+Zg(qAq;CmMuwT)?C37|~n%5#-}(TAz+n1Y?T* z1+W-(#Uub8{SVmLe%5>TUGHWKgmWtdbBhSg@PG)RHH&tp-}P>QkHOA1|GUfoKIz>& z1`F#70ydko$$ zNl5X^Ya%_s)0GlWoRnRk$*Sy=P?t0^sxq~Z3p%B0AwoA1WEn&{6LjcMk-5Kg9dHTb za5YYe{EjK?9=w5eFU~%x?^0>USL*}mk|&*P_x|gh3*lMgxU96&Lssb_b##IOhPD-V ztx|tEobL6LhlXw#d=kxWO^AjLHSE{S)zX-wx`B9ocn)4J?JF39!7`jj1#RZ;MZ0kv zw@ELyq>!Z1F?5i+?&&v%p&C-;FA)0U5F>A<@z)da1;6@B;6dob3?0WYp?Mf0P!?nI zz;}QPuc6yBY)&&c(D?r15f`h#yy~@f!Qf!0h3etuy|x7({(?r;tusrY&HnI9|2hO&0e&m39*z=-5?I!WB2pu5NYX`q zi-o7KsMp7t9drdC<>MN1e^`F>cvNs(dHLDjgjDmqEgvZ??tAgR^d zd&e0nBhormte9rYOA1K{@dA02FkfTKhUX9?x+U~I&g+(=UsrN#3Xw#!`}!o@@0bTS zQ|=sRzQ=nn!+5LZJ7r>R=95KsSDXmaN|@va+wzgIo{ey;iE>xdJc8^~GdcVTiLT_FEUD>sz? z_rg`I#g&6F=mu&+_?pBPXRM55(etDkQ*iO(oWlC3-ReCw`e?#M6iMA226LbbLwNM^ z9Qy&pA%f)08?zio8ct%RyUt}dNXoY?^cAzvL%aap>2)=-{q%{Z>YUwWOym8#E_&SE z-3E7ecNpB=-Q9I?*TLOkaCdii1|J*-cc-V%^WQsrt#z`KlhYSX`=)8q{Mz(;f3E9x zWAvr@iBUfKf*?1TaFDg(n-v3rMM~^fjjphzS767kTlQv>DLbUGQLR|y1_rX>O6)ev zfwTQe(+B;!#cOML(BSq+~Hv$$xw zyyNF%2n}U>+WXHMyLouFA#BBYu}x&O?|e-rOy03C5jARWDy1se{1^XDwWmVM{q%L0LQ)xw*Zzrqu&xL=<)533#jy(uIt%)Kd&VJscX1M7posnJpE)EKfGnrDV)5Ut1c00SEg(*8(-U0vIS(j3E9a*zQ6Kyv}oQ+iFV!>_W2R(besPB&M ziiON9C}O;iWbO#?1MT_32 z$$4=@P*lJw)gTFb;Pkl39tuZamoyecvoQ8qb@Q3uD&jeU8!gp}%P~Z-kztE5%53u- zn-k`Cn-iyhWSg^{2>OuIiqdyT`N)^pJ4vUUK(4BeL!F(c?r?HM4r1S8bsrB{PAie% zIGbgGFOQz7FTDnyO$=Q;arX+&BcS87k5WS&L=y+zN5oEZ^!6X zV|FoK?JRIW&*s`%8ySVSIBHY%HN)!4vE$E%qZnS?xkbl9haVy8QR>}reU?SI3G4nV zn>qENY@Y6dZrwT)^^vL_Z{RF=7D{*N*dM}g z4~V9dyRf;#GhD4;*TuZb&+hIWwtZR(l&Pdd(B*UqnTD6YDp8M+aP)+*W#0jxMZJ{6 zt~49V!mmZ+$$O4E`!8M4;Snc+5En2w-FGa^#PPU=3&BM zd2^RxN~sXRqC#q+-Re-m1%fC68pzSsXA4zxHKBek0nK~2acVn_4j=8u=EhCkpGg>@ z70Slq?xz9KSf?tb-HnW8E3zPXQ*=;0_C@%}nI7pooVu0atdwluC8?Y}xnuXUs)Sav zQQ{bzoN7@`etp>+NB@Rie#2@}uIiliSeSigQj*u+f~`9rq-spWT)daopT;tXs*7Y< z{7O)_WUK^(t?+YY=-uST9_AdkX#Wy`zY;5~E4#V>xA{LYJx1@{Eh;i!GO3sdS(aEe zT$=%z*LGzB@N3R@`@6&|zuL$R30}c)6!?za{dT`~; zLG}xpH~=+KjsJh;J5V3sAq>0cFbiOBQz#c`D8_J|oTnd(yTNOp^GI)5AR^ls3=8h+ z1tZ)kpb3b(-bjBC;h3bY!oJ^`+ylm8n0HU)3t>(Nh59}Ij>V>&*3P|BkFa$6>Mn;H)0;|N6wK)2QAX_*BxJ@t6b#TTCC@7#I;zns>5_&gdJX zz@;173_JMApUW{)&<%)rO}F#44=6>4gCV#Fr;a>2Hb3wWj49TjM)0O>PsFWF&&W@; zO(1%FGcm;oB}Y34R*(w8c(*$(%v$~{4=khPL4WMMiT^O!!t24~(D6A-kDq9W`xzv_ zny!PkbFwDh`wZ@ejk?4_6^Mw@B1byRqCbSl9kq+^%;fAZlc$WTG4D{C_(~b7GPw`N z9&dSnhWi;)@u#N-2f%O=I2+5w)gI5HV|WXI{wU1EsXj=^l( zpf`vI7Hg1}DqEbQi!RNIMtgK5i+)i{6JM{Zj%VIr|60?@Jm>&9S!v}LkEAU?0Z6vX@)7sM6*Y_DO(9PwQu%=m&tOdqOHuiltLnTbHV?y z2xdTwAkEY{dm$`sCGCT{qhqs(xqMAG%+WI+V)d^?$TFoJ(D$n38$>%dOJLwIH9j{D zrOJB?ju91HH`|}Gtjb)Ewrwzb%EaJ1&og7KKSE~V!82TXEftI|Kkjajqn^XJ0^c)= zN7VGAT#GP>)C^AOuAhQd;dh_3bMek3pR0;=zgtYimJ?KF=(MD_o0j_jE)aC*NX2&2 zxr+&pSG$i-y@%@b@%noV^gw>Fvi%P~AnK+9@&jHnCtJBxdYG4g_<=Ei zrWnW%w&1;)Um#Jzy|XR~GHd_C54wT;U>buv?$2?NZAMI%T7PIJg1NwO8Mh9ez}N-V zw|N(pcI(?k-pQ?`0%ZZ=L7qVcu6n2TzrMZT)^rO(cmLW0^vF8WE5683|1{d(=e*QP z`!q|~%$o9+CB zA(NPm6ZGonO)@Pl5H&=yVk)Nc!4^TP;2r#SdmJuql~K*Cj5J%5?OpD?br+r5kO8uHN0XpTazWd7_h61pHbxb1X8>^kIG{f_a(&kv!gj>Srf6};~ zy^$Ok)>bg(D7Lqz@htDVUi!xxZ+VqZ=KKl%dJ2x4-s;b;3yl94f#7_KgZ}?dAn>+& z>}+!D3iizA`XmEbZ zL&EWwv~Vh?TE=O!h;F1uqzfXs3|AGd^e||(Bgul8_M9-;#pEh$BF-OnJCa+0_8Yeigp!cjEaS}6xLZ`HB}4*lKyur+&}E0TN_+3 zazMvm8`_BpN;`J{~yP|ZwYh^7t=t;z;^N<$DmjC ze;kALtQ8~E%N$u$Qu5#$BL@VQHARGTuVB}qba`dggY)=7nunhg=uu8O*9EJ5r&D~1_B|oM$7wtd z(+fV(!K-}Kj#s+Z1@C)=saNuXClpl>F)Ha5`-?R_1>U|QtmrG#kY@%KgtoyzCo&3F zC}x|dm_@=7x|`LC0G?Au7{>@ZytYc=qO3KWN4jRUQ9&x=ID^;b!SX$C#HX!^9H+A) zSN5ALWCt%-%OPh6gQ1W1`D|M3nWjY+&8fZZRUK(Bz6E0sC&|!jBVqJWuiC?*%9tyn zCnH-0$97pNrkTK4ErAgt4G4RTjT~4jBCO1O$?YaF4e{+F-+d_c1A3sbF6qm6i{P3X zz3t#38%0qEd2^+kI4UONw#JLBO0D>fELdwPBbZL5nd|J!)opU})i!lnaOWh>A){Y5 z=#DJc7*W_A?h;3)>liKe;X1)SfYx$_=auDW?>PUp-C1O|Nm@7VH?m8SGVNG#B@7JRy+i^?OHf9)NM~lH^{G( z_Ak{@Kw98bb|g&%-ep%j&!RoHTH9>0x7cUtI0V@(Ed!rjn`UjuijTV2yWiKiGW#uM z>V^V7VQU@It1(jHAfl=UcT8Q9IKi)y>-FNJX>+?EG9n~NI78TD&Lu@vGsSwe3;7XY z;^=4qutP#H){+W7U|Z28xX0e&dvgMf?*DEL`*mYEh2Da7rhWu$TlU^Xk!n&`y( z1ph1o{sZ}rJuHdzuz>La)rpj72El=4AifM901fzRD-$P^=)ay5;f}~NYc-SliU_C| z-9acZ=Mhk7=ws%@c?(Y?-vt4=_~#Gy!v;i!Bghi=fZW2piUoi4Gd`V?`bbeN8mx6Z z>uLG8pK7c*N6bO8u-_G<<+!O1_=Ff&0VJZdq%NjL0U!=n5vd48--2=x+2McV2)PO~ z_u!fNLALP=2WTc?;K`AK$cbSIfN7DY)v-jfiwL~LdT){J2F7@Nc|8OhWC*w0JB%ON z&1d(44-KiY%Ke`#fJie8jgi?}s+U#qNOZ@&xJh+vuw*muQ}JIJE;widK?Ib&-Kr%d z4zHRJ0v)ZD#6xuu*n+sS@m|7eQ$w-^dFg(>HBI2*()FmZv}xL$DNsojWX|q5Vul>DXgZLrW94higFT zw_?TpwP)h;v~=^Zzj7N@%O(L0=l}Ig_E1+~dHS?IabvDGM_#pK(lqu%k=6p@EY zX6qd6k9}+pdAF>D)=qt>fyR1^Y>F9fHkMrqN1WS}iy(z9!87Pu6(3QYpMV^}LsG%? zi)i%bWbdrRV1;XF;*S+G?((ls8#fPL+&m)nni*Q;gz(~l!gSRYU}o#NRr-cDZJ1PQ;4!33nP5m&lDf~>gtG7+=A9h1=&3lqM>2q0(`3$cAJhYtS+!_}+ZfkVN zvzRPdOAo}ssLbwIjhe%26mQ&3kJM4Yuc$X2kBdf2XEF_lyVL+4Q=QYp$XtK!OEPUt z?NxItD3p76)24R5y}<+l~}?gmBv8NykN0#qWgOLjMg37XIgG(ztWc5 zuf-xRb$eBghMm~DPw$N_er+zrP)O$43QcelB>%}oP_xR`mEoo{XUBHblDA%C`Lw&k z*5y9^g-6&lR%7UKo?i1f<=~*6o}0N^$X=|grqO0DAwQIc^z%5hQg&B|>0uRK{~G2C z(x=eU0*-|?xIL!3($GGm7|$O!$eVC6m2QOVc?q_cdN6HtO9RD$Za2O)&9etqNdWsj zb~9*U$`YPmXN)-T*WsV)>tsi$)>^}W0kWn%-(FNT2I9||<=@UXMakykASRq>9ba~o z-T_gP;y2j+%uWpWKV~JM-8u8#h~QLt8G4};FDj3sM?66}vwIMf zV6L#h(6A}Be58*H6zyVvBtD42cj&L>KaS@hd^=L-tZGtZ{S*7R1ke2o4y zHM2nR#QotT%zNR^D)+2S2i?qxGYeWAZH%4i8mYmqQ+Px+$E&RUtDk2_t>$RlcPZZOJ7+p>b^St9GFLjZ6Nwf&t#*des5G)N;55qFFaTgrH|0Xh|x1w z&+X!nHNEg{&G}?zkS(f$Pk$o6eTh(32xu{ffA=MB&z-GA#x9BRp0x-NX(6+>pRic~ znT7ek%tGN-KxT3K_4}TXJDjoSsBE9npH5hdRu6TA|D*bheUU@M?M9Ra6oe{-*0Gz5d}$yk%Z~ zH_?u;FD}_`3Kt47N!H8|czsh3LpRXwwNj!mEuVjPj?T$1c%^M^7_;84*3Gfi7v(BL zZczup(y*FSEMg^pM+KpSxyyy>sm5jVXTHB!;;rFin{kEbb1%mQ4v;I3>30|1jubSm z)N0t2kIG~$W(P}29`qwCIShWgz%tP>!Zff7BQ=)e8hH)n9Ji;}yG{2h4_x@b6l;#R zie{80&QD_6PsDcG>gwa~Cr0v;gcoxAlJk@o4-p%-(K;+E1j1PQQ@oee<(P`h@ePXp z_Sjo@`;ktLp{tzGkfL0O+4h2wmKF3VGg z8Z=SY&>+mJn%5(R{mFe#?~0=rLoA1Qtgp2)(2xUpcZam99X#z_J&5+hGT3dPSYwww z$L6^@es|`wW9pD^s=9}KRf>MM37i=h3`^q%rykr;R4V6{guJe*v+A&20GrQbNCKrc z9z_s@cuM>7dvlp*17l9QT<)ZY0_q%O7E9P_H6*1(J0v&Z&*66|@wPh&2J{a(F1Q?$ ztN}^*?oMd6LV&7ZE2hp{#V{H5jqQH9bc;fkivoBSt{+qv6fV_&#~Io6j&%sBy{z@C zVZp+l*RnY#*{i>p=dy2W79E`I(>ofB^!o2UXZ{b)Xo=GBIAJ=N-t}|z-RJi|PP(?1 z>Rp`FICd}%;lihjAMlx}2_&=S6}PoeUlZdp{;UX9G^^z!U#AW19bIyM}} z=2_=norE=~>DsOwzLb0%_$H|BdKt*b1x@LH&tdZHR0IGqN8RSxf5#lVF=?~B`t-h>;lKqc)gZU$dC)=RL=io|9A%JSKhJaH z{>2-5)xD&$!itX=Y&!Z70!t`rqJu10aalRwqrA;_YEkR^@i?;3^ja_B;On^9mv&Oo z8v}(PljwvCj{w&>>!0Nq0Mp&DpB;k!(Jz64*O`NWO~pL>wh!Qpe-QAp*oauIAf!Fz zbY=QL<%+GO9dOvCwC|>Q^HB45+jfqD$Z}CD1Pk@oOHBWBfn_Uy%;Y_R2?ph)@&()U zjJI?qb>-K{@-9qx5tte@w@5abSeZG5B2veA9Mf*}f{THlr(i1xltADMyVI^1ZZ<@b5=1vE5Y8M2J)ZJqzgr5sBT0`+ujZew> zr8jD{=_`ujD8SSu6T{k_UF6t=SnGn9SOR7!a{D} z`}o)K2;6@qFRog~fw+rZ>f}*#Njq0wq5M4>QksMamDl%$eGh{Si4cN=y}IjRj>Y2R z7|s{qK-Xvqik;5>L-{O?X2*w5odGmi7ChxSimEg9knxd)aeMAUNm&BWMe#nj)0CSV z`)S#?0=PO%!sMHBKOzUUvxY*YN*ZckW-(oNS^U`CbrA}Ub3SW^!(-QQm*-zj@-djN4U)t4Awph=_36V#eF186&^ePeF ziF2}g)r3O`4d`(`pOm9~Yl`n+z#A-Y9eI{LoaH5GX0sL3)K=}}zEn6d4UfW$ z%Dyxu0BtXrW8t5Ho*u29Y+rp{b2^^@Mk0w$lq1$(tLW0|pQiag-Jlb-`7ql@Xe(Lf zTdUl@5IvA=y}-1m=F3Gojw-XrPaMl&Co9+TD*YuCJ0A15$^JsExyJ+G)&Q1#xPbi6 z^2@hCDn*5=td9b=kK~A}CR+ODw8A$L(^c*3I@4x3xb!T?gQoV2;c$$jN#mCHaL9B- zcWlIS10RN71)T$0>nPunaK4he&xJQnsqcCO9FF0s@L7M4rmL{o*xFdbqTXK`zLZ5f z9)%D)obb;9PpT^b)01@%TR>lmW!yNiyUPaW-)wChf0lx%Nh!e2MLQS=GwP>fWy|B^k~&8~cmo9u1v6F1GL8Xa{0?f4PoY45+R3 z?%AjY$_g}{1!3N*u(b*Wu4~X(NO5LYAyHkMNPmt?Ga@p?^$CuQ_hw#9Owpj8Ixo4YbkEn4!@bhcVjs;c5&{oDy9t^ zPH4`i^U445P0sWrs(6Kgpmq~xN9&KVGSxWw<@Gd@4`;aYZsHMImQH?(2Mrv=pHH9-#RM9)MAgm=$vd~t0x^BNy8)b0waP2Wga8{9wl_Zt`qiZY4 zK*=R<`Zk3}sT?eYV*(CH3U{?9M5gc-fmMSmhTlh#4mwX6%m{i}W-PsK7$+G;HKi`F z%rrIirP3793&ZFzOm+Sh`!x_CVek=iS|f&_$NrQ@BZWiv;|JQQUFo+h-kg2bstK8+g$Io`j_vG4o@wVQIy07Uxu*0ef2*!Pz&Im z0GscMD=&_l@Ae<*UEYFNRJx}gU2m6P#yN(@47-l00^BY>4vwL58cn;`{ zBWKo2@wv`!l_EP?D>JI`xY&Pt7b|g^00q(O4+^;{P6+r<)+dJO0_kh`^9?Q#D zKrTPUH9&HNvm*he^eF_}r})R&TP<8DZvjy^wMT;jt;@2DF80D&3(@1lJY4nn!y*)s zMf@D%`<0-FfO?oyFXoRw2Xr(A=29G9f8k`l$~o{lLJQSvDRfjCMWZ;12JWqNU6jk1 z8^Yc*5K{uy)1cm5xqoBs6w~nYOp)50h07}h_w8px1qP!Ok9hIq7U9po)~zkzm)_|z zJb2PqnMqQONtbPnp?ZFq@OkVn-9+|}a12L$k<~LpjnQU=4p zyZmtqeU4XCL~VA@EO+M%slkmsnD2Q8|0){#Tef5)M|Y1uw;gAkytkmV20I-jkODQ9 zZ!D%i4E~fQoDh0mQq|1UY<}@@G(l7ccI7Zd>qm{ajk>f#5j5$>7dEqm_d&)Kz3jS< z+&Ah&UfulWAACsB;{z^I)^eHy6;s$(+i1u5sCK7!!N~fTrQ9vmXFIx@&1G)7u9LgB zG#h}Lj>lrffv@ZJtGsNT=(xWc`}j8rbZn*-vh z*K7VbSYf@Jc}P#q0i~OnR0>>-8_>L+ki6Anb?`nv={w)`J1`)x$1={}vg@R~Sl}f0 zn#(cp+mdM^0qqrg--kYBqAQaH?|904{3)=<1rke{wJ=jkO{<~r%9!JnbrR-#=3dDo z=b4~-`o20H8?_45p%l*SpBa3h#V@!AJob7YqTi(xLg?dvJ?@O^_Be9#`CMx@*ITex zgeWRKh)(3 z^DnMipYprB?sCa-QBivE`!IDF@2b%aM%`#CNSR2(C(|>6zFoOkk#hZ&OqfRQH*DSs z34CHoTBEv0e7gq*WJeZmPe+aw?(W@gEM81p)4v3T1bqhJ1VQ?=s7pxs^aG&?LPbuc zia{C54E_EYnD6!riL8NU6Z8C0{PTR$e|tc8$`}f2B+t_ejccV#pI_uIh}X;eH5W4@ zHJ|t1Egrf_b%RYZ4~i@!RFpOo5cuRoCwYf?M}RU*D#7}221AZwNSKdcXea0bdwLaG zux81u&6iK6W%T^s3tsoWv*_N&`WZiR#>?NeZc-+*%Gh`rNp zVuC0{^y#IZDnT50(Sk;DFJ+6Iu)v%|20=FSCl`c%GK)qzSn#~2cVCD1v$!99UCbNm91U7k~5njV}FrEegt0nS?iT%;a`Dv2f=)vv3(cFW?K6)L= zCo?NwjA7vNG&}EF7JocjjCY}mk*TN`q+uyx0E|y$D8J@*0{*=^&L**Q18RHWrM^Wm z@2becK`m@9gFUFdQrJQ!%O3fv9#hfVH~xmvy`v9O%n$mjRL!Jy|79}^ru4>meo}Vz zMB@Xu8i+5sn`9qW@9+?@|4ux&d=J>pJyir6?tzrPuLwp|rrNV~zt>BC1aK8+|TO(YJbDdqA@9U#SvYt@u{`TWHehEchpq84L#c^z+2vr$KBxI z^E#}0$>ryV2}58rQft216+qB?bqCOsBGrZD?{TfDoTUYH%Tdy(vsk#b3+Gn$Rg}hB z+jF_0Rhi8yZ}lnF5qW}X z(_{bOWEwd(8u0A5a2zy#Hqe1k0Yt@jf}b)o1W(s&m%p2Jpy-pQdZ-mEuW&v}QklO0 zaGl1~6M)KITL6iULF9_9Y{tFcpY{_sn+B8t@S4yfs*tWSmk>rJ^lP%KW~v| zG*!{B+z-3axGY@Wmot=M_(SClzc>c`Jh52n+TN2>tcI<-Xw62aRz<>x+4Nd`sa@3{ znC87>Y>p@|Hm~vR@z?m!ib8WZuWlx0Cw9UqDm3}*-;!$#rU!KRMTLMQsTB_zM`yP1 zItfL096pR=NQ*qXtBktuQ#49(E6Y^c5r4N}p3-GLK5+Q6@FiV?%co6%UB`51=X z&sfI>?w*QhW*b^e zC0yFZ2DlSDKQSUQG%!1<>g2zA1k4amE?_i7>?&_rmii{ct>=FLX1-bv@=UGNx?@v1QKx&!F269jS zjNwS>+`Uv2ol0wusUj=cEIN}6%E=iCc~f)e1&j^4&7Z>(cZ)`PGd?7kogJC z6A#FcGzPDppAjzvpm-;ue6w(Y1$ThUD64l};Fy+Bpf&23hf=}i@JUNVb02NDEc%R% zQyQS{=^?o%LGFt){-eVQksE_FqesLlxHy)qwD34vQg|l~$*O8XOb>-O2RRsMGBFBN7%7S=9O0{2f5H)u zzKX$Pi?s8KzJT~uTP$-`Y=yyb-FwfLvQ9{8lz}IAuMzH@o}bak1~?|+}-nD-d+n&)yuCP&9;t91u4zh6H z#wbP_K@VpAPhe2H5y(caj}|l4X9@j;2>K`AoY~V=dgli!yKQ9-YL(y*5o;L^C5Owc z^*<1F#ttp=iXH(z+lRyytAO(Nx#f4_O}@*|IzDe8uw~17Mo4HjF@fj*5h%{v>k~xh zqvH^q%a9n2KBJF*AZhYl>)D+T^YLmTof>8}fd!B0hW`6C{%#Z^{`QA}u8x6VpOPOl z^M@&{jLpnvND9VcAYz@N!~Xq{P)ZxG};AccyLFZ08(>+&;!&)Wg8Lqt``bn79pqws(%uOx>{Mh~z?l-Zgsv~8tOMP)vKXobFm+H+4IVJzhFpeJS2o|@nnL@%sl~S!-Q^3qyNOeRoji?6w-W|kmTzuu zINXR;E&8Kw6V@_pJ%`i-0-`^0J^lW;zsZ_L><{Q}MT-l5=&z*(d{J)`16B+x^-xp!^yNzy};ToNZmC-Ay;0GlF4T%T&!` zQVKp(L!^niN- z>1Zh!%j>&nAlp=Accji52~fh}sZ~Wo1B92g9lzXZB@d?;@N-4MbSi9V&x_q6> z<)0KKErp#*7BuFh76+p|$e?3CO?-)>85ypc zWndFNv_4bxYdVtBx`ebkSxpANq|zg!jMd7bmJytEa;21Fv;%BiOOpis8jc*d8|l*s zGGnXg2WV)3g*rQwW>Dd_cWuy0SU+uO5twpW*dBJ=#lJc1%6xj=jl+eaZ&*QB4|oql z62jkHVSZZ`NYD-`vZGi~G`|n_pLHho%#?}A6MRbQYF0|;xgX99WAJa&xV`wBpcn}S zaf>HzA-fn!FF}~1t`;H}QKa&^DkA_ZPfEAP3nad^^@Ji8Cd~u0N za^Dsbk!;hP+DW!FMXkEQ%tN?_qiH%ln%pawjH}dyhHSH9mY;wyaT2d39*T#=iz9kMnz{06sc{`7DoW){$xoqy+Gfl4OX zkddKIj^wlU7W{I% zGV&5JVCCgDkTOPFEXonB`P-1d4B`}69fdn!{R9sfg<|8>Q2C~?C*9LlThl~BPU%4O zWyy-C_rd+*?d0qsh?K)owCm&cvz52)pc8>WFAnKIF%qfnb0%Iuz3e zY}NuEA!r5kYu^hEs9VhcOj`byg9_YH8I{vOQMKd=69ZXQ&ILCe#AK1ie1h z^f;IC;VPoU#HM(Z$mZz0vN_!Nwr$y^yBy-=X}&3r@Z28dwAzqz?0A(El(&x}t1~dp zgm>uDUFfYn%oHp!4o%Frj+V(3tT>1peu%8er01gj*N>S9;KEr|AHHH)331Qyn>M^b z(3T>xs!20IN(aj}c4R-8F(=D}NJ&yvVEwcVMee19!sUi`;%np%%~qPrM_WWEUg1hy z$s$Q*l-F?3xDAvgYMWPOG1Pl%Ubc+)m zL{*xba5F9qm~_fFR^5|oXsD-!Z+HV zjiM`djc2^8E6ntD%C+M_z7QqYMfu`>I&93D#&`x!Acc*3_p5+w6Rr zep)CijbiEsZCFVYr@xJh=@z}cn03iO5Ka0A0)Md=69R+wL9`pr?=VbX@V6*fhTr|| z>BS|mA~OS+`_0Yi`Fh+xyl`@IVZr;l-aJ^ivEnEA^z-A+`S1~B$Fb$RE`Km87On=o z*tk7y)Wgd?sU?_UJ8rDB{XUYs*ZAhhapzmN-Hg|9(~S3uEz-2Y;X-NZAVGDqHX@fFTK_}9BhN50P7{8!dWdXc*fXwOxiBzUP7 zOs|(2nAP9T9 z3lfPn)^22c%ZM%Dt#05O8B@j!) zpf}LDXKB-4g$*}#UJKx(iPVy+dV?0eT1&tl8AkCsFm^g;^=&WjeKu$tVfP4*XQ)Xmmic>+KY#)&Y3tQ@`eW zczU*NJ^~c-=Gupn)Q{9=mt|yB0vyqMsDjOHHFVx|T0eGT{DLwI=x5A1Uo%QZcDsN4 z8VlOC#%elG?~)qd{CG(k1kD#LjTj~{%R+1)8oH8(dP>N&im90mz*hWv+m(I+6g>ud z8Uj1L^8~i7*8qIp%@*-3gMi(-_CU|pBAXt#@8WN3nAJG9buKvoJm-(na8~1wFr3@< zA8#nPW!%qqUedQa9m?!fI(`b~>WyY0hj?_FFcS{!kZHjC_~E*-_oRfjQM(@5 zn#c|UM4G?(LP-d6zgfI=j#n@UYeX%)MQvav1d0yb)zuPUjOn;3;Gyq@s^jU6rr`TjuMQOb-NGoBz#ZxDmoiUleS5zp zc%H%NeA*e5QIv>OQ0HxAynKCX~jp-r52 zha5{59jZYq+=42PWTTPW%Ofx!((eq?@bm1&_{c;-yB@9ij~=bhK75;fA&g#-Oao6% zV@967&u61tMt`{HL5V&hnvNdS3c?;w%u{dZriQIl)jPn1wv!Qa=~>Qo?7v~q6q7G3m7=}lGg2jl99LBz+=7N5w%?EfA>o{Oq% zi41qTeX~Pf)LsM$y58Tt#mX3w^lQ6C#2E++L?o<#5Y$Q?KgeD^gD?#oFi{%0XGW&C zextayglIckfbSMwK7NdfM8W%h z$jn91LPy}HBSdYvNp&)iC0jstaZCVe-@^*NeHCt>i2F(Og6}(D(a`=d&$U)P1t(&9 z@3rVuan}*i`Htu2yLs31_EW%e4=Du)v(eVaz?dcqI*Prq1wRUn`j|KqwFY&=5($`S zx7=EfC&Uc@Y4^z04W+ObM|kTs&qB$3l!i%|?5KVx>O=XT?dfMr#)~`g=WC*0j-6{= z*A)uouk-FG;T|vXapP@@eGGhLyMmt{bY~KB)`i4zuF|4*kR;mKulq=LI5b6tOn$J= z?0cx$rkx486tRkHb?p(niUQ8mT5Y4%ngUCSWXO7CbA!Js-s?Tl87dY;6BEPd%2c%S zB(QYEe=#76zB8(%Q~d%_$YmD?@*3MY%X zX2T*L0=e+Zu7AOgw46t63D&}*NHdldB++5g9~T{7OzG1M?hxJpXy1W5MlIy-SwT1?Ya*f2Vs<7-PxT?+VuVyN_Jt29@$uhId!^?DG@j`1_kJK(%qO*i9{d&EpQcz0NK$3PDz6( zlgmm@18<62SQwoi(718*$3lsEJPO2E5+93!yH($ZU>468UX4w1QRg>M=h3WE;YR^( zmB$-#{-#|_3}9VJV8UaGv!Lkb4T2BA#qlzL%}dYe8*=vM8pp`Fh7n%V_u6aaof5a0 zJ%l6_sg$@2p7!DSPO={^LlakBcV^GqS7{|{F4pYNGSWK|;=SZbC=MuwO1`MN$3=*q z!++;pPXCTV=d3CG(LoKz*@%_p7XnVB^1&*(@w+^Crp35u3M*A8MOjueAy?k#$4~`} z!iTYsDzc>-JmII|EGsCNC(W&ClpxQh&Gj~CU|jUZ6o;5Uo* zL794Bu;5^CguK938p!ZL{J;aE4&A%$Z!KVdd%gci&y#YM`}?Pa)&nAXL)fYZcOOot zyK^C9D@ky#%(I$iM#={ZyD!vYKv>mm>1S$@2x3S?+8Cz_5kxv2ik4j|{0Kotg7t)F zwFa7X!%2i|2iSHAzaP_$$qRo6%_Ew2ryI%kb$fn1Z8n!~yRE2MTR`ACmsI5lGe`7t`H!(< zJ=q(RCiIx~GJLOf5HQIKIAu#gnG0palP$VKkcU&G#{$Ldg{Sw1)NC#nWrAe6zhtvV zmSVr?C|=X}0P%6mxJK3|ZVqdAMaeb5p5gC|h-M;Hen`nywfipBU+mSy)>gmN@)=2u zvr0Co*aE|r5)8h>fTC%*gh`I4jF}2qa5Fh()aQxr@3-0oD$_Ey75+zu4Oq{j z#1e>G!=8GwD`C692bwFu!0YEZ%Z}I!wNMj7Q1hJI=mEED6&dciuXZ@1ZFEoaQ(6op z=3RO%e87FZ~8df)B&GaXISC+0C$HK z7*-Nkg*n3xur7ie;infuH6U3wSH|@oDdP`Ak}8xiGE)zz_xB=-IS^|N$VJ-=Ch%T% zjMV(Q!7`6m&rwJ+nbZ)v6}E&o*t;|?OS!C6usSuFHcV8{o+h$Am%l&`mpbUl5||un zZR(E#H-I2wHPg@Wd>%Ld+k0d4+TN4UD~i6Zt7&sYQDQs902^F-&Ln8ZUN(*w>m}$@ znv5<Yv`zCHrZ#zO&V0#O9hG;)BgiZ zK(oIRpuQKgd)&m6Dos3znuxXByuB4$OQJcUtesgngigO>3wnZktSv;N8|}1_7c!Gw zhrn0m}d^>D*a=E-~z0y8dO>z>!XL)(RjK)>BmEzQ>Y7LL4M&Xt%#5OLAL@E$b8I?vs_9&nyB(5)p`EP=F++(`Vh+ zxUYAgan}JJ#E*qr&;44 z8l7M^Qi+2Lpq3*V@`AjI6SyC5;%SVQe{mDfPKyj0cuJ+;Ks~?wX?N%R@~0j-EodTg z8r~qDxgv~fm9ZX@vb$!0Hl@CVaejH*Q-)i_+lJcGDNQLjp(a`v_Rk^b%H%>4Fyx_? zpGbHx^ep4|^Ee$_kHs+KH!QVw4cqi4ied?qCgi3VvMgme6IQPaBsQ{){B^=Iw&X+- zK5o*KCi6TFA)m_fT|gGxzdn8U{u;MrDu< zP&qmp$?ci*;@Pu5lqQmrVm8ibgiIUekZBZ?#6TPQhD935Lsr6U|9`)!p9`Ggb^{LafJOaKF=T$^UfZf4af_Q{il<)vX%#9p`7)mmsvTq4(!KuH{N@B22}> zd}RqZo@H@DI-Qc;GxA(RWg6-_k)4xQW;Tyf2qodD-&}8rzTgWQ<87gxpaFK&24T}7 zL$g1R*mlh&t*VJ+LIoKzmP!#BG#t76RlS9PL;4mLQ|XqPbC+yY2s$zuxRA;N&{{+4 z?zJ3aESQVX;dFx40<}3-d!g`K6q$jqYSyIehW2!o-{q8rq}_$H?b?6^V<^3jr;Hbh z_ww+D<@%c$&FGZoQklT>JN~)f@4DirV@sZ{llBA7nG<&3wX|NRAgWf@z!ruhzMyn=gJK z?Vx>#xv6oAfdGgvcCoG6#iF{pBP9 z&dBHKEluJ`BGr^CwsI%>-JiN-FfEcizNA?WGFk)vlpOUeVUkWu>8;K==|FddGVMNd zVpCA|ardEg{Xn9%_7gdnFTkh`=&rK?-5Lil2xWNBRD8U$7hk9w9%;S0yRz^7pxd(p z)zB7D3>VcLl@o#+RXbI@Ydj@<#!@kfM|lJI`k>Ad1}fg7fd;PeMVo>Uycgko&}!`N zcICqdt%FwMK>eX7W#(Z~zIRyh{^I@qdm(c4X4>#1jR0@8llKCy zNnD8Qr>g=MDf@{;LncP#K@2H)x<33MOQehyekvlMp2yZ}p9>t$@LH3NHhZ)zIt`L(jOXA$d_-cB(f^1>w zY0ssnCcGvkdrNRi5iqz>g@ABF?-I#pMAw=VDtAe3YHx)AOS7rHeRrm9$D!XYO*LhA zl>ryW{y9KfwLlF!f$dfXVR6k%o=vG6`)oxkrxF2NRYVbf*rh2=k_MTwC@!Y3p$W~# z%#Ve0MI;I9>B5OTwTsz1tX+(DqKsGu;V^te206>7aSCJon9-13vMdG(5CR8*3cSo1 zy#f6R^a#gn6S+1GA>nx}eyc8aQPt`59+YdZc~%My-hiN}*Bb>MH8_DUVU z3N&#M!$#terh2TYFhu?3Ad311@r8yV&mz@qUxM|Qe5`jYs0&~^PgQ7;&?upln9`MA zcy5(LlEi7wi_EddHB^|PyIy&c5MhKS{;$JF&JP)((QPc?AZV@veO#L~G=15i~Of(3~!)oy`(3;PQkeb7gf{d^22@q|se;g)vguICGx{qC`1DVpq5O(oIG5tqusJGmMtOwfJd^+|WdZQAzz@pcDG3=4 z3#zV?*d%C{Ffh}lNS*aMOOK)TJqj>zZ5od!ImxqF@?o5g6ErHpT>f+dGeNTNM&F(vAZVC!byC~25WQlR8kn9tJi2&6PB^zyy3~7 zBN`{_O6Am#Ds}9;L`@Qd%LnO53`m56w~kk?xQ8uEIk1l0jMu(v}Pla`ov6K)-MM4~@0X4ws zOdgwuuF5Rvi5p-B(3$HJ=S!L#W_%?eZZEi3D^PnP8e|ai+5BC4cFVGPAFcT&L?C4hj2ApB z6B{ig(BzKJ1qt~~a?RVAl2WuF1Y=Hl#>ja(;teh6!3K8Lh*{e2bRDf?_s}9hyJrwU zM~WtV9EWmy?r1DYQr6Ub%u*2LZP|&~?M@!fTnBboWJ#?$C0Gt_{6RThE>7ilB}*$V zvbBPX2o1>ltyW!V1$q~Ev`Pxm1|O7=^I=$wMl8eZ6%~nKzx%*PBb-I=U}ZCQ@1VE+ zJ6+pd#g^WMIq6`sZpRr)5i$3=?gxAApyh(tw^n8qliOK7+Xa*BH4k^&gL*%EDaF(^ zjp}3#bmsR@>d%+kR*dB(}SI;O1HFnmwp@U%3mjZ5+_#r`Uy)eB3bZ zr&9Q*$bmK}SLNc+a5IE`Ct&w143s-_@M%!1*F0=>EG*CT=7jA4FC=$ti2hEZRRd4v zif1f|Q&uCs83=xk2(k$kcX51qPC}X_sQZJj)Q}QM4y`bwa7AnQ3OtKP9+&| zQE<)xqjO4+4_1v(tC5yPSzK0@mL0Et({x)bB zTYS!yb}4^f8We$-G){RQkLG~p8G}@9c=)N%cKc$+Q}9Hn&?_lf>GHD1_<+QDcV&*; z+ikf*hil2-Gj!Jrdw2UvnAp@nr^2~uD;@3B(}eOou;V{Q4+X zGKbVv+%$dVauoFe9&sio#z&aGuVY8?>vyAT`Q?cN1h;*l&)?>6^j1++)N*_^rDfeT zN=@nbeC6gqkV_t&#v&^~;(u90V<}tCVlso|l%3o5kma53e!oKM|rVR{J&g=#Q~ zQ($v1#c?6*?g};n1-rYu?NMLhrv6sA#V9jDGnj?HNS?IV*mWFD4mQfmY|`0Xf=|Uf zm4bW5E;&yy{7Ylu8VOCkcBdMEWwL#oEi=J|epKP0CaiFs)YB1-lOki+lZ=T8Pf%3e zgKG{Eimv>NTyIGU+znJ+0IENer+4Dyi!GWu`a@_GRg_vO zTEgN0Q}+&6=0tmky;?kdpkfw;_ItO{^9X1eLz%BdF-619mQf715$imMw7ipbo`?mG z2a71K1^i$MqTtzGLdiuFy5g15dRdI64$&k(L&_wYmLQtF6qzf*`I%hGTCa(UOH<^# zc0#oUa8@lTBsIr=xA{}|K@9c;+d?q7(rL&}Y{8ob-s--d(G^qry`4-&D{~Bv5o8Cl zG@K(V6jFxgFh2gRRMwcgx4P#XR@WSsszox3Qj%vhykVKjH36z99POYIsxRUM5-MiK zMH&`aQdYApg`6;&$XNhL&EM^NdBm8`ED5xEd(|}2pl;2o=P~g1(%GxsI(g?&q^A|BEM!$8qK)L zZ&q^|ctnxT;+@;J${gL%5{rt~Uh<^5tOGZ|$=Fto%9&$YZN+pbGck2hP5nHf>gS0W z=ZPt~1)gP;#0>W?zu|dM2Kzy~7j#^tYCFj&`v$2LQLD)_(%Hhu1+{fG&WV%|C>q@j znOh+NtsCq*4!%}1>mCkD2w>Vf|D%XADdz$B>-mgffSA7UrCN`6jn(Xqw$VZ2G{ka6 z&W2*7q}oXxQLA?ti7?XOEsYrV+_ym&lmP0Jv~-~7M^W`vo$M0ZZ2~jKEjWCLrzjPJ zt?D!G2^gE=)O_2Yu}~4y_UfWwyJx+%%3*3^ zz%fTF^D@8hq|T0`bk>Od-|HN@MBDXZ)VME{+rl_3+*)utr%j=@7N~=ShcwZc{tvag zQhZX?uS!Kx72BY~3*Ws}60bQiE1LRsRqONo>|y?Y|Ihzx(9_a#NcePx{8=n9D$7yM zeb~LXlrb&kh@FD~d=q_^lRue7=^~imQ-S5|DKCgqaJ%CP`5T8w zt*-vcW|+P9?hg3T>M{bgxG(p=>5!-?)DM6QHK55XPH_t=w)lEI`uOKQc@u{$6--&V zGHt6gzLz@@B=`urullCbw!?cn`_{kSGe}h*6Aac9EmGMSOzlooxAWHMx)`0MXcN>_ zuFtBoZk;VvR;z*tu5wwEkGM)6V#6)DQd=<<&V9GHhNoe-=ji{6aclG25_WW&@!5<; z_!3dMRMY9Mix@etW>sV?M#)@ok9tfpp86OV^ppftiYxTa-ycs-h<}BeOWX5u}(Y6XmNI%g|3)GBsWrqrd8w zx65iQIa6`#7xzjxW-0w~OpSw7b-x<8PGHv~pw%5?*EZjn+SpD=MaH5cjcA%blnLaP zWftJCw&{=q_jUzk=g0uh(C$M!=vwqBkvF$QEmpu4EREziJe^H4V>f~6KzU(c&9^ai zD^)pd3l4EGD7fkA)w%$OXNq&Fgub57B{4jJ+o^Yrs>TE<D%z6^kGD95O`(=|?+lunr~zXfu$&VqHSaxK8b_Ce6oo3dN7r$l5> z3a7ukI)8h7^_KyJ+B>EUNVQ0K5fPJV1UG9sQ*xn=2}b%Ak5iQxKAY)Zl=5ATbX5jt zp4JyB*$Y}V^=E2j)1zn4NNx76!=c@1CMXgQm#@=m{TM?X(3N<4$LHyLd-c*lWoHKM zR?rRF2LqqilBPVZ;NIl;T=1NUT8U^F=P8}WS|-Xf#&pRp{ga)*lbko(Ees6)rfVpD z;@U~SXW68x-gj+#6U73}!%$-EwhP%5G>_3bA}Q7K8;8oSW=ggLI+5oNCFIU*GM!YQ z`ksPK2DHfeRQ}Wd;aZYMP&%K=H9TE~S9OCuCCKi|KRWh?w)ZMBqML2v$yM$B;80?} zDxz6N{1rt)2mBuuq(1_rC(bXrPN)37>3olq*ml;^wreRf@8wJBeGN--0`Qj}->!Rn z4uRcs=xc3lyRuJ`P+=}A_U?l8`;5)Fh)V^$mhIl&*RXqg77;p#)Ng5~Gpkp{gWa!T zCA;oQ+GZuKwN|3*n+#NBhLI7K`2hT6O37*J{M)Rq45h7D8N_@i;-T2dskjk4rlv@9 z#v^MNbH3LKT+?_dno4|Y;?O%!93b$GyLOw*xVzTcI~&i)yICGj2JN;} zPWQkSa$ULIecim-a^75rX!e4xhH)6|nJ9q6l7#)bf8Ey=MoeG}#88-*clFDM72$9g zr+G%_RSD~I1eXM&*Bhiq+A)g5tH(1;E#c#Cm=CE zPYRr!XsX|BjMGZui@g#1!J$5guBY}_b5miVua8NHm7Iz4idaYoCNX%gmXgc&CtT#m zNi64XU)(Q659v856N#5JhT+2yHZd}pCSX%a$0|@vXZ+X!dNl*Af^1)bOxg*0+jb#) zdLga-62Edc8OZ}pOcYzSbysa}k&`#0-aId-&ump%NjS6HHM1+z|8X3ZEl6c@w2yr7 zl!aPiY?;L^dtKpJwJNQSYC(%|xf&PRps)*#$}p>TrK+7~$3$)&nrW8qwy_cEUDkbj z&;+Hw1&qn%uD-&5!OxebsJI)SA+~F7)2h|YB^sk(`6-C7{biq^3fuJo1;t-W9f=y{` z*1U%#POf=(@$D+L(GpH02RA*srUio?XV>hl8RvXD%lIuL3~Js@nGo0;FfX!{sk$gM z&)IYamktb3+6Jcs>ka&FDe7u9tZaV`ntWT*bX-Np^fK1AJkSf zDFdnyqp+L;0{%oEbJN9A>LMYRqL6Vt62YdoEIZ-T8O<0*{CP#_q@cqh@@GSh75Ua& z$<#S?G$UnLe9O}~*F_g?bf0AlikoaaBbTb;mz2IQxyWC0eq$2&kh!sY78bzzv7%EU zq(BfyU|-hi12r#s-TQQ96Df>26>IZENnolFscCDhdR)qLcKaD))njur+9v+$a6O!I z!&g)XL?pIB4DcRZV)_?WWC+vi5K-m6T6I#hH>FS@mD2c*QmMUCQA-r(F z+n8Dhf>MsbmmA&EG{j7b5Jc!D$w9V`joC9T&VYGa9{(&3!2?vx+$wRl8M@M4%Nt@&KqDV;6EV;ddz+s`vn?wnm^Zj~j&*LW&^?;aQqsw7Vc7MO7~0W0J^Q zb4RmO5@tndUg@+oWjBSoP!QlLNsyga+sv!w9b=iqKImUA#e10nOi15agsSy5FuzvN zF(=qXd@n7eY-3fNV3x%k^rVCeaiNoWD_Lp=bk~61GoV#msbv_8(w|j&6-@*uW0l{d z>p*hP)vr*)W+tF60tw96hftUO({53SERv#YIqONz+MslMchQERFaY(l@(E2zn_Q}> zgE)0hzd50#f8DpR4O;qpt-^V#l-f^IddbFdYJnLPY^}kX4ugdrez}QSmWLx&$^94r z?EQkpChAp_j-8l2cGW)pvLdJeL!%|}1{hx^7?1%Z4A4U#Ucpdgz#XL;8@s=)z_gBF zeHgU9_@%Ud(pJ#!m|9wUWz|jv<|k!xdUAaE;pFwZpD*0}hm7+a>T;B*7@+Vik0Y!G zE0}rMi2~D}{wo$uXl`TkRMvvL>B5tIq zEndEf8}lP)4^6U@1aN5H-z||H6|aQ!potX00*WkA_FMII*! z6tUA?FYMT+Z$+od*fF5fKD%G@th|Fxsl#Ra)LZbnTI9m__RM=_>%=t9+NT;)7==}^@q+@m((T%Bw&{^S5Dmjp@r)%^p z-QT$)ol-?%07?XX=lY= zv#r!1!YrQU!iCq-@Ei}*sp_WS9XRM!x|o6sp=}_PDfpD$pP0skV*M>{RUj+#(vC+1 z3&FC>q!`DkqSq+QyOZCyP!A1IPZ%Ki402ZgcBxJ7E}QfnA7+u|A72cBkTNd81#bn zfhWGM(nGnUz2Nf9J8PI>dqMjPkf*l8xFlXLUTTBzA1!x;Mqz98FHSBJfkzEbibuD+ke3+Cuw2z z46JsA)kxjT>5Noa>0Zsdra2~Lfx5*A;uJH}W1dK9#*-|FfgQUa)Ux-PN;p z7(h<*IHU>YnqaBuQngQz!E#PcFy8L`GO!L`QF$8l;31QMbXkd6F-+nx@meLl;WT#L>*WQ=Pdg)TIJWo5oyIN=-(2k7qPYSTwW{+kW#r2N%(#b$R+y zq2L2%{El|q0y?aN4SSy-85$D?Hm|7$oKt##jiLkj zPq!PVg>h&=ls!Anw3JGPMv?)!G~LiFoC5==Y^p68;xaa-C`wsLTNkx7(DqFo)x)x8 zqdOW;>HWJIOC2@GzHSmaTfSv*pzKsEJ5NV^pe;&Bg6!*iD%Ho`)(0_eR|l(gwClM9 zL4}mmxq}GLR0cv;q-4lO=#OG#)0VE7P`7%;reCARj@&gP@A^w$6cDDGyax4k8wU=p zHYyWMumg)uuDgLOrdcUB@MG3zD73 z;jEMi2;aKFau#mOz+J=syBoAM1TTNCqK)Z`0;JVRgOoasuppd#G&gQgu$p3-7~H3+ zp~_BE20qsgIH)<%s*&*^*blmJh^_}o+c?+9h)#PEMs$>oL+*9i#NoD2v_&9vv198P z+iiC-)K552TUDr7->YJ=QH6Q1_s2kWCBck6EfJOUP#-zZeSclKVxxI5C~LEJwugGM zt*SI;9prLkUG1BSvRu|d0$qY~mWm={wgseoAA6Vxbl0oCQ&<-bB?o8)=;|=0X8&8W zXfKst%P_h>rdJGI3bd*fz0e}1j-G`#KrP0C7^=ehjs|D~pY0;|YZj^OJv5xKW3HU+Mp-QVr5UB9B-G zw<_Ud2v$@*JFw-ph1n@(cLL1WESZ=4sd?p}pm$pngZ7$|Fbx`Dc3K)}G{D-~gW9T7 z%?TZpYU*GyQMCOitsJByCiVSTObN8&ud6eu$tpa+62Z`R<>eo)ecnI}RF7Gv`@_`S zcS}rbjtiXMs%^L`Y+9*n?S#=nX?Uk;PD1Cr$W4<}d7deqx);5*U{J-{DRee~AlR}j zuEt;FKFoC3xmT$ltZ>jSVNkX^2QZeo>}>~qcRjZ3SXa*Ob6{L2JRJ?Zy2pwHI#!ud zH!7FVSb!()LxXKQ8H4woD({fv_WN%LAB`XvE!LhArEPAxYUDNn+8L1ZG>1AxP=5y^ zfz{)I5QzdcQ0-{2TrlSeqbca2ft{H}n{J5*8zCL619HFJf`do~V?joR)T?pB;(tQ@ zOar{!opmJ%ts8VSgjUcsG`kdcsB!kklS@cbQ-X5vcfnIai+m!#2tA8NE9mY$01bko z3@^{KcuFGFwyj3&io7HE-o6nF((Mq*c(!`GaP?^@6|`+<4u)JWh!2!=xOmk!GEU3{ z8BFL)8r~GM(>POkO%r-bb1MIKV0TOT-7T-k45F3WJyAem83%wt)OJ~8iXf;|g&X{% zka{=xYcsL2o}G{f4Q{jR=*f(#3coGw(|PITgv3x20? z4^1%g=fGJ*yJih2G)Nf|`U{xwf1>paw?T&mWwYLU-dVj*)oFn$jHxTiaMC_dh1i07 zvSm=kL4eMo0qR;(&`NyDL!rz)&Kn1HZ>WYGO1D7!6H&RU&b#~iT~BhCti)t4-qARB z8>*vxsMGamr0h#n$L!=xs07nIpMe}%IGffnNa(A3;4qR@+s$eFVEDqIF69pEQYNPF zIK}#X^51U7ISe0Ft-5R!DNh**Q`1=rCT9@5J{A>%PD~$)LC90V6ZVFWRr6DuI55C` z4GY;Mc>8W(H_UaGaquGz;F7$`_;jEx*Y&+O9PqXabI&zhXO6l68{kR>t)P8a^V)+X zxMP)M?IDNk@%@tY3Q0T4!H+#<>O2es6?rVLI#rEObRco6q?vMbwS`W;y_otB z@>ybPF%K$TNiD2>4QqFSD_S|!k?`NdI%Te{jdOq)K3yQF&*yNj@#4{3(OXy9*A=ZD zk<<~F6u|%(q1_MFHy|oPWiqnyYQj!!uYPLo7HLWaLCnT=Vrw>*)OZWqq)q$bBe2!B z(u1ke;-vLz(O*?(tn{VPRzFC}?^W^~&D98`Jgk_w`A+Z9z&3R@#z?(xAZ$>UoeGsq z2TcT2?OjJaO(c=Y*o@_I9^W!M!H$_=$GOU*7)m7bNRmF4GeCxrG=Tbn)U<5>SvjzK z^Ps(wy=^A_|FFb;jn^3w6l*M-t~}1~rd*hX+Ny#202Sy3?R`rIy0&71&&#FGc2B1> zRpY=QGgeN~iCqTDeyJ!x9df$DFd2ZY)U?p%BM|N`e{x{*t4v)j2$06vGgBAK^N+-D*q?VW641gZS&7Q^0j*}yIuy8JD0x->PFWuZB3(4le?JtD1;8mM` z+$ycZTSmgi+`eSaZX#^|hZ9>%#NLG1s{ z>pY<`Q@hS072`=w6EUO4TNtGbV)r;)+fdmzBL&d5vNyqrCyPx zGMwwS4-3_v0imK93p;MNeq89js`@|r6wY9sg<-De-ePh;aRPDCgdsJohEAP~BH>G+ zJR;}EZ;7NR&TP#kw)f*_8x=CBR(l#WP?GcAKsn9EOy!%o#vrJH03Apo?WC3q-hA6dyv65!&C??0zGAjJlA22X%8vSjn z{LQwUs57X|cahq>0u0KaMxa42*bD9l0ag>%-^OwuQplbRZ|-OoITeMUJyVq~q{xs& z=AErUD>w}H1~7*UuJQ^-JW2SS+?Qw1?8Kfu!*E%Z8L65J5b}&%YadLB?oO!CnMi*i zea6UN9rO0zw%kHXhBioSxnXkwCP#o?nnNrUHnE6Ma0x0WYoF!tJPhRQ=fq6fri)C9 zVIaQ`#6)KV2@n*VB%p26m6hJoIKk>BH5E5nP#e`;ElZFbzh~KT?ucKu`LhGgmMCa1 zq@=YRMk(z9#@p8!{Ayp5RDP9r&<&y=wDUDN;uR*oj0ASIECDbKbt;Qx%R#gZh@O+3 zYER*vtu1*I&%hAd9Pz9f4a*tEa6_Ipn}X$}lvs;|=czMPuV$zRw#|qVrS0oE>8;)M zj(cIoy$ju>rgj!gy_oq*Nx^-~}tMww*U z2e4@)dyj7!`a0mSmFXdvaFGiKvhBmO-U2Ny?z#D42j29wdfA`Qj7292ji+wMOcgB( zU0%>=;sV_@KzG-eSv%;tK)MD<+nbs0%9QZ}OIN7I*ubAFQ|V|ZR$x3uE<9BQ=E0Y| z!}4&)xF7=6DYZz!fCheR9rPlJxw4WNYbKy36b#x@RLn-oNhG}UNp5+XWH1<7r(EHkgwaIaNmAj~O@rS&~$pk%T z6rylEI}b*rxIk_0Tc<2Y=<9iWd9FN%R=`6{%vdNzY^P+VjxcjZ*sB>~6%x{QV3=ex zU-7SFPS~INEkv_=-HSY@f)gm`7oLLQx(kN$=(kQSyI4Z{0X?-_({{0vuTEGp!(BYZ zjCdz4Op4?YpVBz3GuXfsO_o}Bk&Orx!@L>LAa z__h`I+M2Ce>u}o+2%Dd@jwIF&S}^S^h8pOl>*$7kFVS42j8aD!GM195&U^LyA~q+~ zOl{ZZ%ao+9dQ&J($@!(VIspG@V!)JbXKjIZYk+IRV7bbt^IE6oHFaYV72PIyWnEs= z-%Fn54os6(QW6N=$Pbj;rZhe{5V-dfA;dOe9%G*}a(!OC>7YqQm=gVcufN>1_q>J1znsJzElkd=7td{=i?SGLe)>m^4Bpns2b3DsBhwjUz$~6;4BhWK+d0YoV+aw$K z4#9HEy~?S&Fifg}L!nMIkC(U4F;028A-8P|PjEZ?E7xkBqc}9-v%pf^DeU{HKOPQM z9WYl)h`0nH;-prX=>+Yb0oqx*H&qGGg`%^|k_W&Hi|~f!XHqW~1B`N7kduUPP0pDk z4qmZYLPI9!lqCaAptXnmZp`uli3+B9aLVpfmAPpNc-Kt2wb;FjJV#rOYeaf9P0n=s zpgVv~{aV9TcIZ$xCs!y@tqJN~EsB9e4+Xy=%vRmPl^|2iYcE@yYeOtG|4B{qF4<6j?7x zI&N~t8e-p}!@t{#hX4$pfuEd16mY4gQOFubycNcdqk8_jU`cJU!I;+ zLGiX?4pqoys{Y(zVE=;Yy$GJ0u5JI)joe;f``{VPCs?~xMO7-t3C$%RX0bYzWnFfj+oTf=V68o&#z&l|$K z-Exxd+g(5B0pFJ58pbZp<`TWjDMBje%_wp}f6-N?!tXy3k9_^DrVno9^n=Sx{w^ZyyaXaqa*-tbqr@ zQ(kGkqrK1~PD52s>5gU0)CC7R>z?#M4Q!Vn@9s;k=vkC?SRy9QZ#%%uJzvKoaG=Y1 z00?uT+g%@Z$}6=Dhh=WwAwV%#uwatx*xVg8Bx>Y&6oVzHVtzf$J2r1{BUyC zsy5pt5Oy61etJVKUkYhXlQ^YP#k6UGF1niV>opX=1IGm0wn6(PwxR6Vt{h0?q&f*z z#|t2oIBfU$PW+@swkHSW6RMF_aH4QWOJO=NhDmH##|(7fm;zc(SHHFPN|!Fuk;i;Q z2K}?EpU$p6{MXwzAI|>k`s(<@$-9e-Gs)x!q!2EyIY|-iwJ^o7_CuRG&|bq1vTqOh z&?XQp)pNhPhjd2MTKDF>gtHsxH3=qF-H@tEf#MlasYA4G+v=W1_ywI~$a63jF`71W3-E%VYrU z8$c^8yKNzu#0BMWSrLM{(ECc0gWQKTCb1W^yABMK|Dr5$2TRbk&YXpA54Q!Z1Kec5 z9NMe(bD#9P5Ir)GG66Qamzg!hJAJ}B{0mN`Zt-uHaJ(4+z zM`S{88KJ7cs4+1t9NOf}SlXa?$!RR|EFKnE?8R+|tK<9w2ad(NGMqorTdHNL&E~0l z1riQTs{&19FgiRMZmRgs*v7oLYPz!NyTjWk;x%w>FF15Sm{8`F@h7hPh4M>*Ov@$cJg1*PDvIA7=Xo0I~@XAX>!ZpI@* z(>!kKqT9AIK+bEA$U-#<0}e?skwsoesvr9*_a-Y+h~Cj%`jj5d(mb1^uN-V`!j5KA z3-&96*{shju_kx2b^7wd`>QwG4tATUT8lf%z^$_XbZp8&Yqv$VFpO)Cm53m+nqi+^ z(>yatNRIUfRo!C3Q*zKcXaikh_Y+!(oJGzYYz+QE=`^dQ$efUGkla<&4_Hj(t`%;r z6-1q@e(W*a^3Wd`lMz z_TiQ*=EJgfEq3fVErs?UddqCau9E`M^VSf?J2?}1vr>Dqr3+Qq?q%ksx)z5czaE5W zxTV~3PNs2sZ1QFJh_vl6HW6X#LW?CtB{P75Xkql|;zaH760jOQG(VPA4IMI(>Pf+amC6+bbdEhyhc2|G{aVP;bb= zXlslRF=2cw1ri0VjsN(nKYkqO&YSa-vy1-OryZl!YPEX1yYT;3t5y1cr`PW8{YQIu ze{X+xZ+E}9_aCixx6|4C57PRy)2ROxQVjl&)^8rGytr@VLmv7lPWeW0$t6!}e?}*0 zHOQZ6T1eL3X|;A2z9b2}s;Ew3n1E;F9UZd2vmsx+KD&C`C&w43G}2fyNf<# z#J=yJHOST3<<+~>_b2k}20V3o-oL&&fB9a10|43qIW?tOMX;e}v#D0JNyJ1d8B-`L z2q`7m6zkHM8r3E+q#;?9sr=JWpr9#*YsG+Sa9EFKI46DGn58XM7+#Dg`)=2<+Ra)ERzxd+H9cY6VKv*!gvZ!-5Xd0Nsw_y(_9N;Y9E{lurW=@ z8Gx=#qDYl^s@hzEbg5>6HOv$kEQbO=N$C%UR+Z2inEb8EqQRsa1lj{KRUug_tvpqb z)Pp*`mgZ`}fV|SBUyE$U1rv5x&4KGP+Eh?BVJU)a#oPEQzhhZL*}3H;;uQbg02PZW zaaTc6X%2D+GBTwp9W%_h9uZN56E(4hlyzbG!#U2L0&I6@?qZ2eJR@6iyp6{rCh-gd zWJdX%%vc7QCR=;0|7#nDlQJ@HyarU{BBv>60AfNjDJdYtc$*Aa%0_V*)5M4F%+ntE zUwE-ews;2r$~L#%Q-|EED87xOLc+?3iysOAyJuM#gKY2S*#Vy6C4gh8pcjrZV!NK z_zP4pO3MuSbQXhhfPIp1nKrD>Td+&LLdbO!x|A;7-6@M=3RN(iW&g~xn+j2Yz(qM@ zphFVV>=lSpy$o{!-kB~=(*~JBxd0>1;{A8%QbSTxiGbJ%YB(p<&;?E1Ql3Nm6Gh&X zQ7yvyV}#~8g%VRLFjdZ0f!MNnmtK0Js#+$5&ReQ99nyo@DtlEmo^Y}_zT0Z*jnxvdW!>VS=C z2uz~k@JO?%l}X9nVSHpy{X~*-#p7#FynF}Dm37858>kfp#Y@dRBrxwx0EbohuHI5u z70vN2o-Lew!?_eW7ICCWqRF->hSNA##6!y+fL$rHlfLg@X=!7Wi@V3HqDwg6)fcUhAi_Dfm%BmPvfv5Phm(Cz@xiNzL$dV zQKZ@uCvSnf>CA4F+^1aF%L4l^8cSJ`pV)XSbo&RxH9o2%Wc%DO_5N{23^oWlii}Z-T#8 zjgKKu#VigBUWjB)rZl@jqn445w8+IoJWiEq8qNqdwiXj5vD&=gIibY8tHEaV3YX+- zvuu3>9~61q4V3h8S{l@*U1ls~K*@)5Zyb9G1^Y(;fk1x0!O|REc08MLWY<#Qb*>*0 zqE0}5lwy?}_QWh-OID;`Aal+6Vr9m;W-e7`G@KA;gNP&{!#PTEAQ$|F7lcX)Z^rV1 zCK~*Rh+cG!T4sqzI zi`o{mB>^gw&XRM}y-aoDYS?dPEJIc+;2R}axO$r_<{MfJ!g6KA8g>q}1rMm}9MC0& z9=YHC>1?t4kj-*v29xW{2oqGTk+;bV*Xo?!l-{rg1`0?iPfrN+Xg(UL8i_1P8tT9C zbjGtB55~{|B`7PY5Xg*P8FZ+CGcq|rvsoh5O^8x}U6!;hSOj}9hJ5C8D3B40VoXe#qY-}nl>&aW0phOjy5jfxpIB}s=Z3`!~hcnm9(&sS+Ni_$5 zaXK$yq86F7GOBKhRy6D#Q!GzQRmdH5tfa;jDzv}`4rPUWW_M#u;Ej+J>;~kDxt5^- zhMv;7rEFy?1R+wwSM(o}^596M*a$i54c$l){?83Z_SI&hx=YnkvW8WKU_&fwO&QBe z70fK{I@&OL)i$oQ5IGsk`O4{`zLBvop2g6S(iMG;b|^oUg;JAW;hxy{z()0$LuX7h z+*nas>QOrHD5M#BCZ};K@dUL;;SBE;4VN%gi8Wi9&>uN;M$TAHQsj2Vac`GH z0%~qbWoO~M98g;Iq)@0p6{FKg$7;7#N9wxUuZS8@23!a%p|Q%oI$-K|xOQIw*=SA@ zMuk+TTr0hL#okuP;T1=EnpDrx?zP<^539t|IehODa}UYfd+i&bNIbH1MzYMY<$zWA zJZn^UnQB?jv2rQhpk|jwrJDi*#4S`22U|*fR=EDOfO?1z3oYgDx$q z+w7b-2oI=A8zUc09OYF?DE@RJUE3>)M|00MZ`cNnBKhx3>N_q*I#Be~)efz46B@Yt zB91)N0ey->?l4QEVrt6&xEP}e3+jLRILkB*HrOd2QhS-eT7^ZIpvGqz|8Qd#`7LU< z$Z8cJwt}%91%Q&_%Q*@Oq^9Bt1YAg|)su2-BzSAut8p2rxDC5{KB}2b!(JO*jksPF zajkRaDgit>v}4V)6R3ErJT7dc?vPJWl$LnpnPrToQqlyx1K9&*M;BfIUDZl~A|UUv zI-NW;U2!{^BSgnRV(c69vJ~kUvm7(lLerI%^yP?ULk^01L)ZGej%udFESRQ)FbD!g z*GPLG@%;;)%a@Gz2KdX6qf#cX?HJTM$wpvmqL{Hvu!x}}NL~ZyNYp?mU6?i(MkN|& z3=w~>ZU|^(?4E@Vi3h@KHpKQ-uIL`h7tjmHwH82x3ZvF?v?;|0BDY(BLxt(Wq@h8M zB3X3Ggd@?2hN-cy{2p)upoRtDPVEQeG!~#bu}nVoGtK0_%*`b*QytDxfd$Q5s#ul` zz$r!;GOVp&4SQhf?h8BBEhukE!%101b)SxN;UDfcfihHN^SDpW`(}qzUz4AYuda?SuFuc<z^UFMQ>rk66g&(k%+io;H;lKt;hA!c<>x_s-$Fq_kccJm5Cl4y+&JSx7{7%cGAA_51$f8n z?*~#(kA3ir4E2#ESj%5o1TZFhO2@tpU%stF0&H*qgzH)(TbzcFL8Dv=Qh-65ZVW-U z08~?G(@>Gn41MO3iyK}oiX<;9y0Dvtp)m!16Q}Ah9lGjT47Qf~#PzJ?vJ;L7JLWvP zixby&ctb?aXEQox4Uh?nT;<9a8FC|H^2mIB0-?>M;3bffdezK-2gn40(#jH zr(hUkI*o2)aFVKW{9K4wZGsMNP~h1AVF*3Ju zi+~YVeJu&8C8IE3kaVmU#Zq)@DRR(2*@RW@8PI2W;qku1-i*xNL~O*;2;Z3SB&so; z(`*WqNDJ3y7wvT}vdntRl;xNT!Ju2FGNLxBcH`k(2^)4nbGZX{PmM5t=U}5FHk!F0 z0-s%+O1@eb1_!?%UtXSFoSy&Jkvu|M0~f;=+|WJw8%*WS_>u@A*DJozP+=6lH9?Ej zJWg1K)saw}YgkPly7vmg(lFryiT*I7;SI|L+5F|#O{=6NG}PQ~u8|4z5>e}OH0gkB zVWnhcv~ljJ2L6X_0@^yLsba#5B*OT4H4mj7IBeVT%|h$Id79IE;|K+%9w!ixpBeVF zH@yUv!$2!-P{a}NMhI;932GOJnKR9dwQF_AY{&|DNcBvhjrZm(i@|U$Y4E1x6~5Y3~`BGX)> z`$Hk8D*m|%;p&8}B%v>oJyguuhHItt_DfGM7TyDkB=FxTerv%Swyn>YHwBI4ruC#Q zaz@7dmSG)mSKw6E5-YEZ>biojJoNqXQ;q*OP2N7`7|Y}TTYIhcemVYscfb1>|Nl8Y zPspEMW!$gG_&Od>h{`ZR*%G=EolIz!Z@3Mu{_yM>c~@l4&m`iZP%eKc?<@u2B7?f# zrAi1Q&RIZSvV`9OCx8^-QJkWeZAb-l8*q9(p>Yo?JOY6&Qui<9uL;Xb^(8TGOsaug zX|P6!Xb=c7mUH~;xdG*EMHAQd`k0(M<-To~BgscFFD>NLosg%aq>!rcj&e)v{OQ%z zyB~Kf^glY#pV+URZ<}(&*T?R7+kxEn3!by1XV1v1j8DBu?g#DtU>CJ6G$P6!)Dw5> zl#`IAC~jb(%n18v&>UrC$TgK`p?-(QA=xWQNKOUDzJU0Fb(VvTjj!YY!6RSAFUUE& z%os3m`Hdi_ET?fIHa4D-Kie)2?Vx@5$Bk$HJvjD~LlMFC>tOLzO~mBFBkqgFM;lP( zdzNs&A;lyfPm;OY@JekZ>3UTFL}1^iZu2se!b_e#yLfkf1}Ncoq_&OHOo94!dtW<) zr(VAY*b3*i$VDs4@Z`x8$Z151@>*wfVuuHbDNupHp1FF^b`KK`K%`VjQ&g3<`>XSu z1!yl}DL-`EC@u&&iCLO&Yzzj2Ar+I2H8M@*I~$#VT(JARp(`~}iMRhK7# zvgMwz5*CGh(k7p8V@YbM6~s;$n5rxr6&dO|_TYkzZa^;hU4s-@!ax-SQJWCdtqY%o z$-|kb(^AKD&CT`BO;?RDb#uD<7$Wy&qsathx13 zTSMMEG}N!uWPXwNUfGm%{T`m;^}OvWyGr0b?;^8J*mnbdAn~@0dxo5sr~jY-Nkqcf zj2yP)zi)~m3-bhtJtMQ*kTer{Y(JEmlIS&_)^G(+4v{3V?jfYips1_CfGj}KWmO&4 zbgG0$6RC~^s~Oox0~_2kq~Q%IX2=hra*$KxT2)v<9;Rcxluuuiw4!#Cy*HsuYOoO2u{C4=8G#sm6ayW8!Q^uPVq zWB&K&`DkhKJuj}B$*d1TWm0Zdb87l-+mzXrk<#E6N^Si6lJ~_L(dUth% z_E*W)bhHz&Jn(#^xv5L9EB7d=#}N`YY#j|bw`!C8BU{}5;S)!?BY(nwVWACtR9DH? zyOt+HAt{^-%dvCi$?>1rT>tW=Z`QZ*snUN{YvxlC|A*;+r`0Og|LES=twis+I;WJO3*eaZIKk5a6h;w zZ{xA**7W2FxkPOk%}m-#Fpl#{F$9Yrs(Pxw`7q%_)mK&bI$va7f1=_?nu@G8Q=T)? zY(Fwx{KDsXWv=J)y{~{pfAg&OOB12T&lmet`TscKLz;X-_Ww}*ulC+vugw2D-ADfa zSw5@le~--LcbafrYlxR@9H&R*U-J1T7~_w~CO+7V(>QMqy23C-@Dq{;|D^yP z4QA$9149`p&8{@bQ!#n!{M8JdzgRfoWK&nLj#J{U)2Way9C%at#U;%rM`Y8P;G#~l z=5g}tul7{bPU8>@j%L}ZJf6qzdmiXtEcJKO$D}&JmCi>U4+h@h0U>x@liAE!#Q$Uv zEq_GXt=8Lk!FagR(x__rD-P}M%?|!7%$9m%*7U{G16SJWl@U|D+ms0P?OoTZCYz1T zX1Lkd1hjvu_8GkAr5o^+ym&#p1Kd%Y4u1ssef~WOY5w3dcMx_ix$gHQ;>ugrZOK1U zw)wY}FIokeylb=&A1kb{hWA-M*6qA?7@6W^d#$PYuFL`LtL@G~&AaWEeEHi~qfm1G z^lJOxd7J{Qvj;xEO_;#xe+x9?H*zm;p-|58OUwD z;Q29x=bJg8Z-nkVsxhCM1buEJ#RhQ$`A&E`Qn1M{zcfiaGMI}^Ou&a3=q5r6TZKqI z%rc(ykSBs%zv`G6A{F-<$nosoA6_Pjsha z&(n?I@P}VEH0H>el7rU4!T0K`+QWdN)G19ad6vJH2V=;G22t6Ex>w|C+;LbU7-j^<&n)-qOn}UwzdIyC9tYzqkI^^Nh{-i{Ovj z|97y2Z;gs1k#B60KyLHOPdbyxpy#6HTG$a9+jQCOF&m^b0zozd3Ceu}F5~7V`4?o` zr#WfKIaSu;jBmH0S=pGT-Mn~2w!V|Q`{Dk>h-Y^+i&%toi2OjFKHWC=Np53K7ClS8 z6XAqSnTD%}RCxoYkhe)~wBGKZF)fOIP;uk{C@r+V>J4Z$5Y0OoRGs->-*9JGb+S~38N9q zAWd1`p7Jvx=QlC?Fq0gBkpF)6e|CarWTy_2hr^5R5MmGBprf6gm2cb806xMBH>a5%k7?!|7t=$RP)^}%d$9P&6iZL=KCyJbVH7IcD|E0 z-=7Fj%HM0Kqwf^qoAE3!pG*>3x%q*$>_JUbok3wZ7M= z+AoHud@3pUCZWk4oeOgt-TUYg1?O&B5(DD$ha!u~f0rneq$=Nel&AKpeSv%Cq{rwh z=T7{)_V><|UB32}(1{1;Z)g9jN2;L=hvab}xj>x`R5>KhU?Y?4iEV0oMB00;4Wjz!<5Q+HuC^>q>HRe}*gqoOR;wD$2b$CgFVg&o?5gipkC>|o zetS4#uHCA>b+9bP@1PXp*KUnBoy4L~zESN~Ya`=D&fwS5ew25^EUVLMS@WUViXe(?ahr*2Bl}CFERF%j<-dm|wpB^;ZkfDxmQv$h z`SCUK$2SO3Hy?F*RR)3ikl%}JFsme>{hw>X*1ggAYeki|zqGIKF_4R_M^Ng}ld;?!^5E9ayCUHvN zxQB%TYg7MbK&bD*p*-PgNbmkv=|46R`+WIdQvYvN^1pW5kM#d@d>-llzlHwaT1&0% zJf#13*Vg|(Dr4@T`ciM1`rj+5{|D=8|Lbkj-lOur`mR1I|BuT5#0x;ssF?Jf45x!t<&o4KH7gj%jYZ5{~znWJ=TBwrg_pgFDRAMCjBHO zlcl7xvb4$v_x&w(64LOoUw*M24VFhnVme>bhf6d?c`MJAwDur&U}0+Xg4sFgWDu3l zXSuqp7@Mz?XbtESH>0vic7}1fBPQyvW~l!0F9rSyxubbF(UFm(cs!*u5>9A3W_k)c zLc)ksp2wqkw0s%}0~s#M0x?-N1FHG!ukdmZ9i`+y)yTj6@=(H$P0Vmtk|%-)7G^B} zQcDpc@z>#Jk-+PuIVMjd%BIjoZ}k1sN1^cJKUMiJ%f&2-^X8|k1OKr6*Q@IP`;YS9 z=lHB%|F7x+Sf?({zo#Jc#YK~qz5k!O`udsziuw8MK8G$nkjSiE3MF?!XBCkq8`2OY z8t9y(W~E&0$6*LqT>hC5srCX#>U&RK^Bb1xm8oP(`L-EUy$=0eFR*Sw&O{D>%e{ug zOD$VOl6CRB;C}v3D|waX?|Bi*!>nr5Z&!`_?T_;Jgw&=*Vw0; z?#H;sKG{||uCdQO_S!D5Pc`sId!0Vj1imyE{dPZ#<-gAs|GD3(^ndN`?mzbb`7EDD z`S14>zP>R-mMLD7#3L5Y!-SCu=Qr57)j7@LpJID{XH)~4OlTSNB3S8t{u8^BQA+Nlk0ndFgq5 z#uGC`evvaO+5Vo5k;a3f=<0%xSDY zg)glR|J!}4_8(|T|5@w5bXt}CuhxF+(f;!}K3_rn*S8-(^VR2n9empKulBcLRqIu( zYVIOBYo&&*w}T&*8TLQT{k7tz{@ZxTJ}NQne~gQ6Bg;oMro%7I|Nh-SRrxPx;!`^R zR+0ZI{a^R?_a5`VKFeoy|KF75SsaRz45AG4GamKpI$n&#j~Op!ClF1l+i7gRORqT3 z-^OA}^Ki2H$wj6jjYlz)Gs;+go{F3%m{i!Xl?R8x@IQ3I%AI@|+^aS1-7j zMI^mh#cQ^y1@g@`{brl(7A)tXBmdnTXP?Dyw!m?wethx-l)E9Lx$XkCp}(k`m;UW9 z(?rMFCwx4P({YfnTb3M=I301c7}0zZfXqkM>=Oi~^7Tjn3nTemp5*=fkAf8}5Hrpb zjY{%&i=8u;O=D>NA&y9Ezt`H(&qx?SoX!fntNDD!jtG{@siSFt*a;Wx1LLD3^4%{d zZ{GFKj$lW1K)PJKb2~`$qxRrgU}rIkSrR!Qb+u%&`S($l+bfsw@dsRin`42$^CF)W zdEoBNOm)`Dr!%wVo6a8M&O5s;HJLbNLR@Bis4t9T4J>z{B2A*wC5W3cLA%<-lMem$v-9ePa=SjXG62O z$a%(s``^3%(_XvV>r~>uT95HxpXH-kQqVLaTkIbXk}gSc$Yweco7-fo?%@eNr8zx~ zGo5M#ZPlLtq1lu?=p0J6(KP>q{L3kPPcC@Gh;aI|&nCbrp0J(_szklvrraKifW zKbdL5xBAXicMrOLHAg~ko%#EP!)4ara`WDfeo-BrJ_=IPS!KO-eC)KUQ$RFu4`Q8T zFtcBGU!S{BUaTSuxugDz#A)8!CBOdqe8&T1;lr4hYF4Llw#pdgcL06`kQrn3{gSk# zuN(5YjcJ~je|+)FFKfe7yP-D22j@+;)D>sN`HP!)c3Na{I(|P(IQ2k0FaNm0{L8P% zc~`(Jns@n5m2r5jOW~l4{JJlx{felPjs)m3h$Scv$=9Fnc#tY{uBXD946jUqy@e^h z2^>qYHSNLx7n>v;E1oq>9vLIMk3}A*<36yylZ3`ow-=rrQ7)E_8EKN!c}k~o2$5AT zD}~)@kXgo>s1!%=$m#L*F>s!j$0vV&e@SY&5<`1rN{&C>yq16nRE-HlD**8HqCQVOc?ERY4%rww(e zLF&cS$L@w4{Uw{w+n8rBGG3(76(7dw^BwO8`#en2d_QAZ$kLpS*{h7P{)~p~`HugH zJCt6`i;jeE{;J=muZyzn?7ZR`VKkf&kFITj-EF&}I)UPmTK1s3Q;pRp{TgE{|)&42QgiA}VX{IChN=o|HK zKZ*vAqQSQ!8h{EQSyLnkjnH105J!Qt%77mkS#R-s;F2z4C_rYLBhhTvaSnK1UcVPG zyXQOb?}yi;u0O$By52%+sf2q!CM>+UWLeCk7u}Y;&Gv8Sc+u;19=*t!z@8ZrQ2Y6g z{lz}58cM%tw_2^%^Bw(XX%7&Dy9ssF3Y+i-_;4^xcz9!ISDqEjn%wHN|B?yTx5n~p z4P)$>^RdJw)WSEnKl;_?ZRfYW-NbI>o5qq)euPJe&=!J4=5Ob+f62A`rr1a6$DhUT zCH?$f;ta%Z7BngEdFA^musUj$XLC!e9DhuEH#Ll9RJNt+C$+dU`KV~_Z(tDyOMTC?>>lyE(@cStIelFqn zD`M)ul{c&+AOCJ*?#BwQ%iLu(lU;*ct$ooDgmM-IgqvcQK$c*F7w zI$en~`OxjV0Nw%RY$nYo>qDMlnC9~xc)dJZFhh%voiD!a%vgc@ZC6leggU#Q#o4^i zLY_wc6|8?`{bei|$`uqA47CcUS*TU7%X#RXXo_z0w!0S75nW3cyH*GOSO7E&C-E(F zwCLlX`=s9`KXq49pg&@@%VUrfD{OkxTtQH78fGfGze!oH1GI|b{GdkY!25Wm&;hcZ zUg+`}DTH_033Cx5G;GYCVl;1;ubR7O|f10UgK>9iEWm@6`XDf3>4ke_Cxo!Q~0p4d(q~2lBsOZIGL(=(FsWW&Jo}XK9$tq2uJA+5Fr` z+GP*gDJ*?z5{^E_{e7y?&_#a~YF9f^H zn25(|oQ`2Xyl?bvb)NApt28e%R<*3w|747$HE*qCuw7{GUCs8Yu6e?X=vBtk{FF~= z{QtA}t?g~&#=_tAE3mW@+u2C6olUlNsl|91I3CgTY`h6EcOXNV=tgBJ8%eD=HZA>CgD~)b(RS0xj=t z{mPf?c6%E8S8j@b#IYGOty2m4nXpqc@qIPY2E7lWzPttas7DkM_0`TOvPeI=7x)2@eBU5M z%|?NHolfM#vAp@Wid*|OhZOnZv6xfyzk6gxz3&A65q$QCFXunrm-A?=4dHL5gb~-u zwq=hcbu-S~a;|W!zl8k$2SJwWozO(PtG1hWckPJ|fS&sF=W&jH@)T!=ulOoqXPdPY zx`Lof6Ai8!Fa^K}Z?Ev;Dba@cqZ>zm7(;&;Lyvo~wQMO-V!iDz|5#8~SWq5qF?omnSV_LsN`erwE+)Ts z#?61Pf7bl3K24d9(y*2k<{wR_gb{N<8P>#W8> zMbS9C?g2Fz*8p}(GtYPae$;Eww0SC6nU`Kks<$%ilaU|Z*uX;4_xnIp()FE6z1r*E zPBm&Qw5HeSu9H{Z*EXo!TEOe{c$Qb{!PcWX%-k9g>-2ue@}=846Iti)F>P(7QWb&e zdY*l*XpM@pTG`6l5K<^>trTqFzZZo+R>uE>|E%l(#2>|Oip$CPQTkZ8{{Qf1_s3Ue z{m0j@cK@jV^G*J3dc6y)5uOe-M z=zmXmvsSlPJ3T#W9@O3+HL6bUq}Fo^g@dzpyK&O(eQLBj&DKe^xLYXv(ypDJHrl<; zLA!a{?R7t&HhS&T<9g$L)uI1&tZnM8gP$Ai-a+g5wAE=;9r~O7bP9z||WyAq|o`?f$uvXYwbq8 z_tRm!_2I02L|ysVEC25>jz(v3;Fv8tjf1mxv-`RCQ>%IMbK|r4B4Ndm%Ksbr;m`Os z|I6cAr`u>-zZFH2$UybaPg?axuUQx0fWG&=OgBH(KGi<9jvI0)?v?v7n&2$RdaYY~ zU+Xl0@S?qDH)X(U-%#@RPFtPshjyb=b)<2W|6#q|{M2Y?yYpiI3di~1sna7qnpvhu ze$i>PPin`Oq0JH;hwfy-@OVAa?63_27!kcChVj?M_bb{gHY(;P_bJfwru*>u^+PMQa`PWP~RlsmZ4 zAGk?M46ijX!hjIsP^$Q=-6#Hvle9-C&9zV?Fv z^;x3X{w>nRUf5Wbc6_)B6Siyb-#5F*fA7@}Ks|!O(3mK8`+Yw>{;f=rqbfw_BP)>R z>0%K66i9x^iVP(Mz>sv^>wNBX8^^uRwPPX-vT%o^v>}?b2kMZh&hjPEe)28Vmh?SU zk7kSJNvB&oI%>38H^R5UdMa$74?)y-12W4=>V|1Tr_m2lf0jTABoss=e^7wof@bS< zYbW*EQR}1uY5GFX4WbY`N(;t)K%%Nc2%)~^ilpjr#Q}&kZc-w;!IqzpEyK1bp_-tA z)ICC<^1mGUQe%m#qy1Ex@rkTD{4b|a;NW^kwfBvqs7nB)lY}V2mHk`E#QU7elU&#r%-Qpy6xusvu?9>!W-57 zB#r(4jBMDP!msU`fctCg-l~H+Xq|Lx%@eYw@b$+Wj?x6@3`uDci($1J^`_QAj6FXg zGg@3V`bm^xy4T~HqnqZlKX$~UdAoghC&euAbEu~4EA6Lk~Ec#sA}kI+QYgBatm zK)&iRSk;~KYr-qUTdld@J)BpSD}ar(NK^UcK4w9kmW>gr-$T z+y2)1mrjAmyrsDmA<|XP^Wz+YTeL&oWbh^L4a8k{NEzf#;yuoCgH>l8lKj+%9U~=4f!wRqM|NPLYQBoD2CCI=Iow?Ii5i@nYyXp4{$D+FX5KbN@$9G z1j$wQE0yc(Yig;M2hju^;lT`t16=X^WE!})6|ie7G~4#~ZT#EJkHKx9Y|v-wqmugL z*iB>qW?M{OmM--OMtJRpFf>jSV=Qc%vY*R|Vt@5i+fkdi?8n;FZ6Kz`5+tvrAP=Xm zA0&nL>2a^#crSqv+qhS69M;Z`x>cug<;In0npS{}6{$b?cbqZgdLTg&!Kpgt?2V%d zrpx@;y`s|w>Er!0(bKESP8gB%`{>?IG4gQYlYNY}L#_iPP!QEzh?4$_Lvu{G34F}D z>CH}vL0xfPv{2kpwGjV$Yn%GPXbN3Xk+vN)ZrsT?snL3O7-`lr37IHaT zt^1`tv!a&00bLXSnk2y_@^ICohQeZOvg_w#62((i;`Q*7tM+RgVdxhYb~1~DGGe&xqjL+!7fxYbXg5AIe*L!>vGNO5qRKCxP^QW+Oc7ev z-Qx*O`GqDU%Q}95RFq#ZAz09dV0*!1|FCe;mm*=KM!Vf=_w4$<8D6=8pIvT<7zn#j zHZLyl+c<@dlUBE}kD6cz`@BXZ*s|1Afn_Cd`#310jvo%_Iy1pZ;*KzyxVL1}Ox=1! zp3mAx1kz;~U5D8PM|e*%(j^=x`T*ny%X~@Scqr0^V-7`nZ?vH(A1rfiIfZ7YC*7$w z5Y!2*m;{PJq3^I(vzINKOleE3XEQGXC2Wx8=;oQR7VylLT@EvA+Zvlr&AeR>&oFnj zO}WkH<&&j@t2er}=21sC|VANS$oK_G*N!UJE<~Ajpduw-6Ol^JY!kzo+N{-h& z6cpL8p`Z(8?x!nxZ4L3v1tw&41068uBX<@2Hp2-x4+KkarT(K~dO;20gdp*w=z zIeLdWI3*e}kvnvWHHSe7C!gn%iydVd#8N)wB%)TQeDRNSxAd=C>3?=gf4;ceee-fo ztP0(xkd3)LA<-*rAnrjFEX)1pen@CU-C7*h0uMLmVK?NS_u`%It(;SqBnqO8+A}&M zti(M!N8>0-!GJF%=mJF{aznwCVd^Y(gqN6GAF6fD?4a z*o0mzFdz!iY&wcv4->UopK!G{19vpe~aS)O5`ynD*Jk+11T$#2`jtC^O z_P8kuG4{Y&o2#L1;!keuyZrz|tk>2yizJ(c2~JVt@UTH;gAGjC z`d{MPZqxzeuv*+zUf|&ngFGJ7tw>dQ7vp3Wq*Z5!ZYwuX5RE#15Zoe%f+BN4=77?A zADtJKelF-&M+ZG(uO1+JMMrJnpm(U^;j2nG3j(zF?)fhI`c?E={Hr4zHRs4F(&rAU zR*|#gpbOJQqoQ{R771ekf}ssF@-IWPs#Dxj+ip9H95z>OU;*)^c&CDxOMlaM)Z>)- zBcBYQ6?t9^8iiW-%WfY?;#vIlW&p~F}jYGkq6F(W0he0&*lW3R%oA%vgTuJ?D zZx97hob=q#>(S+aXsg2DAi!>1b-v*9orzNzyodXqU-0>>KfQhZ_RXKkvo`kXvmk)S zyKi6Zl4qYW(YgH5E7t6N?2pE&{C@Y(KLXGw@MzNs(KEdu8jW!L0jI~2huiM8;CjlW z$8geG+;yZYt4N-42Uhdsuq9n8JouQlW)6~iU90M9C-aK%WOzQyzch5XSy+4R?^V(!y3yBpey%tPP>7$@n$*o~{jEiiPP;+^ry{?7jH+(BQFdwq#ES>QcNquF$d*^k-t)c8N18|oG53>#h4n_S_hf;%>UC9&=kQ*csO59sG>Pm?Ll+Xa1K1Kl^rlmCY zCll;}5sS2`1rJ5|P(JTfYEu&vB@*<-DVyN~Do;82sr)7nxozxv%c4K!S=_=*= zS?SeILdBcR>+XJWkIw2`m6+~0#;#}ioP0NN{m}e?1+{l?r}j=?%RZAa4g%5V1|W)> zbv|%+3L)D>m!}a6OG;GKNc(0z2R8~dh(@Zmq;uwI&AI6Fcl0ZwfHfQ1{OS&L^t`w$ zCBy_nXWw&E%&TBuNAYEXJbyUEF%Hw7o-_3Qv%~JQZu!}(lmDTThJyqzZ@;s%!`cshN=?;B>V4`3BG(o3v1bNMxb+mQbtZ&oLI;YK(-f6R5Eq=#7{n-x!>bs%f`Su=? z@&zxE9}a>UgrN|{6J3MHIW5E2Y3xpsb4ehS{)~zoxWh@e`}&{gxyqbi))Ipb?ZAlB zXljm_Y5l=q7RO|tkW`CX(*%|95Pe0X7*A2@dO%3edz(8@*}*%md7eT#n5DUJDE$253~cY7s=m^Kbe-XTwd4_YVP#;@J1 zWV095;?@9s_(C`-PiD6O2H>OChu-_f$J(c6s|_(n$Bk|++l>W~Q{2i}z|>0H*50s# zq$2x{ZCf;TLut@yv_CZuM0hu-5yxoLZPyNHTzTfr$9AJuuR3J;lkbd1j9R^;=1D`E zCxW=#Affh1ahF>mSpX0aoClzE=u{jD=HyU?vocAAw%4P%* zg+?I{B8XT(emL|)KgB4BqG=hOC77!BG(sNk&qlChcB4Z-u?*B>2^K)Bs)Dn|)05eLN$B zM^`xRM+rug$fFS)@Q0t!(Ubjl5MARqgNReS`_yQ^Z*>|ywqN+JiiEoyU7+XBb6Y{B z>BUW;_w49c*Q6n}Phh)6V14~+yy-NLjy`807&KDo#P!?;I*2h{PWX(FH5YjihM57E zxaRBtYf(xqk%!*=u(KLa_SI1| z;=7f^FtNXcWYdsUEDn12&fq}~lp59}Ww~ebl@WoH#xI~0=u}Crv6-5VrUY!OB65m< zTetwOI5OISkax~bY+QB1l6(PHS+=TkaLIx6%&c4cnwld*Ix#p;+gfoETF^v@kVaBE zL9i*}(qmggGmX%@=Wp_qG56R#J#iA!&9@p;BaS1<%642esNi;SGN3Sl)41_d^iW_h zC<17gz`b_ka4nF()Y>Om^k7@D4w#cxw~+U?)nyn}Xg__;HYyKDt4Jvv$y@QWq?5bqK#YiUJh)_)K_3wyV9NYtF-IeXyX9lbe6 z#P&rzZha7Sv@dj1Hn8n#Js; zCK<*Q5*YK175|~;@oB5wt(|nys-G0^I5H-mu)aALEp51JkN5az?ORSpBa~jwjc%*n z0vj8-bWXtb9k{_@77*Jnz~LwzBbLwE4T4*gMAX5MfXjw7$Dv2I4VTCb{7Y<^oTXB< z)<4^z?2N@sx@vI`ek!UtSKf>48^x~)_X6Pv`x=dLFip@bK`~G1&z;=Yc+*2+6i

  • YQW28!Xc#*BF)PT4ExW==$at*624KxM6NOV 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

    Z~a^S{602UiUBN4M?A!#IB(Ynkua@-~Zo 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

    NEmbY z*|j}u8A0I>+jK8q83f;SJ$XzL>jXcM(AJu#RKW&1R+Wq$$Z{mw#^Q!~B=rqcuDe(@|2e1mBMF7))~Bp2=Yg!i zYrfP_-ax01<@ly~4A2{t)(vlDE+dpg>`voWA9?|w|97QZZ9r{M0#Qe)dg@P-sdA-| z7dj<&Bon^`Q{SS9+m@mu^*VO~c?%0^_o4~W&kpXNit=v?Q2BA<5aU;Ym*e&*N1%8y zlKU~@?C5DIfr8nBOyS%Z(Lv3kN0Kn|Hhb@4f-vax8mUNdAM$$(c!j7S$#8U(8N(QT zF+d~qLW+C0C;^NlG`nF3?)$QZ$nGiQxhg)_No$;>vXsBjEp;%@J@G4bxPTWbGVXt2 zDE)eBExR?tGeKZFo5JP!Q=XWIi-#Hwu}w1&u~@W3Lbh#ZQuTq$h$Qg%Y@5cN0KPa2hL{~0*eHz@** zFl38md|Y+~O?vVaMBvz%h5Vh?)uTe!3#;m0%^PiPwyQ1|24f513g15Jbw@LtRj3er zVICC7WMqIVky*S#80N186yz-wNMF*1#Fyp?1ed!u!HKAZ)B;91?hI{b)6859X10^6 zB`6;FbI>WnQu{q24f!Skw2BK55Xpj+Lyh0OR{Pruk-*H$ojXFL6{28ysvNlF;eAxY z#1RImm00gj7>hI+Tsx-tJ;6gu3&vv%z(4*aHxSGfL}G9hqY-uXC8k)6zC5fSjPcXQ z?Pey7Tl;rRbl29L5~KT_)|eTs*@NiAEn`o>-Sp{rp{RJ!oE(oB8=VH464Yy+Sum#IO7)PZzn#xBY z)FO?aTPNRPR1%ia&vp>nKOT{WYL3Y5?vl@VJdCZ8I(F4qD!=Auy0BIJ7w5&67Ke4B z|8|AF{+T!`YUuQV@58@SR^WOOqRTM(-HK5JD?3|=%5W#On5?0(!#IEsQAmS@Fk&eko;#O>?Y9EL31pPLO| zSShFhz^L;R&$6;nOtdRCP+&=>yU?=qB)U+K_vgyZ$iHo@I^JP3sGc~k66vhWg}C`9 zuUKFJWNN}HEDDGK7spSsynyHF`zt!YhGaGqk4Evc3`K1AEOo?_6IXaGmS5BVaURbt za9`h-)9`nIr#9^(ly-Vb%N0D{xMjwiMTYTE0-vRhGw!OZMR4UwU zev@ZlhzYOYY1ZYH%6%yJMO@1~P1zwAzUQG&CTPgge^58*0(9Xat_;bA9;Ur*$Y?5pJK0^~LJq(KoG#1`Q!cluVuy zV12Rzh`h?0vibZjW|WD4`EGe7JxfJR$a|^N2p+*GvU{*c;jWtC|GHDwp-KR2HA_(l zR#Q}(P*gNypHAmjXh;#tGJl?&EK7jw8C^&R+#>t7lxC`E!y3D_6Z}<< z^%`F?sMWFHOeTE!Zy%gB?@SILyZiJ3Kp(ZB82zr^Jt%WD^DL{f9Hmi#t)mKl$J<<1f!FP2Bs5*>$ z=^5W%u%j^au>=#;(D#(DDJ!;~kGd0WF&cuI7?S(=R<$=%GT3T`NI`r zxuemHVz*Qg8ne>m3C{#JKIW##?`*T^k4E>YiGMP*pa0CA9J~2GpET)QIv`fZ=I7CC zuYDRqt0o}h8Q^}KzW}H{el3J{{5=a3Q}7$Fml=i749?Stop<7hC=ty4mD4(3R~PLo z|9x`nG;PG@1Lzr4<%OVQQ;xs#Xd6;GG}t`d@+^{?G7uNp(%iS_A>akkQ}Z4mak*&s zX)K2h@O>hx0c@>#%#D42#COdkXD|UyXedfJbNbT@^)+M*Hw-?6pcPFuEZ3B-sk`8gqP0_YrM-L-vNFE4?@cl+sZBa ziyK@chKaFnhU4#HwKp17uACs?2r&f}6jsdt9%j7Ov#onJlp{g#FVL`qfcD&|)B!5? z;RvF^L4IagP)JY~Rd%o`r#3O+XB3Qtf2kw86~vH0Mp=2-Vvzv3iN&3nXAgpxQw4#N zzX>OJ0hlrcK1{WRIGH8z;xHcjJI}OePvN;-uvc6sv)iUa719t0c^Wh z-8E+cH%?!A-=`naSu{38<;_hqo$D}}B@ z#R8=P!!iY4?m)NT@d?=tE2Mq){@B8K0T7w{dT#vg6CCb60TiEW;+2}}Vsa7kxMd|2 z=QhvABYk}hl1sVjK_b{RYYUq~Xv}3M;v9Vla!ez2kd4Nv6Np9QOS6B%a4a!;_@_DhMU^( z2+dH!nGeP{pznbJxssayC^E&bh`bBCdRUe&9dhODygR>y6@Wec0-8UbB9t{vNjj{2gk_%D0ZztRvXQL1;Nv&mL6Hm$Ran&)AB1pevfaD}gf^ zu*+Jt_hvdl4QjLPwaJ3ZZ_FxW3mvj;?T+N`c3jhk`fIc&v-KO)oOeSb32aTWp`D%3TLDqMi^Bd}J zlo8{TcDcYyBS%b6G2!iIIns(i1uZnY<+H2rH7Pog-|$DYW*r^sdy!Aw9mH?NYT8#W zFQ_d>uqPLrcWW^Mhd(JZl&73B=XfbP(4B%|%PqRM+n!XL8akqL@%o_NJ*Zg=+D4Vl zR#Y3AAb_D)!|wksH;C{_t7d%bT-#^>Jy}zNA1hU6S)XR4a%G?7n;kA6y=`!b;%~nQ zdVRHRADde=+kqUilmQ+WC@5wZao@}@+NaXphyQV`F*m1lWxF4UT)$MtXGyXP!dFtV zl;Li)d68fHtI$+o8%}9xpe4$PGcS*LAu3U^5>@`W>$>lIkC*fOiv56kHMZ$+;?o@k zoM`NQ&D@vJa{_vjxJ}5B7zdGrh+|==Ue8 zzX3P2mMj{Wn3HBJ9QYk1jNaoPRzp>mB*))r@B>G^R6$P4Y1a6T4_m_csr<`Kbpm$& z*`dFDN#%GAhKdewk~!ZGCr3#H`)j&t4?`7zdM2$SRbZ85jwD%Nb5$hWQCC}W5!E)&vNylC%@ybmvTT^Ip3et!!moql z$Z3Ershr3MH5gmjM+2zi-WSi+o=Kj;U`xO zVcm{!8$AxQ1@}9IDEr6v`=|Puim=6J^gTp+gP&AdY98@xZZK`S&b`ndXJKz3+x&mT zlvq{u>I#;UBD}HNJ{n|BpX2E$?b4qsBT474HS_^youl{#t>e&*h5+C8SDGFG-_GXw zv@f+gjB8p$;_X-!{rl6?T+5gM_;>DM8$s?_gXmb}wBv)sVBJBZh|IH^*ZO_MCbIy1 zoPM-E8x+}I@AMB$HkYQ@M?~kNESBhC+($$qrizUS8OJb(94lUqajMV9aN1kn2TNBJ zh~L{Z2cL?*QwPz;(Jl*_$E`Bxe5jky@OUUqYPgQ;Y`w)e>laaoc4oz#+HtXt?^EFD ztuB{8YCBREBVT$RYE0fw@P!pJCj+ZKaT1_GWTJl9urKpVj!r_PU|BmH!gx-sIw+bF zlckSZ^(y1e{?R~(xxtKx?XjrmhNg=~`7;DU#rrqL!(H=nc4Cj z%H9a~K*A)~RYdFbKch+);sUNpjrpaq_IzY3S^77cplE!_lc=739e|k#z(d}5kNc3z zpojU&;gaL}+>y-2o9S)q-O`l%)0vsdL2tZoXmESHs|w5P(7pkm+|t)7^1TidnX*(veQ683kU-49 z0M`hhK%!kvY_0^IjqrnP3X)$lq}Z6Ke?nipxkY0jrS2?AWyqjog`)|bxP2x?|JJ0% z=0aBT_4Di0oS|s4O%+e1^NRu2&KCaopkgLOBB`f zN{rWUN#Fza&Uf{r=LcR^=amdkt*jiLTgwi?yD?M8W!qdMz#u5!zMq*c@U(vBlj$v0 zJ`H-$)YUvhy2$jOUfvi!@n)ZHENLd>LwF_Y0PX6J#yd?{Ocf zi|985<>7#WY7RU~Fr(lc8b@YnygBFv07xr?-h=+j_Qn+SPqO>3ev1BC*8w15pirTmn&#$ z-*q4E>HMRZ7K2ln3qSb@ar3QG#cCH{|3ammLv%J}SFe%o8RJzJq!E}7`I%>omqYY}r7SEJZYW|!5PXee$%>e3y|$*ty~>X#Ae;4Bk+u}I%yJ3mxj%3mUypTKH< z4jjG_#E4*jaUggCuWS84GxU&z_ptJR+qOS&n8p*LXiFQWB;8`eyl=kr1_cNjugYgIuyzX6!LKi3* zZ9DDCbWO0@1Bx3Z%vhF|zErGAeNIs|=Wd0<5K4$ZW9%nchcfPP!ND-*F#?PTP+4lI z(8X3uK2?W>zIjPTLMy0@6kQ4;q8#XZZ9(n${f2Lf-MD4}`APM}L#IWFJFBzN$A3Sj zG?x z{DSYsYublF!NjvAPixFu1uLbhxX6~PnjspzeA$}pa{Zs@jN|;rgqe2Xl&?v=zebYA zw+%EK`4EXCTMdVGX2A+p}`X=PjM zTATgAmFz9zUFdZm`k<|3M{PvcQ{R^jFIM%9GjX?3MvmW=ZD*Z{WAuC&*qG ziu_5he}7ngWv9%)*sGY=o!*CPS&Y8yqCoPD?ry*FSVyAZazD)!-b+&TYkLt_Exz`@s$C_7;kJcOKw1Dn72BYWtH01RQgI>oJikThTDNUln3$wX-e$~9njjG z!%Fuhnm(kdQn)^@aJj;)Cy;P-nGWmAUOWu{-{+xGBs)YS!6itqm-?8&hDlu6*#(Kq zU|Jc!IOAkbE>oR2VL`f=4nB@X`kxQMid67VV6^TuJ#6p>6t8Ud^tDzYYc>5Q*5?-4 zjmPQv!IFW@$JQwNB{Ae9IKuZS!iMh~umnanVg<+TKmUkFPN^53YRnb-M|;4*0X_D1 zTK+^5na(yqkwHxI_!esr@0E?{C^%@ZGLwU%Sl7<0gydLU`Kn!BX9WGA%FM8{Oa)?j{x1eGa$*Gdl_({^>e9=k>$9=(d> zlZs%0PG{yH?oH`&9JH2&X6$83ZD1EPf~bvmM=7^-@Zi(}!I9TZZ9XL?%sBCR4USuj z&^*638sYKvINk2ly-hmWFn_~b&N*kz4ozByLUXpXc#Cg#yRJ<@k%CSH(lu}_tBZs! zq|uOs15NRJ%$uGMO()mYOGlQs4ihzU6Rv2JxG;QS5y~~J4z`hy&$&!oUFh2IQ5AlN zEWs+3z>FYY_ga^T4xJX|&PqqlPK(-$tG|PrT)Mg7=(+zk-0)~2 z~PbAMo)%rQB<7UyIqj`(yd`jFhLd(jc)1J+tO{=T!4K6MM zxQ%LEle#hah~86E)THzf<@a7hK_QUFp#Uz^rB80Xbxqs_0sXGpt>1 z8W=^RD!yJFNikpSo*uozmMEUKbOzR+_-HbwWq9MG3bxBtCW}c{5z)r}nL8JEzYF8a z`OXZFF94-hvMJ_8OVb;MrjY^ODI0XRWa#gea2<5MYQGFNEHsnU%~>C{R_UqRW~xKQbn z`ZBOnOxIFJUVI$Y&-Tp^!x*3Y6*QZ_vB?&oko$f@TTvbcgPS&0`*89Jp+%R^p-|~w zorc-M4t%SU#9jm0UED?90ZwEs+iR_jzllPWj8GXf>54Fs(meYDX6QDB-=V7y>gZ?e z&pQj8*H)5gS~(fz$aVXYt=TElvoaPy9UaRb?3iRoNrOKVofdKVEE*>6p{+}$7aNZ_ zgNw>EGAqMWGSPXkDGT0qQFuX`G}I6mq9G!VydldN_%CU0+l%x5h2n+``4u;yT4R9Z z3sZm%yn!Vz?SBPwLEw$3`y7t@%sMA{2}irOrJR3G5*GxOY}*C)cbFVAmBTnhuj(@; z0_a&yHz5KD_Y0hjdB-RbBHN2nlrnl3OGjfe3u_msAIoYiw~Hd^t&M$~lo_`6+*`iu zG@W|kIN!XAtC)1-YmvYav@xxTTXtepf-u=hfS*buX8?`P&KLc%$T*ovS1|?q%+m5# zT*wUVfu;a$ekAguh+q=-rX5B|{DDrlC`yI$TH$*tpSeF_1wMkk*)Z9ChAakIyJ~`e6ShG zJpwS@wL@*=$CoT4jhsg^AtuY*`v#PyapGeX2% zn`z6$4<%}xJ0~x%ZI7nx}wal_c9U*ulZ6IygaFi>{%Gp zES@Vq)*$I?F$`_AFw<;JBiBYJ2Hp)LenIoDe6(C)H~rEF^oOs)cV$1T$bU0 z_abqv?1f)uS^!c-*5p=wD`*D@mS)2<^|p-e-Me61Ox*{vpdoP%0N&=u6fXwj)ec9` zLlg=)wm%FNCW%Qo>Ix91k0?yrGA>+52}vX=cWys#&Ufnnk=@Bh07pM9Co2~tyZz;3 z?^NI8>coim@%%opx4Ub_@bPHpYxnhjwp&?B%@&t?Djx5qi1hW!9ilGjQg-oPj>tr7 zZ0&Delxlr$NNIVwbf#E##qi@@vLUb00(JjTAw*8WNdroK5~K2g2OdWULa22F_p~wM zdjfzxH(wAI?yaI!D#Jvd28@lrFT9k35=<(CQXWmnDwF~Swufm7PD z6`T72Yud|bgArt$J6{y{sAisTqKf2iKlEWF9ihc%FX=b#4x1a$YNd_NilT2vkhjcl zn^-vn2+UAcD{^822;DuOQa5@hcA^xk;SZzC3wqA38ZS3a1>(5TuM2)yEnynkPYRv{Mb~zR6r3vQ7c6?EnhwOUP$6YL0 zUp4OZSDBHVC;5=(WB+qti!6R5+^e z&u-FC6{&Nf{{IhVAuX{4JAitB7#bEWi}`8YwSepe&lzCMc|U!1#*)(3QWD%PDdInW zJa)Ug_05+)-9P<%>1EgHYR53i-U(Ydh5w$TvUy%FvSICfjNPNArStu^n%k|8XzG{F z@@SfuBqp5ms-}U=Bt~hye)WQxpXvI@yY47n?2AdcydSO9>eaVRW`0n(B;orwJ@wT8 zn4g`naimVG^lWOoeYLPiD00SN8O7kIv4}pZuoO_TLLU8rw^=`kt#^2s%(`POqBo$L zXi|;75%;3eL}=plx5-v=KH!3l>^^9#BAPOz!8Aij)iw`)9LTgzMpD-v)>@g2-?dgF zyi@I5Ht9XgbWef;y!TgZakWsd*>2yrT@^~d1)aK?6u4p|K^1jads6!z>i9LH0!}xE z5SVQ;6qMe@lJ$1t(Rv9+UAL*kan;?V@^_syI(5Y6Ys}Xf-;0ZrJy?6~c2?tg@0=~dT%bmEObJ%_6SkH~}vNoDz0M+E@|r}W?E!upG!y|*RpA|@QzDg2a~ zdl$c>$9K)+g?t1all+!BQ0y>GI#jh-3#{1jeMtH3F+3M@>7#vQf_DTZ z?SDi$Bsl`p`=f^}ScW|nm|y;cs{Bs`VE4>;DhBs>m%Y?}^Oy7bPx#xb;a<{``^bRB zH&e8tJLM@1UC3`?B{LDQ^uv%8lCH2P{Wp5YO?P66l6JG~p1^^n!5HU0;?&7wLV3`y`yi5zqqh>zQix{3 zf~^<)cUvoy%;sTyg38PziK?3^T1>>2tK=` z5wTVW@acgt`RBD~FhRb3S6}aYSjHwp`B-_sEv)_sha{uPc>-->t&072vIbdX_dA== z7xh_F0ZyA@p4|CmzfGjOq*0km`v>9^E{ojx?;?J*)NGbPm~7zMD&lK6UP>NbLjZ*E z7|ZoN$S1j}Hh7pO1u;3E(7?};`vf~X__>d$h7ZFl*8N<@!;Fn$kD3m@jm2 zb*jUXBpCw@kzG5}bTJ6ekRxW`k*3vCl{i(*c%3}lzvxush7w|1Lw8qW`}3u(hFx=R z|2pVVMD*GQu3&~j1|HlG-0#qb1hNnGt;u1YkoUmF&~M}6pK&hqxX2Ss^>@!wKO3RB zGpDTPA9cSx2MDV%usFRR{gDf^dQ8{Y^Zp%bL?=fa_v~Ey-)o;eDSjLnr&pO;pV`Cd!9y= zi`iiHxl2b_-@X_imOQHB|M)Nk9KN{K`gWhbEDS9-yWr$ixmb^>FWi|ng#USddWJMJ zn_}?KD{1L13g4WOjYXKDf0B!tc}9q}tV!U$l-v3onw~0lY&hGr`UVmIzlRQmD{X6D zSyI)XpCZr;^ylUUrTi8g8;uZD&|gK<+!F2!^9csNcGX!4sk_^uXVE@-)u9C-jBykL721Q2UuSH62^)!Y8U?p+{bW3#-#*xeqUD7S$FN;1}!E zC?rabfwYtGWMER(Xx`X-4Yc_fqGMzb%TWu~V;E>fUS}LUei;+}sqD`C!PnzBw3b8W zCn7QjnNy9X#u-kP9r50_)yDs|=u(BDz8rCsXM$+#c80dhr;GHc)$KVn$EU{E*cWms zhR-#|VXh^VvcbYDTH7R6pKOZ#p8}k!rCpbN7=zj?o@8p)*$FuaV;9Nj12^;y+Yk3; z&5>_!=cy-pc0y z6%IQl-N_^>d9%szk|T#%@!^R~g`_Q?Rsc;-C@WPj=fB={H_OBjL=JT_09#@YL;^oMh>q*EZ-}8ybB%aUD7zy*mi9mqCz$FdmUzaG%-hI-hD#M_ydVd1Quw{sS1tl(oA{*CfKcdOI z-4p-rl@y{68`#u3d4tu(xHsr&6Nr2~*bjuQ1b9AJ;5FHI%&cZ!)=6L#e{)hl87#!c zCXF0dnW*6yBu)1deLo*Hs#GooLV`*Vl)pJ&gQhH&kz?(^ZL0f*9Mw5l0!!2467JA+ zY;e^Wx+>%Z_l|rpMgZqqm#T2%Dvzs)q7MQm%4Y{;xQ8Pf&g;Qr*T(Eyxq&cUr5=p} zo=-cGZRv?etGY;(*T)sirJ1dBP2;NdNY&dn0Kyn^(97DS;9u2^h%w71xhlRB_w4dD zUz1QfK|klQgWm|Beb=IO0mGCbFlmdUO`>OAq!Ul8n=_}`9=d=5En2%y;FO4+p}BML zy$hEu-jm3T`6x079cd*^=lc4rt-=#C9t~2W4zv&$x2J+7AWNniq)M4mS=1lTOmL@z zGxC%5N^`V)%hDUPU24kiV(94#pPL6HxhX6Ghl%?^RXfX5D14oJi`w^hB}#O5m46G8 z7$Js2p%r$N3+?e1rNz@+%R4zF?9fj3;_&!A9MqYnw5s>eRWWo26n>eDmat6xwyU4) zcXRobwxEw4e0ynyLH>5J;K`|RY=E)>%wz2!Hn7I+E6|=5^B9Sx6yqw=Ai}tvgQUW- zXWPgb+|a0dFZ;_O=+9_Raa@1kU04v!hZmFYRE}YF4)pCpBUvXIT?Y0oIVvN=^hHwi{OfcsZSc6$h;s z2dYr-7RRF6iCw=J*(Io5l?idGl5L5zs^?#DbZB73fLQZN%Rso)q|~a*q#GSy5m%X1 z2!iC$a(9(#pn)qDyAeuuoYwWP+(!p<2xPTp$V8?6;q%wy(rQ=KdSBaTR-_WAcCg~{ z?I*Cr;7Qy>@kM#~nHGVOIHq|wCv*emVTvi+E<{1w0pyB}!C4j=GHQI{DEga*#ZAi6 z9QuWNA5qkWA;BOhX^p$TU*1n%>YrZ%r-oByE>l!)!(%Ds$sd3Ig=1c^k_g+qXGNN&Ja~xt?${n3k}G>l_^ghB|B9@ zhV5cQY5!_v!#cxsg>h3c&U~=p9R<}q$n*faMD^o4hQd=CZY9aqdO0iD-a%O`FXLla z_OdSgp8Jt+Pp8e*m9;pThZOjwH7N+^f2`2dq{5g^x8DfeHU~mty1X~@;M@vT;aK>$ z^y6&ADNOaX79TinE4PZ0Z`cJ8qNR;y8)Yn1-XKaXoJQrN24C7N|3bh z%t5E7jiG%Ke#wy(6N_+Lg~>(0<+~JSFdqOwKI=@5@?aySi870uk51ws*-jJviVBJX zBRn`2D<>WyP)aC`LIz-!eX|-77s+w9e84^7i^|v8#8{kzS$)#s#_YjbVd z)$=CL^N}}-UUsOaT+OzO6v=n&t;fYIj;1)OQ9ZW4R_$jlX!K*8c?S1QCB>ToflXAU zbeii3=4l3+xKMBXSkzzSO$U;mHB)~GPPnye`9z`KNL=1cQ%!R_R#aY4>-@Bc^>xSc z8Wjv-O|Gr->)D9*lG|GkrtdW}%CFNuVeuxFFC>Ss-PK6~o7N-i%&4<%E{Oh~e^{94 zC(lbT^5j4xxZu4;C9%%g!8piQY*F!r)A`<(U=|*Y3siYwMbsvf&vWx>X;-pK@UOw% z$pO!LjR?f~j)^HG#tXiDU*Ux`Et%I2oPx&Al9(0OngZ5&6&7=5HQ?Nx#$ z<%pr;rbz?~4h)+RTEgbdFQ+;mUR9h7iB15a=U$d0cf2i`fI)3mjFWi8N{^f()<}*P zG1K`IV3VS%3EVC(cDkY`p;PZVl`&l;*~D*+eXARcY_iSHLS97)Fe__DDBEHg`QlmS ze9EwkVkMKY@-{u+a`p&ECmCAXhYvy>lL%x)y;1kPO8&%fabjIG!X$Q38Hjt?f|6RP zE>Nz`MK}S=YrYa-dqSURVygnyiy^NaZ%OWPQneXDPj10G138MX%`Y!Q=YF93l1 zq84<+;GHlZ9-$GQPa}w!e!Ftc#e*gi$t9gBd?~7u6ykDE^yz52<^6vEWMPM(;$wl-EO

    tptSF& zOk51W`4?ZgS-pG)0N|@$1OhDpDoW$gd(5fcEDPHOAR`URY==i%>$_LQJ#g1%jI+$-jmk<6}Q|Lsrpg< zI8Jh0!2o^nqG~4irRwR_SjbdBhQoG;$C>Crgko{gufOVg-Gg<-MZXDiR9|$95D6TG zNmQ(GVn=$^sWA98S;OF^Ke?fCPz)9S$Qw?G4PGF;q*tYQ8H*a~*oZXAP=H&!TwF_Y zkwy1#kcSR4xps{Dwr(^8v3>}rqL?is6+Tw6+4F=M6YWRtEDl$B$^m2AUomK#@IS=T z;FW?vHqUdNw2=Dw+$!9a>Z*CJQMdq8?&!_mxdJ~+)qCm*)bg`+wn!tops~2Hmnofu z({R$VL0gBbBV=sU(pXcEVtTd~s%?UD7CxXbr$4|E)$L>LWhp%Spi6@Va)+JIqToRg za&+*ci=|zd&cpTSdNoEo7uYVx zZ?E5gozk9y>*|Xp`afC|t<^#wT?5@%`}`D|=kKF+K0xFAqHTU6ZS#|Bn!mjY<7LS* zr`d)&<{w|bBz9I|X@N4m`T}<6DvK{aF)0R8(wOA;BXQ%0b^6C9C zPEyODO;6J_gBEn4>f}Ds9P>NUu}@-%A-@wfdHlsa;7)&>73w#zKsB{KHM2b7-WCve zD^&p%aezcP`}Z2Lw6Hs03|#9!8`*zk1+UGbf7aT6dZSYQ&!FG^;{X37pZZYA4U4}0 zW>G@A!rLDxvrnxQHBx#({tfRv`Sad&81J#UsCM-F-8Uaj&Mv-s+SPx)J$iHc)zcpQ zeSUWGl`Km|e(=MbOp}a6$&3VHM)HL31NDW7aW@YmO8!ayOxgi?`ilhoEBV)dkwBGY zWD%t)5kmz@Oaul2JdW0ou|$TT--zPss6{PHxIeA>{4p4<8yVh{HIWS^)U9jAu01CHXvL zWI_Fy=_zGN&Zh)AO-yM3bI<)Oms8hhMfh_5$&X*n;@1}W^H=-}JT-w6`J9>xST|u5 zk@g{Z`pfy*$*=gx^&@&=C1|Eo?-O$Ao>$!=p@R~COvB0>`rE}nZ^ZxOSs359i>Qu0RL5zWkZ3Qr>A=x-(kxY6&ADHRmU(25{o zODz%`>)V^}N@sOH_`vMdnmgV&llAvT>Mx(hp9j!?)ZwQ|^@j%aUwXy(@8SOFOZ}Hm z@~Nl)dBkw~nw#X?-_G+qWoy}itP~-t8r&?eX%^F*f+r`BD%`;v@CbyWe?<21%P&q`pM#*YH<8^YM`qb0^Ubj0a#(xg_qc8EFpXB2t zNWX9NCuSvnnzB93PmoVgOq74kiPZ+|MZ9P;nLie1A$Co&(x$}j+C}0=EB>(HzqOvI zWek?bS-gg*!rG|Aa>w4{AR>(Lms0;5bnLCm+|TKB8C_DVp-v9#&3k;_urp{4M ze7Z>Z!Wr-b-O4!`*4M%Lbz%deqaf(K&O~-T_xhls; z@i;ehwM(Af@(sjfO&a2g=&xRf_n^!f|F?L%SZ%o+2ND&!bZ^|7ds7P4996`p5k*^z zqK`za4oVlTraZX-ktV?8?1C-j_VAxt!h0~g_{eZCO?j3Ngh6!3f*MXJ1Cp&(M} zcXjN}RQ|%ltq1jMl&6w~2^>kezI^E?$99xoUw>$T949f$Ge3-T ztGRu}BQL6|bglut<7|xYm~j|~ImcN)jA@4GjdLX`Bn!Vn*0tcQeib^cI1ku4Rn=&u z#b~}vTlzu3nd&lBGn$|AC(hz&@^S_1X_4(HWI6Fj%U*xW3S-sCc%DSzWK~Eo!0*X; z*i=7Ti4qg_&RXKrg#1)Nt0zC*P7Oi;7g+c>4zf+2$;g#w_`CK5|M@ivh0GSDolS2Y zcM27>TyUZ#qcHcPgynykCd)YZF9c_C6NDLQryBv9vHwE6r_{7SEV&fJXq~2htzy$u zSC%(?WGkdr&&5ShoO3P_6{16lg}h=lz?+n=YP;Ob!_BZi>Y4z^=C?!4p!!l&(U(WP z9Q81pUy-`Vd$-{f?vL|I0q$9(Qq2+`D0V1bJB>8SB$KF^4A-w~MJfDPQJyM}_Jo|A zo?o0EA6=cjdrRKFyE=VAuI7|XlPF5=!Weq#?xMXd*N#qSYDY@{>b7mNJyz>S7zE06?O#R0LjSBc-HRY6SZGCj|dYe)BDLh$b| z-2OcaM7e(Z!(!ZTC9MxFXljSP@x(WeBCJ2%T&I1yu~O?)60HbmKbu*E|5^K$ zr9ARsON!ebY}K#7KHG?otf9G29cNfU7oIv#^rD_VY(@qz!+=iw?1A=^(%T!EP|CJD zsVOD_y_AlLqV>-D%{A3Bh$4}$GTT)MB>r^D@epB~`ldn{sj{Y4z|WJ2W<^6i`f|fj z6pl8toe5_45C-^AYnemVay0+1RcGbXF1fQLI@G*V%ycfm**q?&(aI|&z|t<%?13v5 zeuM3(fBo_HrDCmXcF3ZiZcDnO12Q&3xVm{_i`#^MQDtVYYM6|Y{p;?C)wa8KPw67f zS0`cig8Z_X0lz}gT`OhVov+=#0UT;;BS4``cBA*)sPvG2+pdV(${qU>JoX3sJRtrn z4X!t<0Jtvxx3}Lb*8kfded+)BNj{C@zrragRO{@Ta&G-u880N2p}es8~6|6wrdf2sfQNj|@E-k!vjKURW;TClJSW;9CtfM(A6P4L%~ zCr`vb$Pt;-D5V(@>n3?ZmW+}xPV#WNf(kYQoG`c%-Sm#XyiAjr=-O&~j78F3!(^Ep z=7}fe^OvhQNmW0J&_BvYUYu(34Ygdm_9)9*%{;@r|KoUY(G zM5OeDbJG4vegn0)p8g-CO@AUCITvD=*?J;jkdSd&6b8FukSbb;2KtG#)pPPESte^5 zZvAnkuFvQ55cyBA(&jdRHS*u6(<{|~7!AM3f1l*D*8W>jz6DU=kED@*I-9FINK9Uu z#<{U;-(HhDTNSJ(A%*EHe-TD29M5Ba=ZZhAdHEMdLk*}b56;$lKp|y2cm^S5FI>qP zRWGA({efdPxhf~`hQohG=T^!#ww`OfPW<4dANlcwW-T>X^*%@F@w0PTwA=o&W@?4E z)o6D4@5-@NX8l#>Pp9DopO*8dnu*kasG zKg*|V^YUM7^#4Jpw?8QQe|r7?7ybWJdf!hpF-K_P~D*nG^!7W%? zMZGQf4PBPHS~eBWHm|`|1n_7jxGD-UT7s{qxoDA>cRG{LwJD$-Q>W-05eBIR0t zrTpp7CP})IP2-qJ===@KHGN#cCTATU=l*U`q^2>lJg`bluRPttaX8vKR+KujUNUim z1bzG9+DT33S;{CTsPxpekBd){aset*Dndnc%dX=jT@k^kfD@2BDG|fUk_R-OwEn9A z)>W5kEtDdV!nug~@tu8lOS3EtOxUa-Z1!%!j-{kMy(N43B87?>Xf_ULTt@gCRHEEy z+Iwi@TuUgYo4KZjcmGjGFJh5>>R~*?J$Kqu8pt$Xl4eOtvpl5i^FRkNY3L}%XPqa6 z6HtiWf*HD(;hANWrUm8HzMA?GqvfA6(Znu{%^4X`d(Tk|IQ)UAkM@+bp7OWLoz+|u zfR1t%Pu$SeS5LRO^4{XEZ-31y7$D1ZC~5pUJM^c1Fb zD_GChUvGx6YtP9w_B1Sh8zgaQ*JQst*wz;Lionsq2q$>Oo9e8_Q!`bkRDgxr?TyvIorjZr9jmC7 zw$iLzx<&hKp_7dD6B)X-KdaQ#e1bXo4rOfj>fm{jri`>hs$q|E>2HA$Ud=!T>)9* zRhBGXlvw99q)~7|r{!PdZ(=i*T@tJ|Yj^5L|JHw(=B(-}pZ@*o;^@P7N8cZPcz1qv z>6&f%{HlP~X`P5_DI2AyY?Mnz9lyf271K7`d~$Sk^z!KP^uznh(~Gx9Z%&(UxETI# zbXD5)!cMv;T;c`9D%OIEl{w!RR<{yM6`vcrY6#_qt}6I4XIQR9R8Rgf?AflZXp%3a z4cC*521RZ{YE?`#SgUe0YwqV6O=%oRgQbye3dZ6hTPAf9k3O~Fy3KdE*~M^tx$d&0 zK4W)D7L=bT{gNgupJkN&6uCJ|?R`2wy1e}1-Ngn3?BVxG&-^3ql!Ec`_ew+B{BG4R z9YIyh8bIQztxBSZu_~gawPr@S0lPc_W zJ#WjRM_`ay^br17HS;gm_mS9R=CD>Yx*@JRf|s%ISxz&q!xub>GAuEv#Wo6cyn}lb$ao)(|0B_MxjgypNLLsxgu z3%$=X|1`c0vm{>7IRD}v|M zmti={i$B2YF4wfKH2d1(>@GNuIK#E&+1C|lcR=}w>6OvZ*{z~Mzl!xEYEyHv%q>0s zL&sJ#?#(7(A zHfq39Z!Ere_x|ei;=|Ru^LMY`ee;hGuiqUXz5Z}<`rq%*E>2G_PcObdJ3hVo$N6c6 zX}0y_6x^4L+m$`0Q(27lQEp|lvqish@in)6RerTv za9Kx`3ZOq$m2p5>FN<$D#|Nl1q++5QYc(5Abz|M8YNDH}H?{M0UGzs%a-8E<$5m|J znW*mN4f6&{J#?PPAU9$~xqVE4S#9u~-KqnBS8Bkq(V%FdW@+j}NVW6eiTP|3bXoC`cNdVhiF-TV%(zn~xJyxNiG@8>Wr@K_BJbakz*+ zDQZGi+?2HbgSG#IwJHwVYLwG5L$zuzrVD}B#b!MqEbHNUMg?)f(d5UvI@=osQSR7+ z^2VW*&MUJfj?&Sci#R1@M}~FV0kI(ycxZ&^BdzLD=F)JfNM>KydyR8Q zi|zE}E}(B;IH%JvPp~3!wWg!qqYt*ez21X{{N+obl1Kb(UjJjB`kS@?Slj<&f4^Jo z|1|1-ssH&&J}#>0FY|;={3wiPws!Q3vlrCYU)^@SUjKp3pwM7@w>`V^RLz6eH1~UU z4JA>QC|aehf389< z{QFWd7vhdha#wL#gz-gXeGrUC=t23Wf8X#;^Pqf_=2dwbsB7zD^z{s3ZU4W1k^Yam`=c-P|5JQ^M}^3itPP)eNK2CD zsu2I4rT)Z0YvF6#Ui>?kr!G}zdA#N(zv$-mPjBdImxwH9+ylQ^a-kgamtO!5`Sn-h zPL?1|_uiJQyki;Ue<_un{36_cy3gkHf8+B1>*#-P)GgNk>km8oU+Dj*_&BX1A65SU zbE*6H$tbEb_)t|OUsv?6@Zg3O|2FO_u)f@1saU=>kgAoh#&1p=omthoy@r~YgHOoS zJY=d71tV-uqlnC+j^nd7K-JU(9EF{6FeXb{p>*K-d1{W|@kLNL$Z6c9(8F_pbe$t><38NVlHx)?fu?I+vwBa{vUsP^!A(6 z*YCdZ7QutYsJ;K)QLo=C+J8Eo-k1L0pXBp|eD^9#X5`2mI63Ax!2cW1Xq3#hj-p64 zT;xO|=QIHDM0cABlF4#G<6QI@ga`O_^0u~~JR$$=cJ{q)XW(@^{~~Sjie8iM{x0ct zdc&>fh^T&5FT_gdGNQOG-U8}~nThGa>@E(>c0&1>(Mgyz^!7u>M)JPX^aYSKr zKcm5zTt~^|#!OIG2vr)bA4RBGq<$aZs$>ZgGPa~J9v2^{NwyG6(2oZp^RFX5+eD%6 z63P;e)!#}17Zn@FdS`vU+d&BIGf#+k%0Nj3`{Yy9LtfBDa!UiUjPHE7o+^*rU`CcK zL{%*{%slliys_>jy3joj?(^H^mh?t&p9gwGIIO=f1WNvWaW9zsSsdn}A6*>1L7ou) z#2f$K{xXCXr7PlRl=!!P7{OA*SYXu$j#VF$6wcO6>F~hJsAFby6a?0K#+8UY=G(^! z*ERKXA|n~eKfn35-R^n5`};M|XZGsX}=S4O^x`pG~G@JWG;UMBB2T|9H3f zP5ZVt22s@ULpumFI?2akM-GMuO?TvAc(9{af!N7)lH@GU{FG>-F1Z4kP%b~`3Hl|^ z_kY3bqF`2LcIL8Ri`OBqAbseFN_h(~rXP1tLRx z^>H%O$SSmWCV`5yCCN3{Bp7fVBT%3zNhkzYb^sGq9xxMSehcOo@ua3xyY7r4DB#oaE$!PL4kP!-kXI|2vK4) zl+orqGcxJYfbv|HkSvBlzlv}gPJsHrG{B5^?8idQi7B~Wk<=H3q;uS^a~GZ*i??)% zL@&wbG#gvL>GAEatHlz1t}lURlk+U$hnpFx-7knj2Z!p~#FXsgTLY68mS;_lM3?u4 z(BRt1GUtM{*bq?Xl%OpnVor?p4U}YC;m(R=oB{x6D3rqd_UQEO(aYDTC%`H0*}{*a zJ@~H-30`KURY-yEjlUv2Ly(L3ctHi?8Bqu!<8cyC$AaUF-xhuti!ZHV$+nP`LEVdV zvvX`8;m?aL0C@N@4ix>4Zu<>g!7id5+(9eccdqaPs-OpMZl9zyej|c@fb=*Yl2Jip zG}O>Jlu+{nGG2sIa5H3u?*&vAzg{Uq?U7eWMn>K}|8d{rda-j=>6)2-w96B2z=ocjc0Jj#y)2}ZT5lCiYWjxIjGNUmCK|$tDDf>*G z=&ORo1{EtH#j}G3;u#FZ_V~j)jwvV~UPfciE|ZwZJFAB1Z!$#R453&!=OJ9h3*%fE znOcnI%d}X8z}S}y$2Q65 zS+bnXg;7$l3d9*?baI|#Wh>c%)RSJ(Ytrj9kzh=^;2QY^8!2shqa<$Vt88T9RGgqR zh8a#$l!|OnQlSC~8(5CuMnVS_IgOTeS12px3`o#e;v@$#J(ibo(A?lMKn53M9THXv zmd6YnbC?oj*c`V|D4l|)*MNc%-VkJ zN2@%XKv?*K=5xAad;I$EiDE2!a~jz_9SewwFh3MIwKO}6r^y=R*Uz@Yc$#=WGCCwrXQ*QyI!Jbj#o3LSJtZ%o)Z*Bd{nA4!t&vHHkzHYT5GxYvp zBXS_|rZVGLl01Pq0xg&_l@;jwJP}TOPR>-A4B3X;v?HHkMm$G?zEh~JCGX1*5#YI(hg0N^Hr359`OdGz+|dt!X68^pG&qZD+kcmuAkU zLO}@`KTdjwd2y1FRk9?0luq$=^Z*!=ZP-*X{8C^!R`J-u*L%w$rE{yXGDAGd49dB@?Lqc`w#-!8Z}6f-qky zx<_diMx@s(SPTQpb5Nw9*g1(#UfLdP>wy$Rukm@mkvk+0K)8KBsL#Pb(?;B8-94sual9*I$vY*F6*|lVL2}wZNy)Vmr>o zDm1)A;Rk7Ad%GvnrXzg2U{lQ#6{ae%uGc`=(pc9!z!%^wzV)Lp;7GObC11E>m;LYi zWV~2Ld3f$;Ifz)h@F!=9rxE3ZmJ44l=aYvQR;UOG5~fl3`P*1&6wB#vvYfudT?Yb^ zxHU2qTUtKf%KI89ow488Q+m)JnSmTlfI*$ADul@=1oy)^RdI8Ym?SBU**u))yTE40 zF1rm_NU8=JV^e`LTnO-1SnWY)cQEMlhYvajo!tZZhnke0he6=cA@cnlpxgT6lI6lJ zXQEKl>A#UF1=mKF5GO1MTrU>Fam~nD&h0!!zX(;K*foY)L;JjjCXC@>O~fNs2ZZkn^!ZoWO@I zT$_c5&G3Vo;gKg_QGH4vTFbkm*YhN~VL+V1SR1+m96D5Q5@RfaD1dM(ga|fImQhe^ z7t=Eg@YY*I{GxuqKMA?h+O7bILX^rnIAL0;T`z<@6Z-C@if+1~;+x0c+_))Y=7BmQ#3pkg^&t_CvhNQfX;5~Vupv##VGg0lxDyJk|`PI zG+Tr*jP*_CPv|+#LJ$)n7EEt54p=-+A`_lP~G+p+TZV? zTQWQ_PL5t5-FAUc$i@Lo6ZWHQ=<0jjj&14c4zSBQxn?w?;Qr!Yw4;ROZD{#zyQd5a z(D=64!BmHBNcCC8tqanPVsk_giQ+?W2w*!QXxaP7x6>;vyX;*QHwKu2binh4FR#PH zz2$Y-UW9SD@S_)pUf1h*-TlKp3NRSTc!%%&7+;V_8*J1Wn`k;;DUWH!2~Wyj#nOog zFc7-3j29v^eix@pF23peDTK{M%8v~#rEiwf>wL^o4y>gdc#xQJhg37Nep8da6&ycR|Yoar;ZaY0?&OJ)XtYP7UA>D>Z)vu=6vij*kcue<)g#G@mws19jr!4@zv8NE8gg16`lJxN0TD6_ zE>)k!(_~k9Q-EajHgGua_xgxr3=Yk~bVoHpC!;xddB(+ngD~db6_kIN*66T-dMa!h zqr*d&>rL?EJtbs;H|j%1e_B$Of4E*wr!<3WqC8dRyAR29Dra%f*s?M@0L#j6aRCOB zG{2kVq$eWhdT1zlkul=$d&92R!65dH;X{Tt_3CF^F}YFuaL{#!7ZsCRu)e4QiJ8ny za)ussN23Nt61m-@;m*1};P{F600({#^d7kFqj+JaM9g!Co$UnKG*TQ~IDjq9P+>r>HjGEeerRjo2k)huhNpSWXb(y0(nOrUBU&(o?WQ#{L~^v;MfOG<`?& z%YIIx@H+FemAO`TcXy(V4%{rhNzsyv<(yEak-yr5zC3%!a`(b`_LqE4|Dx?OPtY&< zoa*n}A`n=G;p|k-xR4K1nmNR=CfS76*o}%X@&pnfgkj#XeUN7X(dOx@geZ_C66VCj zM#=n29Y2C6BGa){Oi37%NksjONTYE{&~;vSJOv%vdP7$Tz&NIN;D6G^Z^paY%9JO7 zhr&Hr>wOcqH;j`!oUQ=>GYUCh@bELHEK0OpO!!y@>&5gfrm+a`pPsBu~g32EIXJK{km)2i0>P zlmcoJH8L776t%ZxREW8Dz{lF1?1`Ox72YEcwrsi}0t6Y&&ZFflj4x$)h}fB7r+KI% zf7+Q6muJfuM%@0IjQSv%vmG3l=x!5$wyTR08<2cf;Ue>DZ6E56ZQv~$4TLBpB1HTM zJY+dlu67anJq@M!!4(ah@+Xb`%cago&IT zHPJF8(Xe%l4%%F)WVko?JPA%hmMuX6c)1K_R7sWH4Lq@Q*K5^h(BHdfJg z>nL>pK=V55%DsJcspJQcyi@>7NEsQ={g+V1<|NFdzklwZ2)EoAxnLCj7!4qaNy6A3 zlzvH5z5}<(GSjzVgV>}|AIl$ol)BX!)KC(YCxbg5j0+w|q{VVxzIF#kUWL{yNwFAG zVMs1Qm=IKL<23e6N#8Y8Rfys;3I!KM!n8=FCfha>0R zT2r|LLP0G8Vp+?T=phZdY`G9-o^6daJL0BT&Ja#UTvqxDqr5>BY!g1HK@>?iq41P; z&06I=UM^=`lW3D4z|t=aO%VN92n6Hc?6`kvzgBe6sw)1zdl;?6<$~ zufZWGJ4{4V0Tm9UR8F3Bf?MZw=vaSan> z)#;DWVfFQ4$*k`*h)JILlN*}J*kqvO4Cx4^&`uUc5P+L%m+WMjMMY)HQ1v#*JH492BuN8^LeVHYw0EE!6qxkJuzPGh*ifE8#9r7m>YFByEr0Myf6fo!-|-RlZ3Tok>t<4l;H$ zIWZnxSB$*Ma}yj{8G^%{aB0Pta;GA>4U#AN-T*oAI_<1~XavZD$w*Z(u*wO9F-j%J z>GNY@_%pjHFZ>3V~FV${|xLw{O^85i1Ntyim*il_~B0EJ>)=Jd)@)?K~cFrJ8t z83oVFFp8;suJXAJ<{*$yhg>p|?UsXw0MrpW^Y4viUf5(}mQ zrdY$!&5rdBOLf1xs1jWnL)XM?;wy3{4U5MSm)gKM7smSA%al$e)^wZ8$lZ}q)1@a^ zho*{LiOfo%ywkRfVQ+(|-C^$#vm@;Nakzb?yGJ{Ea5a%*{g@4y3Ec}a8fonDj@fWQ|8iCXGzeB>=M_W@O%lTuu_BpyhVo1{zN ze+#;xd|c@bImYT&{US#~F;EX}kF#rk;t4h`d8@>Xqsb&$#<~8@QOrYFUL)!=E>DEF zVMJ%h>1fCwMC5}C11fu=ue2ENLKa=}!Wg0q#7^5AAdpjD;l`G>VRx_xeneM>jnZfM zKDH65SwK21T#T_FrC}_#pyY#a8w!qoxFoN`35^+*w#7`@W3`Lvj(L%IM0n{;Q+gyF z^!80SfgzU9pTEqge*-y6Fn{3;k3g4`wJlkmEhoU*r8`yLvRkdpi-0UiW1x7YJFmrf zueN0|mJ%Xh?b(sv(xkQ+P1mk|wk0O%gl2lBZ-|S(P(MqwunC5p$#*xOmIbdFx zaH07Ox#GC8`}J2{uh)}XebBgO<=|j{XI;A5z?H71g-UV9N&;n{zlXyju9s|eo!plt zPp%G&9l(Sc7`9~w63%}juO7N-IF+Pdpu_LnC3MNQ(R}K{e+_poR`#5Sv$?8JDu8bb z79u1nk%;g$>8{<~UTrd9Z-^=#_Dh;;CbaluDmN@o2xvjzs}FBNozgGO*_`m8ZQeLw z!GQS-0)wazw%|i37VeZUtID=Fx+42tZ{Q)tN+)ahSs=w^-8RaCjBS7B}umgGb zh7%(E=&8J1!s(E^V3xQA{EziFs-y= zCvioheb4JSsvtN8LQ(CaobrZjH#krV3gZF__d1<#La5c&$lBAhtv$ox*!gB@XU5%* z*Y~;yW1C}$Mq^^=*vlRRm6I~@or`9l}- zc;o^ezh&)?vaWSyIzNO=7>%LM&21l&7W_QCj;KlbQleizi*@O#Uys{V!CEAMIKkbmeJ?178Jx^dw>J$01*Wp zA_D3xp;UBb5})a-C>ozYR%%AmgoTAVF?HKF{2aD#h)|&?@cNCPso0*Tm@xPpmNKxG z($!1pY_t?rmS8MHIb3Q$!1F#ehwFAh%d~cx%y}&<)#BdJwaipZRrOFy;Ib=37Lu! z3lRLS?%Q-&RHSOmIVjkH12N5?4mR5E{^zjWeQDdkRqa5DUXi8P2$pAlPG_r9VMwgT zsogk0TWYAaXjl=DmThfqft=QI*<4%No_X~1GNa^Wlw9j1ld@|>1RRYmU(p#X~W=OYz_|cP81;hs^7A|JT z+VXc3C)G%pL6>4*)Ea_Z+o6*ZgNZR=`B4<|A<>)rg~%bLXG?p4l}{#e2O#EQs&h@i zfD+GSkeJH(K_iSDBQ(L1&p@>7d7~Y)lA&5jr!GE2Tx~znF>K`)DbD;NlshBeJS(Qq zY?tbC>khsu81M)XblO+*X%t@E?LJE$w6~0O0Fjuw3Oe~vW4w9#AJtyZG)bj%g z`aEIzw@GrNQ~3msqxW>O1Q|dSJ%XU@APm4FtaJchzhw1GLKT2<^Oii4!k8=usGB9q zrege@ahF6nOBtsP&PDvZuPO?*Zs0BV1u z88!0AS$gC^h@~)=Ar0p~V^;jzZRi`Tzf^O2Gp&ozFq2?qH#MAb0?fHq6GQgZkXh=a=~^q7sQr zx`n6~+-7}O^*QA;MZ@&V!!+5i&wvqhuz_|USOHX(F)KNqCwDQqmhSO8E@KiX+Ji%u zCXggaCh)8uiQsicCcaSaqGSe5^2wc_#hgA%g7Zoxu}Nbne+XDFP$D$3uAW%OI>{ob zO*6`gbz9tywibZ$wn!f@Mn{~KDz8>9h44JPdjLvx9MvjO1-Q`qYPP2Beh86G2g;u{f&@UGC z`9+wAGxJW#GTtWte3N8Q%^+D9s5+rBM1~-13V7udI+?LJ{q+{X4w_SD5BPg za_xTxmim3~03U(5)yOpAU^luQsqWJ$tI&*yog6nC_dhnKtqf)I@bkwucS~+iEJprt6JSecKqd-)$P5b?EiI0q|l>W_5_x z;cpP3bts~5q zs$rL=(~6{r#~8?PSL_Z^QqP>q{%*RHZ=zh^mzS@Lm3T8cji7PmoDzSMFa3!8n|{}za@ z1B7JG{{6A;>%<p@wU*Qx(v#>@LA2}CuX zF`LXOS4K#&%dY$Zjd!5O>u-K+wmgjYXJ3P@chDTGgbhCkPo$Ut;ZmX z7b7Q~TdV0$$5IX)u62RVA~mQ~E(Y21bzoA*Gu7(KdVQ^6)qyZltIEMaqv(x>iELew z$o54P0#V2z_I!2djlAxGCEGR;g$|Wxm!ElW17qUI>;4Wjs*6|`N9=0eC09386gb8v z3uq`1CBE(-^j7vw%&5O0izHY^lw8w^&#^`nsgIL8a7txV{%%GXI7Wckd-;8?qS@NO zj@qWVt!chuAoUd_1*NNUkQFk{u8(TG9V5#h+)Bchh`QpWZ>*;I7KU9 zzDg6r_ee%(z7PP6gs7?uYG@+`t$SD$$yM*eg@6Apr7@Dv&Fe#5RdL_4(~Yh6*<)oQ z5lQ^!^?^*Dz}vbO;RY&f=ukQ20Ky3QvB0UHWC6`ocOaLq0+gy{Os?rP5%LphIWz5B zc3lk54Bykk5A5|<1?R|?a-O@2-WXN6(DJ??ktmr#g73@aB3+Bb`xX-S+&+*);1CQW#qetrf2=C^&&iqJTulvBX&uo5T?h!QBhM> z)rR(2QMl*{8fgRJ6P8sHpjcc&#_c)1C;gt)j6!_b9ii0^3VBtC-f{YM=dViTgvRo& zynvK_MsvavE|}egQ6!xL(Mm|iKDj*m=IZp~4WH7F{;h8Y*7+$t+aWBZzbKi>h-LPI zusom{9B!1%AhbyKzY%>P4a{C4vr1i9tyW5LA9xt$ibTj3e8&Q3^)Yp;-~k#jD)i!V z!VMq0>sSwXsARK8S#PgYtrbOuL6K(E5F+wdsvSB1i7yx=Rx@a)Vo>SU@}Y=0#B>$q z8aaNIZy{5Wlvc@X(8HlJ`M6|yg0VDWbxj&n&%Rf;rXslRmWg6E_^TQxZOKMmF(@_; zXL7wVp3Z`cn8WE8a?KPvAO-LAJ_izaoW#?yRS{H*_DC@-bb-P5$^bmZ4~=)sWHfFf z4xV(H#lIau@G(u&yl?H6w`s%WTf9CoHA~Y25_zY2^W!f&>Rj5 zIfWgoe1Ytuj#_^);2a3Rz6P*gptAu)J;{RWKP|a(0v6e1d2PPX>{~>JY(1|io2=_+ zkvqr*oDs{tLhkey0opN(^mV+RCKFw(G7A&dNitzl z6-RG$U2n*`{Zc$lm)B7^Au_cKy{m7hO{2lU%n3Zs>G+^oh^v0TtXmc-BMuO0sVm_c zLn4Y=wy;?UHOY@j5>qmXbuSb?xvswVgUV}f3bBtTNz9UnzD{Pc7odrk9g9e11NlH@ z2-DLobdTFJ3~2GyN*; z9JInQuMHiqt);O&qb@0T7vxOtBioOtd!8k^^d^c95-uy|Se8WRRL7Ss1Y}*;FqUS) zVjzea-ZlE#1$4ZQfN-FUA&_v|uSAy1%WsLl%;%g`PP7(-;2UuJh5S_{6wS!5VuqdIayGo$nb?F)dwc~)g<}QxB?!LK1u2cnfodo zK||5$UgB`OhbjeF#~Gs@I2`}Lix&CI7;>M5btli$Ldrh}trNV2VgR6D(mbb`D^bEy za}+g$vS|jQPEr+OE(saaW(TUzW(Z7F(uGEMHEiBnoe+cEbe8MAQWK7yi#l3^q-A!}g#A zV7|wx;A|NNbWi37s5*gdIcPgeBI?caMYLm<(QzzeJ+ciAvUOz$kjc#g7$?Fntk&;q zFR#$~xE$rl@{6I0SoN2AqB7QmWdY9sI(23&SCP$V0v*Q$zIQ4A8RI~Y(c9n@w6&ds*tfF2 z6gnzrLm-2?eBA^`>)r-LA@2=e18jmR+RC{Y*p+4-a0W%y78~p=I#3oWKBSvY*@0Ek zvJxRf%q7SY7Z8mgl!Lo@nA1oSH9KhD1uS2s^eb%I8Jo#z0ye57eSW+W5p2oSSp=UW zv6M6`wU#mQSvzEA+`fCOmp1}Ce#QjPF~fLYI{ zNkGny-Vjbk&}{YXH`n6_Qy9e634d3JS!|ifX8L?4xu4Cb%mKlT=OB3(3d{$}smDPo z9aorQ^kht@jg9V+re++I{T=g&{x{qXkc?9C~8bN0=*SLEd> zxj6mr_h%QUC*;+;3v%?9yg&cu;^^e`&;N5Gf)=8gsW=}6n(HgsQpUA~G5M$5mk)2> zU7cQfi{M||#cy{044tm@plo?9$sO0JC1B8|6atM$-q5=rd!lfq`ZnbI5JN`O*&cViLvJ{SIlN6|DDgDmERyfb^XFz_&!3CXIGL;@t3ea;oLniV zNP+IoeWnr;{!A_@CI3WL7M5 zAhe%zJ!umoK3`sY{CkhhRT==xp%tk)@EzS%P}V_@s54U>3h?&z)iMMLdDDunOeAxe z_N@q}5rMTXJDq-9cns8oW=x%@Sz^R>WUQ~m0f1pAX*LGY(IECw!j{>| zccwboO_)MnS$mpf?clnYxd&MOtlefbCxvu1K|G$Jp>&E@cN3kbr+gHmHlu=ZxDkjh z0?|{+1HGOlNWyi{DjUr{fT2mSsqj{M!eGIwxA{C_IYW@U8nRiSC?hY5tJ!j>*urP5 zzVj>s-*JF%vUqdcBPqv2>cn6JAYE^SftWOsuC;}#jYO6#MOh@-suVa5SR@dXLj`15 zR45j*dM~&^_h>%Z(jLLoDcZwoF5bk&$Q#5{I(RR+9p<>zz0JB4e-M1(e{RYGkJ z63WDsL`V|a#sjsD<~)`44C~Q?lx`71NY^9BNqkFX!ho`K^PhU22w68Br*JR}f1>Yc zizb#Mu&o@XWfB(-(ecL5a@8!*TsLhTW4L~ayK)J@HVFzyTMHLy6i!$A3g^pIBs$EK zET>|NLtkXcqIQD_tWH}%gh4VDh1f^uXHt(f%w8N~Mkid{Le;-isVOwVz%fFT%hyLR zbm)KA=2aF^QZfE-Yv>v5wQ?Dmo)G-iIgL`WtP_#7h~o3Pyc8r0Ka5?Q>}~&;of#$h0Cr7j zOcpYONY7=c@`K`{jSZQSz!b{y%@_~mJrwi=#{>vg0e+)onXC3KctqXD78S%+amJDwCgm$`xU^k zdtXRu3qOi>$$|#qav>s~xSHzng#ch3bB+v&&}=tkLb<7H$x$S!2CNPW?`|U6bi4tA zVZy)>!ZV5~tg-6b$^$|u)goPbx$L@X`&^aQ$lS;-ii#sDa+571M0y8`Tf{D@1jikxv*Y!xE`u^Yr!C@hESofpqOZrOG{`riGU{=ZECXFtmot+0 z7q4O4K}*s>BM7LzA4o!?5tYm{xb|c{q)_5BN#bJ7U}HoDi271&Vmj#{34Ea9t5%yy zp&dk+2PzV3g>jv82z7<*7Rvq%z1mT)ZldyZkPO_{ksPZUyO~ql^Vuq%c#|Zf=SdPl zgE|aWPt7oQ>Dph$pgidERfP_?;F-Q&#z90q*@(AL@)8E<>SP96>bCmkqWW-iYPC!k zz7;hG3|EH{_dAsv^4*)ilZ7atgLUV=jKY9WdTTq2J$Jz6qpC=sgqgK66R&$%xU;VG=()U#6CHjCObbt8Xb06>m{qo{Ur1hi4!ri*DG2pt5;*{^ z-y781{0&!rn6*19S0XC79gASDmy;WspWcToXJZjW0(B*DT~Q2ICqW?6cp)2vk0;A4 z8Y_CL+^KgnnvY2!dfO}ektpf2C;;#3Nq4Hfih@NblOVppvN4~qITfM6qE}6;W%E;vIG(6z7CSNW+e0BomuecU44lL z1;s!(WPrg=iV8CFL^E z?NeAQli?*?EY;^hB0rujZLHH-MV(#&{9Cd2=VI#%vEf!>ZQZkP2NQQQ@oEzxnsHGf z?xX16N(BHGnXZVh^B#qi$`z1{tFu=}$5-zz{_)}4cW+LidTN2a_fZgVnTFL|3Bw$F zk{Otwa6QlCGm=x57iv6A=&ly(n6?YZCUzL?z8#ibwR~g4RsI1>&@Z83Px6j3j!B@N4dK4&J8!D?KM=VXHNyLEH2SQ89% zDY6%EyGA|Vv)bgvQa zP-RwfDOVtzOUI_rBWb?zj^)P^zT;}M<0&gy?uMine&(|nLTsUuV^Dg&@B1T#3y$* zP}=v+86KLbWTZZn#aw)PV5SO1E}YD|y*XgSPeTJ$$1B^xl;yzA@Mq|vW14xaYg=o^ z=&Lb?1$vF#{z**cBGo}Ss>?zxU0hSZw-cIOue8a?4AXND^QcSF__xt1_6z_WFx=Y+ zg2to+SrdyxsFjq;a)X_I?+_ns4#Lckx-(ICzhcN_s!X!VgCTt72FAV}<}5kEGR~2C zLs-SP1>4<`UXBRTIQGm_A3%fqU|PX(@Ft4dc30R+KNbr>X(M4^U<@5F)}GBswPN8A zboZVETiB{-5hOB=-e-|13krUTg3-sgb<9XQn12I~IMPiKD>0=e^i9?9fmnQ31RZlG zi15jpWSl*OQ9Ho zzBib}(ChXQj9wuG{h$V2T?Q3lC&!)55O5RU-m!p7v!V(4(;?-MhmRoWT^ect(g=DI zvOEi~m!by*ZnLIBcs&D02juoQ*zBQqXdvitTj_v4u=XSw_=a;h zFc3_zt(k+PPlPUjBV*+yQLV8t3chC9F!xQIt(4A7cZJI?aT3r=8DKizC1X|ZQRdd< zNhI5J7~ZvSf@Bfo20MY!wqFQ-K0ujKR>_q@$jtMhoC+&DLA4a7x9&`pBvMpjZR-!rs#065( zL69v58>ItN{djHLH-f5~zhpgEWb(u2ajmkXPqfJF#0RPpxLvW&wL zC*%ucQmy9o&dF#J7_o2RkTKM{`k`ni+{9!^8<_hJn8GLr1~V&jOhLcOIATtuxsH$V zI`M;R8NY2{kN#z~!>};ZQB6f%yKR~2ER5SbI6yE^5f7ryGr8@V(T0UM!G`w$K^XkY z>S2NQ!t8LG(YB_qa+jrS1qC&9a)o}^&#uEf^RtzebCSfm04SuraWOa(#X-{~U=W-( zO|prsz7mBC9ptQff=u{1OP8ha06?AH}@r5Rq?-7H72~bA0VjZfG2g z<;_&_HafkU2DTwuKColv*m2CU!OmzAWN7fupx=KlliV2fboh|E4|ib{O#CdsB~kWO z***?wU~Msq1U6ZqMThwiMci6%kLRiyC`8~uK{`?>_hh=T=ZRG+bYGR$v*8vCqr#>P z47v-RiNv4AQ(bs#g z&yG*uUYtEo55xFU~8;N)wwT=_(6n^PD7iG0liD zy@&YA9D$B3 zf9$|;Ty+*3uK+D`misYC@NDj9lqrbe4!Nc=oraUpk8J2@p61B^kt|zeJIUZ*S!)NM zIwUy+;cXZ!IjoFe{Ez_XJFqFTGF~|X^CB~ zrC8V}F}=WW7(*b0d)XBr+9<|>8oh!faiBZi19h22K#GBALtU;fd6j4iY2TVW2A(nl z5n9fLX|}0J0TWV=a1tf!GK}wX$U-0r#9}(25e^IH zJkO6cpXxj0zmBr#Q`i%$Sh);r_{-lFI7v~-kquQx4H zEA%XbxSK02Y()he6{2~u-g{(-!#-mD*~-ge(jIwcOrC-z<-K9ctdGEn9tYsq zID~a{8H;)mC?AtZCqNTaU?ZKRh!S{8cuFjQf4)9f6dB%Sx4|N-PiUl~ctXk(zHW;w zqYBB8aBj13xzQt34Umzlgq(gM`J}dDbS)d{>lPbvR)K|WNYX;NKap|%&>|e^&}!sw z!0}SiM6NUv+_CEQ=(C8AsXt*89!}8+$LG?cX0#)%auH4kk#Y;e906hrSE686-Z4uM zMmDWhy*vqL^lLM0O@X(nJT?Lae;j2RP0y6Y#3#VXdsC;Kln6QbV?v@NoXTYMVmWA`3xCu#nzVYH^?!yk`@H zg@!jK$pnXb`CYO&3@)kAH6j*OVCFf!fX9kI#PL8FPHc+9WLoLRgsdYP$K-=giD-{W{n}b2LhQ@NN*6fY&IaxNWnC3D`(pl$EyNR=h&62{#oWY;2UvUtCZ`Pd)l^^^ z-8yERS7fSw*yx@J{YjJs%^`R1?^%ji#BEs8uFyj4DdehDJ(bO5|y+N=gSEB&OF2=2~Hc&0Mwk40fqJ z&lgBm@U)6r#P$qTq3IExrkpLyg&+ckyN3R4S{_V^P#fVS?7F^d2>xY8BD-cYQa42u zan!E`~aL->J_j_Yc5uM4&q5TQ4%Tgn71(Ih1|H8$yj zDCkd?rMjA%0yfEARf>eNnTXDNDJ0v)+sgr%J36Y?H!+VntKgT z-j^+}v*5Z7p>kX8*wkgQfC5U%VHW1g0;Nq)3iFi3*uX&`{*Hyc zW3NaHitf6?&?1zuF0s#P;4B@mr*H)6%A;U1GPz0Y6{8u8wEy&UW_cPQ*h(i*NKD|Z zk||msOgy{(Bw}XmTNI)wwJJbu1!uif05Xm*Ybpegf(!@{?IE;QN6RhH;8WLL!IqH< z8c9db`USmNPsv8-u`D)4Z10>QC4eWIw!C?12XebrjubXpx6ejWl@{yBamyG>C1VLV z6Y>m<9euJJY}Hf?LwT=-K(Az+b_E8<%$9vH7dNP3ZB&h}7y0ne+bqZJUY z5-e&x+T^?fmD@hyufk-Z({RCoVp(`ZxTVqXb(4}=J*LU-)j4CeXmyX*Q?HXUQ%!U| z1fmkmS!5zAWtc9GWmZiZp@48drad(k#^|5}n)lCYYSQzjq(OlQkeGT=Mf;S4=_D^l z{0+wfFH=JVrmMZFG+@E<7A98824EJ!cuBor>*<6k+UIFkS4Ce5_J>&$)h+CANwMZrQ*IuG)tsa=q?vL9>;7PQ=y*2fb}gFu>MkxYxzs1w~_P72y7M2 zt|~1+4N66&LyCJegw6A&ki3XokVqc~}kx$mv&2IO)3P?zMpwL0* z1~KE@>|5w26NLSQ!|r%7eMs)a={K53odDB`wq_$PuO^j2V@;|f=Qwd8c$*=jd0kXM5%HELa-nb z1g<-YeFq=1VZzteVI2wuJ?n^5FpM)D2Nx7JNmU|e#yM3;(7_D~yrh7is%RJP1{PS* zoL97LrC+y*;A3LOJNB~ zQ_>&|@M!@~#|IHDQE2Uk7@b>fi+~eXy%vl#Wuwqv&~(yWl$4{Vo+4`*D4(#fnP^|~ zXF>3i-T^m#gPVZ#NfgK$Lly=Fu5%oZfD>uu+F&sy=Sdu=yJgCAj5#N8;hA!xuIK&6 zy{S?*(g95c22z|FW&YAyjh5PI`XU!_<9J`l)uJ#s_`Z93+Bn{C{&QPQA$SYM<8Z3N z7wphI@eR6iY4(y3LY)OGtgA2z*PEc#YDPm6%iJ!~np?N;2O(j=5sCbesVgaaG4{_% z!jX4<Xrg|{}S5;P@Wl2}js?15MoHiDXYT)0kA+XlLOik~nHT_W5fhD%B zy;-=mX&NQ?%4~#!Q7=1CMZXb*L(bA`oYaPaHrgPk8>}17<-J#6b`g20uM)<3Yqdwx zkQLY=)jfe5S-o*g!Er5kuqR}NyYs1nU*IG@=G5%RE6nMASL2>Zog6s!hx9^o%5*dF zdODp>Z_u^Ap)Rw_dIQDPA_**;$z49{c0edvg}c6W%;nlk0Ycj&>Y_y{@c?y7jfYI7 zLR|#bXjYU_aV)E!^}t((9haS6$T-o<{kD)(73*Aq_<4jD($Ht=9xCRnWP2$ceCh5{ z?i84%fq$m@trc%TTbmH43wn{OjLg1C(S!!Qtew+@RM8s}vP?bKrLy}~2;mIg5Ve~O&uNswM9YH{`=;Mc`0cBTCue}iBL7rCg*{J5Ts2@&*gfgoObf__Q!kZ)8se)^&G#F?AZ zn4tv1JhgxC!+^l1k95TgJ7m^4Y@+VRgj4`fO&(K}6S_|lOhaA*f1bKrMa)#dT*Ys@ z1SxH^Sz9WnF{|0njbK0^DD$M4BO`r&1uv0@|oQ2BdV9H zK8|4PK@I1nmQAzrq){R0)>0}TK*L{ z^(g88oj$%Plf{=xZ{%71l;~8!^p+UD4wM`z-3P0jfFG=KDZq3rnZuxK#>|kALrS8g zRQmn*-+P!3OG_LpwaTh1pnw1UcR5al0X6gv)5DUgv|8pS^Dbodm0G)BB~SV}?f5R(W^I|hD3kkA?PAC=sXK{qf;bdb zd0*Z8qgBQgtZ>O0w-snfW-3~K7SWj{y4++r<_j=F zbDui+Ianl#@?uN*KB{(ko0X*8(GJRi)4J6RB^fE|^!@wq5f2F&qo3;HZ^3Q+B$NV{ zpz(!|DxsLF9jlTV-F|@>cJv0pgNq7D$pU7yY6;kYVEQ_%rMZC&iVC}3%JzO*w3>$A z^ss-9CS#ee18y_3vO}ueBT{M5kyvWSU9+%BA$pyfkO8t<5^Gm=+MS8)#b7h$LDgM= zJpi7+p8mIh{_oS8O68h_+&&z?XdAca|MeHo>#qL4xmDkM{-FQg$FJzB`fZfN6H;11 z9VRboO;Ey^V0|%}aH5s~s-V-|cKf%J)_xl`TCJ1Tw)E%-Qq=JiK&IeJjS`asSf?sc ztl1K}rBtumE+Le^uXQ_3B#i7NuiN0?QF%AE)xAU3BHmu0I7;(g$nQc-5}v3hghpf} zKq2qxTdLib{&=#DyiNHi6S3rqKhG9_DjxrhFYw7&7rz>D@nPVprz`a&DNmG%6i*_KdJ`H3+ys%^>Av05vNW=9%c+*;@RGPB8TQaB+-6io9(7vx*3iw31@&|ihMKisU!&@;l1;dks!+5#Hf0SA zui=q~}Xrs+5^1HBT~9OU{&RN+-}Y{pa*Q z1j4Jg(MG*~L}#=oN1bU^p<=z2M!!S>Q(HG%b@31p z@6*B*ms?2=J*iryKtB=e(S7_Q*H#UDPsJl@Yl}l1371d;l z=U>Ql8y$S+Pl#nkBY@So<@b4_S$6*~PhX!Y0-RC)U4-+))ZE+De71O&qxd%R*>1at zt$sb({5M&le-q&>vFS)>Ep+9lyksu={-vezEdtqIeSZy!3~Af9@4#@SM1<;p`8EBS zT-P4tJuuy39qukH=kGB)e-n=LU=Dm~=5ybcehJwM%^r*T3O2gFt{~bsD_7oS0l|}1 zeQ%6ft(=G5am*6tvyh|CVf$i3{=cgK-BkZ>y)+f9>(`J?A+nuWjG@MPSW7})_2Z-v zR`;At?*i|fOjCHA!0Di_568eDBYCjhZXa%Mj9ywJCVdijWt4Q+r{f`sIh-JUY4ArP zgQS%e6;SQQ7f5jO!*xlo9MMzsWBtb;U(TKiNCX_=@RY^LJ25SWd1wGREEH7{W3f6M za)K%b(Bf-gcB8Vm-L%tc4p9YwUDkfw0 zy86S~$LcIW)t`i+DDI)E!0lqitlq?Mr(7zdw1XZ$UNht)FqoiO zWl@>?Loy{>Tu15~@faV=zn8qBy8Wi3lpMX7|2Xf$}4 zLLhO>;@bc~AyHpfjLBt86N3KbY-s+YjB0SkKPGp4pcm*HF*VH4ZzgO8@2K#>cs?~XR+oFC($_xx;nLKmqb2;L=Bn^B6Sj? zJHl7p9t);jxazHLCf3{yS+n{%j@i|;8xtIK6FMSnk|6X`z5X&&V2nBM4harO3}-u<#h8bO*->Y|wC&>9pFfJZ%>%ajMugbC* z4g#kaeP*p`@lnBOCo%PtN=J~MKtV;vtsrlNuPS&zcD6R2Z9T8o>o4^>ZBKgpY$TYs zhj4g_r`*t?O^G%;u&6R+E}$28Cox6;o8d`YY9KZyi7LcmFLG z{}qbKxoh~>ZSkL*FE%%`@n7|45Apx^@_UH?{}zE}8XNNS2w$CyNwk;Vi@J?Ap4Cf8 z-8wEynafp8d>Ov#$m__r(N?{_MEqJVAZ>p9+D1KF{>NDXY(F{yY#Vjw1k)EE^+vs3 zirFL~@O9DAZ`SKa+40x42XYn)vKZL#O8poze+J*8?GZ_^K<6@Oz3)CkYRPYboPYP# zs`(a;vz}0Z)EW10(Shao|AO+L4F)8>)8XF*&i}o5zLk~#Pq(%newEl+jiA5Q|BR6{nfU#k9QU+dl1;!4`6G_MOizqwB!MGC*j+oCGMq>YP9YM^C4S9%ivrM^C4S9>MFVN>h+A$NsLLKp2O=BLOxD#NF#h`PnOh}D*BrN zq29yR=8&l-9r|C4|I3SozRvld`sRy`7rFC4&o&jWpsht&i}v2 zo&SCMY~$I3{J)Ri|Dp50W)aU2%706<5!3d94_G#UP8W%8V#1h({2J^ciVMy*u;wm358jM}Jxq-^nU^_zJ ztCp{?Z;L-pqektKBq3br9+Tu-4Tba29fup@3e{0%fF1{!jNlrT{>#S?Q6P8zEhhit zgpWh2lYdo~Ob2_5{NJi?W%d8(^^FJle=ol|aycW_mN^*zWsl_lJ>}__&fL1B%3?k@ zt`bP44u_Pqmnh>Q+FSt3rMQQE&<`Nbx$2eLwAl51!Nzl+5fVlNu=uu9vct|vq(`QX zPHfv*=2UgKLF-r^NK6P%;91}rj#+479SB*#E~{75|D<|MrvHv(DBH}U3uq(nr`foZ z3(wE@0$8lV67MkkV5UUV^4`ub)!n;zJHHsJEN|z1x4pC%^Gh{+XGh|f>cF>_OAo*M z`Q1hT`@-=*&tE*x#s5Bi@sR)VUVe+~f9mp>J&^Ng<3S_5Lxhbyq#n{f@uz-B(2%in zbqlR^6NowVQ9@~130tUj?F7drwE?*ze(HR&G7%{Mg}06%9|4jH3Eq(~Wb&pkxct;= z)qqAr90ehXAwNMf8rMdIb3D+g1jaFGCuu^2`|ZHjRD$adO7Q*a81v`>I!gTH9*M!= z=G&0Ma?O|iZYIRM$(Fsk$Ej8gev^4#qU~>Lcg>qH-LHF`GSlQYnY5)^|E4DE*PaJG zwr?{JOSS&(%=fQ72{5p4GXsk^{q3C?Uwayy0e+k5SiTFc{kNF?5BFbwz4O0MbLW3H z>h%Zv|2}>X@gLtJ{^Rzj-5>ut;oo=lc|2b-{Cl|}-zob0zimHcL8Shl**3XT@b`bs zo=quE`UU=@x%_`I`G41&-{kMM_>YZe+4#?m7f&Dj|M&8{tN(xNoBIDJ5$#hFv>{Zj zX^ir5iJV(RmZ09KyPzvcb`LOj6<=}$sVl!U}l#N_?@rYU>Ykf7TKpZRC{%x5^w z)ko1y?&FX~gM!ZpjcC-8l*8q&*$7{yF9aFM%vXD-XJ-iw>3?84Pf6^PD1kiNFY0j5 zzEIA41Sk3$fs%evG*V=SbrOpulEj5+Kp|P7#&Y42G7&>QRPlJcv4M!(WfK~ERZ@k8}k0$j}y>&LD5(1`L)HJvXyMms-FF-W;IsR73I5EHQ84) zYCGlc=Zs^qF6En-*?a9tSY`@L&zHsND;vt|MdYX5>Ys>4$@8b^=H^w+0hGdrHcwTr z_G!FG8`(MlKL^OPu{gdCXbAm->mclG$*-~>UteD@4NqZ1A;SgdbT*T!dBOhkb2{Fi z#55Y5jYEbV5U;Wy7wCVsis*L^%&dNA=_$L0mm&&lxroaAI_#+y% z$2cZ(QIoiFMf^#^;@6w6YBRs(!SG41;?8M;6H>vKI3{K~tyi@I7{N5cBkH?wVlrZh z0im9XYL8;y@uYss=?p4k4KR|l2p3f zKnl5pO&>$zpP!PLvf%YrT~KrSY)!B9BR7NB3E?t}r~tI_s+N99ORL+TUT@Uv_4=!t ze$3!Ng0PWL#I5ALIaOJ>+6$R~Zg^J`Pe|&JSj;wJFRc5{#^cO(kTnAUa!WUM@AhcwHLtV){7fW56 z3X^!65}|m=SIJk)4@iN?e<%|C1>$?w`^DUgcTvU*#xQphXV-hst#iG2(5)+l1e%@G z-1|kSZ(*X%SN%M~MRSN(ezOPuH?_@e3PWU%_;`t1-0P2GtTwXK8YgAlec_ zb;1e6JVrj*i%CGDgyN7pOR%`eYhkXvs?GT5F0ah>wX+~XG}VG2T}pc;_dwco5Ru?4 z4qNI%=0%v2i;o$AfK$zCBrk57!D}^Be#j<)p09KfI0iO}f-$4foq8*4s_rQ;DP+jg zcoKyRX_PK#Qx#yRfKuSSiGNO#V?0_&zPQlSx&Yn+Wg!zM!{s55Wh~vR8dT5rwsA#y z!tdH&S=;Kd0{3|&$n}D1T#acwZ4;kGflGqLMV24NjHYaHKclHdBmBzrLVfCv)-eMQA_#OG9IQ|T^km>yL&e* zAj>&8?nX-U$O(O>W5XHBxARUEGjy{s1xt5|q$ij~!>ETj2@s1k-}ggGqC_R>0$6A~ ziN}l+ZY+K6?wHF4bWdh4_ZdAem|FAkJ~>1_zVz>`A=vBTujt5m@Z~n-X`${iQ61Kw)yPm1tr%cOz0_&lLEWV`6Qcjq6^&bOSCt}ancCMh(w8fEVzY7~&&(6S5v^f2|->2Mcq3 zzy)3XOKoZt;d1GHj@d8@zWn8OJZ7)WWvBKl?S0%8yDm+j|J_ zyd%ARd77I?cjz;>M!v?r$U`*ZgBSZR?#2ErxAi=hD%Jq1KvloHDu*=dDbjOP?zqer z-;4ui%k#+xxA)g{d*`Rc=+b~Uz#W5psi?^m@@)~5<`RSwNtCF=6r=JxG%Kk|YSBLSgc)Q*Hhwe+%b-r83Jpi7Pbfv+BP&ixzF; zmi+%4FE(Ff^1p6vJbk$T=U#r-Kt0|#X2HZynt`{CymurVb?p@(2zE~_jOwpaYeF|S z-ny*WC$MA6y>0YCQT(-`eEn6zJq>ZvXYokD8^<(C`pA34E04H`yz@zq#1Tn^d05@U zV|k4RCA|8}q(@Yjx^?dQoF0W&Itdtz})bF>3(!WgIqa`1ez3aap%8{~ysoj1yAj!=-u5 zT>k&``NoT_diMU8t@?`x{(m38mG2;ve-HCvskDL)LOkH5gTvjo-S<1*)RTW6?|6~? zv)z3!p31TWlK&p-Vke#M?&;}ab8q*}VPnVZ9`AO&Qfcq3)oL7fy1zDB?dHkxPI;qL z`mMEldfI4p+k36%X{XzHf7K0-cWXoJ@JC z{k{$L+wXQ;js5P=2d$I0XRSl&3MN7I-~E^k&f?I^mTWin&RWgR`|i&t&EsDh?@bL( z{7FocX;s~sGrQ)|ZoAWH71lL(=!k*3)sIj18{OuXzexZCbD_u7r!*4{g( zza+#wp*|-#_J@U?&eYBJJOf}>_gyHtF}P6Dfy{-ncX%e&YrD}oJDmYZxzF1qnT&56 z*>Q7kx7|5t9?l$GM19N?A$ynTndSuV8*QNZebT3qy7ZDpNNK-_kBLwF)JNQp={TvP z_K;0NbIUbcAPK?E2}6^JCZZ>h;81W*8WJ?&BtX3>q7kQqVFH)GkXRIltsJvug?+({ zkf2fyH}O>T}1cXtoiBQOkS5yiOI zqsh@9RoOYoLfmy!2&8#B9|Ro($`8~fHM|5p6is*A@7tZmQTP4skst<{AA5>Np=Xs5 zoU{w3B&NNI(AmAxKU=#7?muHZwFk3za@^T%9t)YG946D`HZ#oUcB%oZ(km&tihGq;Gk@E=M#$l%p{d z8VZYuU^j70{3H~71b7bjV?v^msMS@lcIwq@p6u-kobGsz1+=egdL=>g1;tYm5>0gj8qZKj-t9Jyg({fQ zONLlkOrGx3?*4wabE1hxEDpHQ7h#3R2>ue?!y#}@F-{yIaz%L}N@xzQ804ryKhs{(@bT@>kJ7pP?3)#{e&Y5r%y&pnM$S zX$|z+n!K0bhZgx`LSyjW@)h~cepRs_j&Kswt2HxydA#g17~v(3U}zdCnX#y8%2!V( zO8wQfbw_97@|B&bTS1DAag-i~K>^|YbpQG_4L9><}_(Dz_!cl-|!rf%TX(I~Bv)kxQ#{>n0 zQ(?yxHmG?(3KU3nCd3r~mQQOa%>+GWSM^pKh4MylwH6C7?%7;O*Ir$dt`M0p+T2Lm z3L95=G!DrQeDwuHuYB4%*+1LsfN=K^jhsNba!$~!<-+s|;CufLdj7U`O=hk3s(C7S zLaWhkzutazdeZ8=uI29O)4!K5-Cm^PY_aBVZCPE4*!87M{0HaZhy`RPkcz_m-sI)y zmqbcnN#f=Dl}hd9ur#A1S23Ojy@k6#IF>gY(mwitD*r)VIp3&!(AT8AE5K?e+yf-5KLR33iN78y{wb=Ru=#Uz=;vV}e^&23~7hgGEdwv9d%^!Srk zx+%39ZyW#o%ad63iA_<}Cts1Ks!y^cbX#*@j%cb+92&W;;m@Fo>Jvo-bJ{S=-mI~I z6|ACm9(FG1^Ufc*WFnVKJygbKmarbl;WUN z6%Z>S?vb#H+BEW|oEZ_$@qnNao(iK`y7dG-pS2DJr1OYfMtOl_yeB#8GL92>0BMEW zd`b5_l+=Zj9ZKrGafV`5xXrcYm748t>P{^JA)UaMN?<7zu7$OjzFc5rrnc?pHz2SFb zDK?Uv&!H7oaH9F%YMn^cp1F+7~Yt$x*;D|i6(9>RP7!q;m`3KhD)#Ns?sMX%9bPOz>CEnHKu*@QNYC=xawvLvj(7}7K){{=q6 ze}K)feO+I_`2a*(Uw>Leq^6o7(zFM#gRT~~z?TXPU=f;(2QdzaU>j$rZ(FjUZc3DDz_G(~o-Z!zJM zFxm0y(yUxTAse)57*3HVL5aB#b0BfOjXspEc0S2khkIS2ukImvQo}Ccq1UJukc(P0 z2}88``uh#^=bxt4@;^M|sQG}rvV8BMogL)WJ@hH-qOrSo02+x}0-}dSJrlni&UU== zs$F)?o9D2(z+nN1kL7C{#7*jN`c8VBa#ivUr+j1f`1WZKJx)-crlVvrx=@RwaKtx2 zv#rK~S)n7!{c1mC1Ik%Hfo0mmd{|59xa+f!#k`B7pex0J;8rDnFC;kL@jj9d^^sTd z-@tjkkL1JFk1wCSeEySo*CN6GBn;v8#>=e@@$NklJXgQkQpMiHbTCZP`WrvJ0H7=k zWYLk}Gu@C41|)t7`(5WUrt-Jf;Z_DJR^r zdP0&2#Y*Xo%H?CvjC_-s92V07jc};>vR78#J$=7s%#*z#j(5tdpy+tz>r9pH`u4_+ zhyFzP@*F)@vDYYJlku3utL6F{x_P|jvU`%8^wS1&05?>`scO*lq3l-mBsPTF#mB@) zIOGg*JcX^UL?}R92H5ln3Q0eyBrzS0NB~NJ49?*wVo55hgeXBZP46jT@WMwmv~ z3M#I-GaVN<`Ydge4@nrBHdmmdsJX8Pj>iz^D|mUrR9F(XyC%vv_h?=5)9bUp|iEFfk)qzH&P18f|0Wy;Dn$ajl>LIviO{%fcE<&CQ%Yj(~R4f zLy`=I=>aO>+vBs^oA!Q*@~)`g<&*K4#R&;^>LmbJsi;Goh3Z4T+J?|Y-yWZ7N-5`x ztU1$8N5Xa=jScERbbIA%3FanML1t_cl~-jyC+LSq2c1Wq>Z7gW|0gMpgq+-7zFx1Z z@_j0?X=|0Rc2uaE6xY1Q$25vaTq||OU#n{+*>m-J$E*GM@nZ%4C#ULiNJE0Q;qQRS zrdW)qAhh2(`2*F_i{I_9$l0H|R)Dw5)G&p#1c{LMvA$7H-}8`=G1^c7Ba;4cQ!4Qx zyKGB%2IVWLo!~eD+E(9WXJLHkdsXNNpA&=y$q>O`X~NNI%pjjyb04jqHuu+Lwf1TA zxO>{%-zk5ms{TU5Q2K5pcx~Q8GQe;h(Z~-c5QZWc54!}9b5e!cNsPzHI~NctKcTV) z?rGF*Jo_DeZ!bM-Jjjk6iw`|^mQ=X#L@ywN(CGVUL7T9brW&54?#Ineq)+&682@Ff|wRG8Eemm zRKNLJN)ctNQB)ZuXd_1vITWGqUl(IV*SN$qQN`WT7C8vBsFAT-+?~!;ncwiT3QzfZ zv*HQW##8AT(w*SFljBa~pPf8qs}DQnRUdr#hB)P#%$ooiz=tPqyKfrrc7JW2v>@i_ zsL|QYH&Xz}E3f7>V5z08IbYO9Qd!kgW{bmaBo5k**00Sy6W$GM#LJZFw08GoTzT%x zyH;a&f5#KzFa4G&q_EXJY#ujK^~4}oDkBo6=rf@cA!9C$S^1#^60!&w-m zhTE?p`?V#?-4BqyRpQ@yuOxB?icvyP*&b8+Bv6zA1X{-$s-OktSDA)-c}iK)G~16A zr6>8Y(-uvI1!=6}n2jXu%NJGjn~XyC8AL1~8ue*J6M{m<##MC2iDd5yLjmbc1|Tv! z?0|-ac18Jm!;o3}DZ%l8Kp6K<0adgb`^}b^t&?NT!kPz4lMV@@TnuK6IVZt7!aWuz z89GbBFJ@5#^i<#M5y9`{i5NV(AaRdzf<`Qm5ghOb<#P1G-VWI%iE|zC%GbX(T5nF; zjjpm6zS}{@U5-AX@4uf}3hq^(Uj$muk4`l$3QGM1%q;=y&p$I?+Rekm_jw2z99ekM zd~OBp#YBn|JtIQqqJR;Y8E}a^&JNHP(@D9RJ1eq@giZV*vSzYS%M*xlr&1{c6s&?k zsLtwxKOC_DJ%3tX45+Gg$OhV4;TaYCV(`kzsDVnwdSHorHD5UrIBxs~ zOhHanx@wA4P0wKhMN$!Y<^ON409d?z$ntxQzS!kh0yEopU+~(+GF?ik;6qZ_cf+Q9J6#O z7x&eH3T_uK2Z{(djVqd<&jkj95`Z=Y+)ErsOM(1tw{@KF9+(x&fH^+tH1c42SSb2n z&lq3{O+unQ@q(CB1G++l^|ZD1VPjOjhH^LXSRbWUwgpyh9W4s@$t^#mn?$#jEbPpr z_gVd!+j>!hg3Vc;v(Fs7=N8wU@$L!*Og)mcNPsdL5{<@_WXJWtR0#SA6TKxS9!lJN zR5M??5rE#tghb1M+MEGOdgPabwN(U5$+a#HfSURX+{+bM6tqs5wM(tBCevHY5FE)ce`FVT>M zV~!>q#ri4%?c}~Bj{_93cmzlqQh9tSk@Z1;u1Y&~Md_Ts5qUaqDo+`BWl_mn)5RPkte-c(RzUmvfGU9~Ai?P-N9O^JQDuzG2l4i!Rc}>| zN2pHCljvzBD68YpUhc6kU0a zB6f)&ha1llmMWvCM?uszV*+~+J~3|fNkp%zJi-1sx$=iN8W7kHH=&ZXji=9^KijBH z#2T&$uoWBym4uK=!YXd%c)^o^O%f=A#mkClzCxo)G9(p_N2HR_k<&>qL0_ZS zs|g{fg3}pWQ#*@8;-8Bd#b`h;2t*pHG$N-vvowtE z+(oL3#XbqOT~Xjd1;3AvxoFecewDz4#g86;*oIx!?N5)z-%vsPy_s@~)K>}7E!Q?s zVblFDDqlN;yFs78F=S{WmOhCke(@>paR_g*by^W6%n6UCg4#XmbjRqeJku{0P)mZXoEkM=IgIu7l z>!T7OZ&qZEH6WF7y1c1|wCEZw7bKL)uUJnnSzBX>P}wd7`xTNO?ny3p$c(SVT8Td& zbag!9yOX2Fd{3FO$_reoIXG~ZHDrUD;<43`4Pi=R`=b9`;7KyjRUxt`w5g}0=i z4%ng$i<2^6?U@UH{TNa=aD-!m#&oR3z2)m}Cdd~oRcN`+`P(#z3jEv&iAOX7M`uCo zf-rpmc4@_^B`iV~s--m20z&O7WOM^-E^`rgzUKSja-BpnPIN#Lc~l#gnDo0!FQpr8 zmnnCbXuI!bMB9Oj#RHLVBGF(s7211`N8vjXj!9fa?Q=SYs7-+mjR`oEoZ+{1hk!GE z*O>%-LqyWw(rq_7ZqKjZ?;agWl2yJj=~&B;LHll6X{QkEg@OhQwU+7rOQ-+yY1_Cb z^WQ{|^VBc#ohdDPJBjcGrlDA}cgFSSF%tM+j>k#JGR^?BTK_f`OW1C7Uiw3eH4pvsszB-mi zq6-?c2*R>8>2qnxN&%G&(a#56ghQqujd~>D{HiU3Q~^6D(=??|sybn=tzNIRvLfm# z4N7-qTfN?y@e|*EK0uA49^X<=R?TD$L;2q}EQKNCiCaS*1}Ur|$0Qpmso^%Fi;QdO z=YvLcu~S}!87I+&4r=(9KyFSgO4X5^FfX94Ms#tjyp$paC+O8H)HwdN+wSbPIwnxM zG!u6-5k9j<0d%CoYe_tY zPR2xbi&y!uK{_2$lH8oH^b?F z$cbL9b_+)a^_m=1=Bbbk04~WWwemoKTH~LD*!)n|l<>){_uU}K)DvoucBiEDv!|V? ztBRo>p1f_Hbap$9y_4gEl5R)QY}qU=X?9S~X*6Kx@T7WM&R|ql%+4pgjaWkaWHe4B zI2H!Nj43;7R5BQx5OO8s5y({{m>i3cz`yw{U6`oy=zZnUsPahW=78(d%GW9KZ%Q{M zcZ6%ma_E(>Eu0A51<5zkHnPc0@HnQKN0Yyqw(*FZ@mvs~)O^H;ESHk;cJQgWzf)eN z0jf;aiVGa$5z$4WHA`%k9VmjJ%4v&QH7#c#<%-QpkTNAa-D9@JgDDX)&+=CYuD=(v z=x5eDVDX_|z{|0Gy?}cxPQ)NnI?w^tnv+6F!Bti%>nb;L1MDaC*pFG1;NIgkY4(Vb z$XFbye2dRHc%J2wF>c&B@<$yIeaA*Q5oC#`P3OJD&dDW2@|L#@z5Ygw7Chku;#5_z zHSMD@FSy*V2b;C!kunFGktPr96*&Xw;5mv29Wc3M@4uli5xwZZ585*)l z%ri95j1m%G;Bd!#_6%)qp(pTP*&n%@NSc`Qhy{WJ7W3R}_sq6Qvu|PoQ&156w~j8s zB`&Zl2(B`L{WRF_icvg?BDuo_QPwgFT@aoq4*PndXnLe&9McOLl7W(IDz?Y%y;OH1 zmM4@|#jH>z%r6fNbqb-tTR6=HFO|mfO-AA3_>x9*e2<4#hq7w=5bLmH5}8qupo*Ns z=_4G$_RDM^UuGUAQhNkV!kGg3VR7uM@?Q+$gP2XmLa)7kx?NGdxp5~%G7zZ>m9L*! z#WEzdH=_>-bIGnhsG6VFWxMD@S+)OZJv|iCIp9;C@ zG$Q1@#H9F3q0qCHl=8KWn43a{VV;UWOq zK9+pPXS+KKdtx3we}-n5+(neV1V(OaS=jnxJmI8qMf{1Q3C)RXpK;v9lA{nQ8WVhe z%4lRoucA!SjkHYh*_Uj7gEyi)gcq4(&gg>$MWJ0u8+bty)0_%Mp`V zVrpZ3xy||fzC29t)tJQz+S~2y9yX76yC-Lz9m(ufsT)rnUYLyAAS@5rfZurtOT{_a z3;|_|SCx59qrtqqOwK{YY&4EKw5WZ6q2#&hSiGv9!Z`gXZBHVfcU1Fk~Qi6VgwDEA0@FN2y=Za}#@c*`MWBQkd=mnd&Z z-K`1VcFm=cnQaqbBO5#ARmqn4ALy}6R`~JH^GT14S->Bo+Bjx@ZKLLc@}wMC<$tVY z;cPl^OlJ%PLF=^csr77oFh@_ExdSNh1SRb>}RbWtE1(}WuuhdJJy zDRQh803^_D($<}|8?Bw6e)_3&{I+@gPx-h}f2tnewocAYcQ)!TO1kJqy{a?P`+ODt*62qliw&OtF#`&xCv`hDbm6Omesl4$AAs)azT3IFX z+5vTApViKOfg`K3k@p2`&S|M~;qFk&1@s+SMP-Qs(TduBLu<35@!GN|jsk@wK~$;t z5IBUwcTS<&C}`h4zI{`$JYjc!B-Iq;8O(bsP`-7%|n&WcR(*lq!zk$R05(; zc|s$p3YiaD;Za=kl7_Npy-6Zt&W=wy$V>&?d6z=bL1|LD1yT}bH%K)l@?&X z_Fa+`J#`7vAqFvgAyKxrD~2F$JZGf=ZSmNbCZ&87P>YsbL133uFoG0_AKuGFv#OTjE=?mn_F1SW}J zSP#xHIpEbT{MD;bSXd*6?pw-dkUzhVvl`DeDv#LIzJ04H3Qo5gc;bicx+bU2qLq*1SY($PsRTwA3{j_3AfR2&Ex%( z-`eOO;_aTXzee~9u`!7hvx4Ng;!$m95*Zw=4iU+Cv=V4FXNPnoSV=}D8`0;?TTRZ_E#YUX*4+Eg98@p z_MXlxVYl@x-9La`8NdkS{&JC#;sFv4a@P$nBWBzIuTG#Oszh$SfIV3_i*d?LbKV;9(L$TO_dfUlA}=2@@f?-N~7mQ|4Nuy+Df*KQls6HUsp=vyN|?@CB)^ z9N{YzO-4PL8`DBUQKtoFLU#F`OFCL|Q6@STiQN z3R$UzXpL1~dFz16cR4qqyA$SuwpOZHPLo@fQ@T>~OUl6WY|qoIG7By(t3q~pS)V?0 ziA~%kW&nSSWo9=%d#PEazUXo@a6fCw-IPj3Wbq8|#^domPB5G?c28V_ze|P}+B%=~ zoADj52Z5ZZ$5_H4RU+I8LK0uPN-kM^t{A6AY}yteQ>-uxC%ch75KBqUnK#nwBLopN z$&+~ECzF_X$gvEJ!kG?5>x_1@#2s-g;0h*0@jp-jpsOD&Y| zcO1mjbaC;Q-SgaT2k617Y%x6~0W=>XGEdbbp23P7oq zr_bS(62g-f!NG4rr#U6Dx~(mzM+7wPCaeppMkar~=)ew~)iO&;=-+mY8%dv5Ums@* zJT~sYbagqhU39c759_-%YvM~~oOay=XTr_jCVHCoRrWl>69^GD5O2^%HpDM?)K&uH z6W=E!AVC#%h>XUI2@a~&s=Wk!oVegE=tU{tM(RMPsc;LQ+A(Bg&8X{>a*H`en%X72 zK|4D@7fT$z&Tjq4bQ}VoDMuJ$4mhdFi`T7rk&!7%Oinn7Vey$;kH6yL`~1~6>C&Jm z+M5Plp2RyI6^)8QH=e7<>SdB5DBHPP5C&W4WW|D5Eg{(_+&f3sh$kKUM zxH`<9UD6c{cc;86{zjEUA9-djx6z|$-5$y|dW6>@CLrEMkIq!Y&o+AW*2{L+PDLtl z_!)V3yd1DB`u9&7ZE)E@fao}8V-nvw33;FfAq8KL*aWsy(={-HWG(oDEHm?81uYg) z?9W}IJsd35L$f0+J9(O^k_C-pf(DEQYIEW&p&?DC+PRjBGW$j|aL(9nJUA5U57}je z`UEGiEyPPR<5 zBn=Gmhcryr1O>}}jAL*M_NIuBu`eTIx3(psFNaLW*NBnARd2c{RdpXd4x!4^Z52|t zXr0+{wll(PohaKCQ4H=UD6Oc0xAZu{6=UD!EqCWtR9UK_>YZWQ^N=UT+l9-vkOu>i z52ZY8*Do3~mgLlBF)Kp5c6DXZ#7=q3dIJrq(8p`>`NsBK^e15mo+vG6^nQeDvp7|d z6N;6V^i%`z7qolhW#Q^>7XvokNx`NHuFw=gyEQNzm#0-%|0y)uK)}#toQ|7PGkT8f zlidr^@K+&D$9FF)Y4Tf^fVS?JfKnSUnYa#bhO4e7DhvZaG`e8t zB#^xFLs>yqA+*{YQ*X5?X8HMb8~U_kl916DG#lI8F!iChZL*}0XKwFEAVI1_d`PQ( z@-n0g;nPkSQ5w6vl?_UUqYG!v{n}d@EUgn*OOXL#zUiq&d$UnT`YDf#dTXNo@)>y= zI1Z`I&_+3&)Ipc9c$Bag_#^?^P$3X~u-K~Tm|coTNNaY96`!X-VHSoiKF|klq6nL}G5M z2lN6YYiWmUps8JoCz}DJmz$|9R)aJEbjKXRn|1o3Ei78j1&5#7JyML+Cc!E~CsKj` zAca74|7KeWf#&|rCws?LhA*~RKRKk)`EC^K8F`Yo?TT33d>S_=Cvy1l%1`ANw+9Xj z%1h>yJ9%bksf5!HHfm>yI@v73h_k-XW}@>9E^Zya+K)ZV@Z(&HC)k}55O>O}f~ZAX zDqz4=%iB_LpwI-)O-E46o@JTOPcilndKXNzb7S4wCw&$LZqllk)gLs{2JWxUBlnHTcPsUs7Ixus_0tN6=G;SBFrSvlu3 z{DL%odKJUQu(N>sYD>6er7NzW{gdOz9kjc@j}A}XzHJ`A&7?e&OI|=LUh{nu+2Aa9 zGiS@MbUgxP5*RzvNn5!pH#Z8?BQoEjeg%fkS;SZg#jbJ#bdM>pbT=)eqd`>Xol$=^Jn7WBJBiSkc!*u=*^J8vr z`jeF}6fTZXrNx3Q+Q(K+Zz#<=LWCLC`lR!-2dR3-{E6xFur5*`TNpDd-SG~3<7lfB);cPH(Rv{htw zVzF{+5%aQY3}xK`Nt*mnOx%$1WV!jao7-{Ysx9`>hJiU6ky;X`IEFC(4`r+1CudeJ z3AmmEf4t9T`!S3JtVD}3Lt20k!hAUp@2V6X2sW0C?W>N0GM-6?G zt{-#cGixD@ z+X+r4Tr82k4Aw;?sv9ZD;cMoeNSV0OfKUx;P^v`?IU8mi@3nhf+%kLgX=51`gsMf3X z>V~&w`v)^%bV57(_08-HJFWYG-5PQ|!hMoTT>&*@$l?STgbb)!os9KaIC)Wez>;On zz9JrzxqV3V&q5Gio6)_#K$zV-u1?lrjz$xI2sbLIq-vkrP3!9Jf=;f@^zW(2x_OQw z*b%igG23AW!4<)VeDMib?6QN>k9SULz zE=2yp(>Wh2kKR=t9aSE+6=kGx_o~0CGpZ`vsg|!bI5oN{;pk8FQJw~J9X{7CJY!Nw zf9Qcs6OlIY3C2PKG08l2fST%`%rVOB6((?d3A-S1KV+Ag+e@C-H@AM=s@I0>vXZch z&qiY$lS*>QDpEh^6&h6nmQ+j-fWBkBB6DPl?>)?i0@Snm^JgpS-xB3r8Ln#9gO52u zUir;#`(5|fMyuUCIrb1uCv>a2QGK%Ju+7umPN&g2-g*4-UmrizD?few^ux#UW2eZ= zm*#O+<$sB)pMEH=NI3S5TMLY?o&QB;RTq8wB+x2_t3b`Ka`4>AQ1JnkSA;m<6vbhw zj+sxwlsAiuJbf=u!2FS}1SCi%s>y@z?CCdu2C3mGR1N z)bpl#-c=_M-*|=H&5uO_@ndN0rMVO$Y?w zRQfR+RWMLq#qy2`U3rXf7^0A1Nc;=iqA{jmPC{LN2X!;1ql_AOfvp_0I-!VZHSH<# zhd9Q*5I9g&*1NvI@rwNC(W4LFeb|Pw+n+u?x$$l?Fir0l0s2V6UU_0eH9%9DdSSHV zgp9^4h7@J`8V!9MGS<6NG(o%V-P6*Fz7HQV3aa~*q5#EY9EyG=T(Zw*&$a+ zcSHv=cSU!|SZ=c|CIfPn_Z!Gh21s^Kqc0PSY!&)zEr))3Wz<|}ZC*AmldXl5KA{*fPp$_4fQf4Zl;op(EFNsGQp_LVnZdrD^S$WQ-p_v3G! z4aaXa^JX`Q5|T)|J=vLBmxYCko0bhL{N7~4lCJkoj!sY7jl%9^zs>H8Thi``$~60% zyc<^AZo-c2h0H%kxSv3vZR z^rPyCZ1WG%tfzbOqbK_olVFGw)4_%MBb)rOsL2;|ntbuuO%{&w`J5)7e|D44Gfm#Q zLer&_M9E}9=x4I9eZ%eS*dAsgOj%#2 zUVxmXeAXQruoxf=);W!nUfC?Kmp~PCu-n-^w93ouR#Lp?Gi!gZP7GwO+fdcvsI~e# zJz21_qR)iDfY@aOJb7gWy@mZJUT~p?exWnT5+{-D@k!@h^Z2cfc{*u}4j*A3y!rxT zGQI6xLcbXAOvl9C3b%EOYgypNa~S>1eXO&HPsTz^A^|#STbH&%H)p3h&KQ6*&ZNWP zMH*ON<+;P`y_wl^=2BYt>NHTG<)!=F&FBJ$G=Mebo68IIJn4ober+2_@Sn?>&+b96Kby_`*i!GGj&|u>F&)MDlor_s!!0P!R+epOsH}1$b;pA915mMf8F?W zg8f`(RGF*yVGml@(?ZR2pnQq8x&Cs(Mp31@S4zz&;f8Y`duS zPcyP*-kP1Fn0rIcUWa|l{rsBZh!u2va-1I3{_^Zx@>`SUT%B^Os3CB)kzwWNc3!T} zp?p}a7Tn9ltqeWFUDMwR;sYV%>E`)7lic)$Wa`IBoNG9Cv#udr>IYk58p$6 z1X%>~F~x)rwFZS)3};GxJmHWN1HvERWU4TNzJhM`-$INJ1b8n5SZOUogSVDmj0|-$ zeHsTyx&@V}GG3j`4Pd8?Cxdu4xu~TN*~cVK(?h>3MwoN@|Fick@NpGY<85vK)`g#< zAP9&I+oefLvzw$ZNLiXRk2W+fk`yRy8g6zc$tAn@F8AKegET<>6hs8|qY9!x!M7-g zsJtnPpnxFC%Lf8Q1>`MKC?N3v&FkKoxpViCM+<&krP;lA=FFKh=bSln=FFLXLwOim zmIlVXjKA@Fn)dv-=AQc6uG*fCwzjT`v%Ko;x{X&Am@KTTID0}dmm)CtmkL|o=|~A| zJ5ChjU>inRznkJ21vpV4OyV082xss|kCl~UNzKuy-&m;+(%4bBO#ekA5!=%i=v z1q~nECvqlO1X(RV=%#rM!c)l4t^K+&vU`}Br0Ac1KE_>~FcV8y7Q?@C)k0a<*JL2W z`jK-VdW_0lg-|e?`cWqw%E7J{Ax{T$Hl#G$(G#6G)?oKAX+v{uXIE3*E?`rkvlC`l zAHKyftiN)hEbFhCN7btEM2~q5TdZzXp-oLbOgmsw-%XYQxzc3%xfFMC!rUog zSqyK=RSV@zUz33>=||3e=<%fRyD+x&V@{Zo163{jnhw@%F^zt6_pm0uUZ;NLE?`dF z$q6&356@!Q(_gtz2KCpBrObX?kxfTYk{tC)_7+x}l~ZxQs`uYWac4zMwXlL>$DTbTyg{*BJ#mzRqAsPr@X7s6*S0nryRZs4kBig_y&7H zWXvVjcyvJ|4BR3VrjVW-Z;rG0nH8YifS>|!8HXSUZ!n}r_!hiZ>@taCj`6pM8{osr zH|0lsQxUCh>R+ztCt>nV=y-f>8mUqK4lWxy)T_!knOwnT=}9Re*A%hz9;Wp(ZFRvs zO7hveY^qF(#4gq5;Df@r)Qeqan%Cul9P>58bpE#?|{H5N&B{av~0>Gf$3_RtHAROTu4}1QfpH8K08T z4fkT(jAq+FV`paYRvOe2Ebos$Q#opZ-ho%`CYWm=H;IGrXc&Y?!8%}o)^JFEE9XbU zxX_pK#W(0xEy6lp8mZzVzWdCqk)#QlkQEI{{U)QyNWu|BI(*xVTYr5`_jq_YO^Qup z`lUMYIEVpjY>EK`rH+Z(WJWy>%(3-!CPl7nwxJCg48(&R z3xJY-=&ZG&RYzciYb|ocejkMRh-oUD7#v*z+Ox*sVVE}zunx4MZ{d=VP?t@+>|%1* zZ1%U|%x{#+TC&H{)6mR1@yHnev%0(@xd7O=^XAn4UtQcc$Y@8{OrvtYN%`u{hZLoR%0!o8)kYTXFJN zhzd?YYz~m?fP`gNXdX*@ZNXEsx`kF)nloUdZq#NnBx5qco<`EwH+96lyDcLXW&F$4 zZ7mJ)aHKSm6feLW!`UD@+tzl}k(G2A&Kr;!PSU$DYuno~ZV)O%>IqjxmZTbMnQf=`8_IMTquX9LMMfYrR} zxEyIt;2dTCds>i|TL?1jhIuZJAiZz}FW&L3EWD4$FC;wq40j77Zskv$8i7m}lLB&#f??^gT737-TLg$j@`oQG#Py+f=h;mvsehf zy5w{lSq)QZR4IzuQJhO`X%tbrV>dXNHwDTw4^%2%wL*_q|g)3CRaSAwIfb2;9?|QcHme#9D+zjk0(nwQHLUU zV%BT%7_)Fz*-e-%oKjBn_ZtWvm@x+3OAhY!j=l5Uor&g0vX;YlSVhKyM}p-&>W7Zk zL$Td(?)pMrYS!s7T;TZH=Cy2y_?zh2No60$xH0Z5{WJD1oj`7U4M-wHEVYJo#u;Uu z##fQKJpgb(kH4U!o1+h01iF{KNW`IYP|pM)z&Xx*yy&wn((WA-?6r7ZESeh{j3FS# zd%8Hef$ zG^APLty2-H%^(_y{lk}`g(Tkck0Uq5`U%nxow}^;m}z=jEzW-w*nC@aJxAWcMgX7w zT@UDM5klSp^4w(92yE|YYe9<{3Ib<{MWQ!e*hmN>6u`tCZEaoydAXSHke`eBw}guY z=sX)w2{y&bR3J9*pCO%`fE6KR-1+l<=umm_8Q1w#DcygO_8t){Rm22pxTB(^Mg zJAvep35%0?)=0vMP3M%8l-3A)=Vec!PjLPe#{wnmtFxI5w5q#H(D#%%OulpJf6GLE z@&Ftb2Vu=}^gfN;HB2%_N(J~71nLqamNu%o4#X2uA4vC_hE|Nh2`*6P(jq*l)$PCzLn0MBrqSk z@ljFWaR6dcwvp*?*%JjM1(lVOaa9cd@~}>Z7J;x7v>>>a7S(`{gC>D&Fs#Zltc*Zp zEC(~Lujw8KIL9w4Shu#LqoK8nr6Wdfl;#Dwe=O#tGcl~hmIoRiG^a1e5KQb#ef=r( z7W4GrKeysXMuiW*aPV5KS2GM~bvl~mfN(u@V8Ve(9uHsh3xRIvj;>)Zf4VS}mPvx5 zpFa3*&vl`X^mL`7K_V2~yzb4<0jf`>+$!@aH$nRuaG1N{OwfB~D#^WCEU82s?9gP? zn~6c}eO{t^9}h>C2CLFI@6RSTABeiqosiTYEzLiq7|#k8xuh#VKv6HO?1>E&CuA5%a!JxhzdwhKl6EDPp9;$9sbA zQ1+2^!sO&%#s1T-btF+ybC|F)n;64O6eLu)`AdP=Xi3u1YPyCC=@w1I?4t(PF$HwY znMHsWh#HlQA~DsIYE4+UX&2L&xV+3gP#o#px!k#cE(HMB{a0h z0)vpbNJ!oVjQt=S#nBD89v5}AemywMdmld?Y?v2afQZV1EyxS*fwxEF0vUt|`BHk7 zfm_SxkM!D!OBz5al0%-_W{u9DPuyg2$I3$VtJi|sV6Z|Uh4DU=ECfl@fZ%8^L_>5| zu>41a*HU(2`J3I0yZo@vW5=&*_&!NG*eb_)Rq_>x>+$vaWL#x(E zuCmN*rqMFfNDFbj3B0`B=O9d(_{KOKlG4-F)>F5-p>7S~eSF@UdnWi1F-T!OgoKxz zC}C%th!+46Mi!LjwpBfiP0bBGt+g!;an8ww(81++nsOCAw-*E9_j#tw{#ay8XhP$a-Jm+5C?FU2)`a|Qw9K`m9j+UlQVhrU314m#2%s&PkQ|dqn z%C(@vWyH%)4_#=^02m&C7NjvncmtoM1)}$-+tBHB<7n{4wL9{>pOVGshyw?mM2rKS zW6TcAKNxH(;c;cL;{;hetKeR(kBrIK2@wL6-JwtBy&Lj_F3Xmu0yjE9m<(j)CdCYR zJ2eRj55QfwJ=HO1q8b7G&EAxc(t}Ce0_7R$ouu10ZSwW6DCIoyhym4J{KWU}sQ|jC z16OJ`Otu+KhV5vn6j?9u6e6Ll!Nv2l%iX9SsoiSXVqr zGtIhLKG`WDO|pl3Cpq08qLYoMS#XdmVA0uGJqh~tBslu$qgf`&1Sr9IC`(wNDA{5X zh>SF2R!`E#MRKEe{rDY;+s0R15y3`+??d_x&iHGYt8fv?mT33ONhU3;Y zbu`q2`lgPCx~{g4riRYxuw+yQap5K;p0+8)a!3+M)H48k|>?|0zLIp|zCAqa=aeNmLM_1gS$1*fGT%pO)#2))RweRh<7<3YauEi$7 zHa>Bk-sW7{o!(}Af;_#=xl%p7&2S=KTtn_M9j^qS;o#JoAgyJ9ZUb^7Ej^{6Wg75n z$(+G;_(MBbe(+PW09z1_hZnGjWp;58wc**V+2|3zGK)J(K^$5n{s$lE)hr`~{%PtX z4;6NmD)^cU%2uqULO#D}E*G~yz*skM18jLXu}EWi*(0nyycLql??e|yXcSXsf1Q~b zCHJ)Zj`vWHx=5Rp+27&@oD0-JJ@sBK5(Zc8^!4qDc)(dcm)uTWwie@>re|vsW@Z#0 zuY`tUlOQyDipnuT655V#kf=&jur8hSbsiV?-ROr;kNKi^%p~+(9Y*((0Sm$=ws9KY zxx-xpX6UD~%aGjB%YbrXIuazt!?6Xxy{vjX978>qw>ywrs5fK?PW(9Y6b*M00Dak1 z3K1-$bM~c7!SyamEQ<`qsXNTy;)ZEI0T?Pf*v`O&-lr%0B+B&hB-*tEVjBhpQzKsm zRo63`<3P(mbH1Ig`@5Jy@Kq)NXY#C^I=f+M7)Uf*Y2n<~`y3?Tx z$33P*RUi#5N6qR+KURTlMja`QFJPM*Z73`!$^DmnBQ`%`^s={rOE8&|4iI?kUNe)* zJ1Mz+KbIaZD@ybg4IF%XntIYeuO>NZ_$G+-5ZyCZ_OM_ME_{J7|0Kfzp>EoCMVLDi%n?nWmaLMhX%?_CH*p$!zJ&rOD7$4@xBF; z453h+MiE46jec!2CauyG>@sz_ZDO2-KAbEBDn<$*c#k4K5WmkOzj|fzn@hve>zRd} zZ9;FY*EUmGN4Yjbe5hKRkrwb9>8K754UrXWh%HdulIM33UEHT;Y*q3dMwZaN)0^0B(C~pkM!=Pj=W9fziB9-GMwB=5GU2mbw z@~LRM3b#EEbdks;p0Xjd2JV2inHtKmX{F+VBst?G4|O3f>j~`eEmE^dS#@)GFi=4;`HkE=yE6sxoL9Am>> zvMmk(3&Dv8NSS-Wn-=tUk(DSFc*Cnvrj)TE?>1=`FhoN{a&S3NIf8t*_2V>-IZOn# z->6iSa!*Lg0%FulCE@hdkOJ`ngQCb>>ZY^RZIU9A`RLX&xZNzR2wR&L=6DLak6w{* zK#3Tic7Pz(MCK6Mx62vgn5jYvBPNxnrp?nV!b4p7c*N59jwiJMsR$@LrOb$dV8 zFNiPbW|x|q7SPEB^=>=kLZYrgILaLl)N?52GKux&T8XTK76w)2pgajm;#~RlLi8P0 z&S(zSjV$z)aRc&{r6v@4H>vP(SIlW3C6)AgX!T-6f7qp zG<3*Q1MqCg(TTW0+W7_+q;PNG78hFcfMug;f#5jDtYYB3^!)Trb0=6&E!e zk8(iBs%ixX6!|3ctgaZ&?P^@S;BhV#+SP~MEZS(b)_@a|CGo*>Z(60P2vI32gzawB z1omC3c3qlv+li|tVAbTyJD0Q!=Q7E6P8|0T;}FGO$1n)9#=|rz3!OM9M)^|Duz~?b zqBIfF@Cz1u(caNk*U;J7)Vj(G=LBtypuMB5y`iIP9cXN7WlqilpI4e3{(iOX41Y8!V#4U??ZVeYH4VQQ+W8&>Vs z&7aRbqtkzP?kSS2^$~*JLccJ>BDWg=C=PyjLg)!7+cpervs+YvIEV|YssSdzL``8$ z!?K(YXysV9avA(}8z*svh9hfGNfQVp70MPp)@cn*735#v2%`^@|DXXD205)|g2$3c zn7er$NoyJLJTWI@Y7THWw|PT7-vG0Zyhh>H&?18htmy-#JUs+lQyZE4q>G}_Uto?0 z-EdQ=l3o3U(r=(gp||E?eadUaxYL%h3pr$9ShK~+D!(jX-!mby&yR)iK&xFGB+-Sv zT?%JZ+RPdbS+xhc3PU{R1QO>U^>AJv8DroOp3H5!(2McY9sZDl8wBoNrA)RuCArDo_`tSWC)8MaLA4X0sa}B<=XlBhv1Y3Eyd<<68>IMg(ukHw(lG z=o20)augYFWQbd{-ujm*{SJfxzT#rQ-GmahIWwkiIP?w-cgGC1A~>_sQbV5fl0g?sebFE;Ze$k_QZAE9+fm{m6U6}I3bDjg^>BP5Em_x zyIBkZH=PUO{dN)`*Fq!7rdR}gyUO9n*kYFNc+#T5c8OHt;Vu2tA|j-}`0o-E#k|FO zJCV7It^p@Lf2CwZVtmMDrF`>PfGs?A;x<8%1L7jwCwg+%3Kc{jljKxcQY#B`YcS7- z=Xl4rcujJ$t&Xr(2N1hC)PjO@Y2exVU_uldwCih96$I<2(Ijy3hsp{#1O=ShehY9C zNT?#*cQG0?EEY4Tt$iJcj4_zwywAU2PEx^a zsqL!67wmGt3uKCXC~_x6L0wyGS3_%;8e`Q^PYG}~fVwec4no8=jadUjENBE>BcMuY z1h8nnVusakdlwd(T<{p@be%`WQ2UM82|FJ3G)HdVxyqsmbfgCwSb6*D3wbMu2}Nhk zgovp;OEWpJUO>H>(eagH3Jv8cFwICZ1;wTFFf*f6OR+=SGShma>C2rdC?07KRN!tu zXRCCWuav@9Y*cRf!ooS%CNcF;V}LcVx8u|oxf- zPmgj+F>HlzvV{XTCf(*;^~5(%)ywoXubN{Ox;Z|}R8@L6qQnR7q2DMDukHFfQkfSZ+iHD1pa51F2D5c$?8Q6)V+ne@e zOP$%|lEE(T(u($aUPpktye-TZQ13r%g4#vGjC{TUgdQ>;;;Z12UNnX_k;HhpTR;k}l|Y zr1a>>7!PoqOB+@eP^+gI8vV`yTl+-D=J9XG`Dz9n_8Q~#4oEpRRqUb7=)RNEl)L-J1Pvqybq1Us*~soMJQh} zcDxsp#Xam5tarX(yJkVdE6$nLWZkVatEHtqAJRpZ_Q6pq+!6T!2<&XHkQC4rASsn> z4kJwAG%VWSEoj0PPi5y=pXA0?KG;kY5gndzq?CPE5ZtAX!d?~6=DYDYy?(n}bsAdc z1##SiiI!`?B$&mHl&|`(8X}ocL>V#IQxTV!D+W&;Y6ra77C;jU2+M2@V39(rjiebE zI~GWdqGHK%FU!^Azy*I5xw@MNytrDgzv2Q*>T({&3Xb2&w}U;gP%|#AfbWz6e}gjT z@5vc~HhKK{s5BSV^?{JPr=kZ1so(nHc#-pZ2Ta>(%Ea;YO32*L_NMg2YV@$ZOe*Hq6c?XU zdN%?yvO8cVr6nM!VdSh{Sc7|*?5$o{!v+L>Mf9ph>Y`?h^kuvnl-8N)jA=vQo>TH{ zW7d;!O~kFhEu(NKkC$ph)RvJkessqb$qZZ)hds>0zG5t2hi>**q@$FX#Y6%wH1d;T z&Kvqb3}hiVx?G(HK}OGAjUO*gc~Eo*dY9wK1T!CknEiIk2+JzqJvZ7o1`0?nbaG!D zmvsrrS}_@y2pWy5+v`Zs5%A9i+_OQZ#EM9{L@%H#CeIShtfS6kGzi~E*_BXFCYxBP zA3B_M%q7eUd(|rY4^#1DtmHEP0LLHyg5SU6Qh=PF)@z+D*{we6jb+cRH zVwqHK86P5rCYV87$m8kDghTvp^bkB6Kk?2UFV~7ak%TOq9IKX&IagH(MvDU;iaIEgax$`n1BVhv5RuAV5028lq*tu?}1D!!qiY;Zk2$=wCdO$QB%7o8B3uf z@qZ-`3U4)tIZ*hq@>P9Lxf;~ z&^(6)S$BooaK##NZi>BP%(%c=pYV(1(D}b$%1z!@DcDX~&!G9u6W1N&UF@ndPCO>` z*%sKPLR?~k&q(1n;>OUOqug+U5Qnz9oVlio<#y%q0X>d$pJ`p;pypd4quCL5=UjFApPrjrr2bMv% zs{=1@X0ChUTpl9&U%*6qgqsv@hm+XQy>^&`8sjns7lRIeqi%6X71sbuyEW0ylgw9j z!iIfZxrHCpvK$zuf;gV{!W@5+89u-)XUDy`KjJ+g1Z+zsi&s3%KFk@pga65M7>3R- zs+f12t2+k z7t9ewLByc|yVVCY48p2s+|gPe8hH=b$~7ir!PYf5)V6}P?MQ3^60IVGD|}cKZ?xuj zci{NHaHJId1xFmd?(p>C$-`G4-a@68S6|ADQt6*^d_4#ANf1Ibr8B1GXodsA3-AZD zrIPrr4}?OW-k&8MwSi_q8f~7vL>kF;S#j(6l@%xN`m znLjyzI+L+-3j(b+c9jxEU+0Svo`7}BLV^8HRF-XFA;MDm$y>GD#&9v+`mZo#NvgUD znXZAhC;*(Ikdzkl4aajji{1JvCB2uZ6g0;uq7ZzM&j6o&b2Q8G0z+ICD<4tl zs_p0!ZVAGke`qB0*Y#pr(4wTdp{t=Dbgr$dYv}B3T-)5dE=)4s=#H+X^wYSVHXAy% z8PrYut0$vnZP+jZ6IlY+eNC0Ep*yQ#Dg!NBU7jmIRXCPQhJTaMXCeivuBsX=1ak0Qku>YmxK}11p*&M%pK+z<0k3Od#(50ea(ebCUx7f z^j?gDvVJ{RKm}mL(v%JBrg5UB`Py_tIN^xyfC`fHpuw=S7PLVcIvVj-=r*xelA-3d zRXr;kR@WZi)Yj2cTVLPP($G~K4@VlCYghHG3y0izGT*g*lHEXJE`vh&MJv<0#SY(I zH-A15FBDx6PWDCz%rp!u*7Jog&z18)l64>+&XKbLQAv1+tBKJc@+FSm#d-lc)4uf{=GU4XAHi4Z3@A61Xbj8(3lt5Gy9mlp*6HDV>H#T zELP+(*F*Av!;d_WWlenQUhZF)wH-4}tx74xXM#7$H)=YyfBAv*c@VH&w+cE)$Q?6b zro5v9^hBX)Aq-DUr|Q`6c2-nUQ2J!=NM|@4PZe{dxoPEzEzMOph!B*QkKkC=l31)0 zT0_vnfAj>5xtOgliRdNWu^I-b1O!BX^ND-GCdDn=Rrm&D$HF&ghw1Ib98E^MAv7U6i>w46ft85NsyUN$Xf@_cMsPJukEx+*>|6Ehrn zqNTNwt^o^bNxJ~jwRnZ6nt(1Ilcu8AqiLk2h%UAL5It>ZBs-vHn@0P9rP;6ms&>j% zrkb`gPLn3F3~(9=t;C(9w*^z$2uz}u>jpHd5H9YJt4uY$Wt=8WZyDfJ)4Nb59@H^T zjVp@?ikysVKsk9_kIO=9w8(+w>P*wfdM!-V(0UMe^Vs^k<{w8p; z90#Vg5x{K2DPlxlXMJ1bGBNTWSCVQ>lrL3)d;~0tJ^c`69_4{gf=gwCO5oLerf3k$ zsx3_U;tQ$JwD53%y;vkS`>gmWV(B0fNSqx=seB9`RB@two&#PES{KFmflbK#uWoG; zk|3L)f+XZyBm_x<+C-KlK`t~ck`Q2F7byw8_0<$fLJ$cgNeHA=K1m3wI8jN+0WSxw zi;{%ECS*xaw>Ei6@XU3UCm|A)8Q$Z~d3ThQ7 z5&oK!6bU83Toj*(P$(J+kP5lIiHn6Toz4-2qG80bv^xC0P;pSXz$ICL9@ME`p(>r{ zMRGrf-c<2D4^rfGnHKr@TPj~*=XSyNg~s0I#DQNkj_r6S@&=C`VflnM-*lMN&c zOa>_M0>uCdwSpvoyqykLjo8A3Wx#+5)3K&n48(2+NQ6Y2LIp#tR#?Y{jD9(RsW}x0 z+5RLwi20|03tsu+Dzt+I%!j3F{EJSv7XZDRmz0Dzi>Pw$!ITXF;G*1%>B9y;Re-Nw z8^XJ%XYMt9)Sv0ABsw@*Y@n!1d?qI6sP{SWRY$el_Ug>6;rQ~Gj3iRoB&>&hS~lfS zKe2G4RF8)vO9E!*I0)8e9QWcOl-r>ZYopLs@NpkfqNO^BgG#U*=-}wM!2h@_<6-pb zh$G6%xM*SixCBt{Nvrn2!x7yLPxr)wJLDz6Bt7Exknzch$XI}=&4I7(ST_DS&;KxM^vt~%=VVrAtx7tlG+ z-Gf3@W~QsGGscvZrjc%3goy$yw?P7e13!XeP6|_4>b(~pao^~>VSi#whlIY*a}M%G zSmcZJD85Gu5My~cDyO?vHBvs;D99V!kT1xh_^v8IjOCRDW*@12WE;X^bx%3hm#?e- zE%Cf@2NUkB0K?Z?wX0=Au(JjASL(17vFMndg@%+f97(_ojip=Y2`5o4(&cxnq5l+=V=S|$^&qZU26 zs!Y##ctc4LCzotcSN7;G)Rq~8R^N3ooI$=bbuCeG<%s(v$1RZIoFqmz;oKuD7FXbI z99eN>B{_cUt}M`FDhxggMHOQ*wM&m=@R}pYgi#wNDidaD;U>(oVoegY4U@wp$!Ws` z*dwA5;}6!!83jS7Vn=hbGzGy#7F3F}=To*(sCB~huK&aT?7wViRBq`~mkN%C38+PXrjaYy9? z79rutnC>3W;K?TSDYwC!&!o(&+JeTm2(zvrlc+FTA_Rm>&$*B^Y$Gh>mU;u)5SkqX zt&23Pd$2^eLG7{aOv7?6kfy%cQ!=|j$?nkzhfSwO2=L<-%HxdkVZ#hl;q8a2XhUP@zw^Y{mM}&v$&WRx_!HW=fbwQVD5Mj&c;#;s6iAk^tsk z>e}&%0n@Z07|{C%ARlwc@ABCE0o17IlMEohco^T_k4#l{zsBiN5q#F|T7jhN4v_ZrGVgaecSPylIXy78^93iz1Pt9s6)OfGSZ<#Gjn1-zyJ+W(bM?-CWr~e&xE-HPC zxzqxh*&ABxI~r;`+gjUKchq(^_(69xG}oTkP>-I~t*-4*G)_w#x3-~Uom@m35G_%_ zfGA{n@*@%m)ITH~lH@^cooYyG>w<@*wr+w$Qd^g6NcUClVg*^KJ=Va_Mi4mo|-fima?;sn&{B<-yO#T;S`ja&w6*(+cLdC!}K~T+k{K7`JfeoF7z%YlIUIQ)S zR-(JQA%Iw3AVP6v#$kDxn6rC1CXXCln}}uM+EKh!RBPMWG$gFVE%S66baXA0C}o%q zqLzOlvu6eJ-(VN*Zd3JrVHxqW+e*!b$u^_aG-^-D;CV|neGKo_l;vSz&{Q#H{07HV z8VjCFK5!o(C{G6?m5?$ z5sf@OBc-Jh>5r%^<7|FuSY#0u?X#R>?A{|H7!=7Q9Yw}vB*ngF0}Y!@m^Zn!YAQ2g z9%#|5LAvu}lP!0|0h+WGR}@q5D=QEvt_r2@>wd9vjK^PSaXO-C11DjD^8reJ(_mofC zImua}!qBG2CGe35VgFmY8ao#YsO8ufI*}8`MA&H86PQ^&ftEu*>RQ%t+WLq-mn|L= zp%-wJlFg4k^Bmhqkwq#CDYDpa0k!&#o&RIjjBnRJ<8Jq>teJ_~Kz_^8vxK-IP#Ht5 z!ZIzFJ;ddq5=&t|Y(&<#XQy!PMr3~P0?Rv(B@F^ha6HtapeTc_l+sx6+A)_2S(E|E zMI#tVkmfB#XFzkrJZ=o|`^8q=C8nY>#4h+0m140D{%ftOKAk^1S%7WS@2DBMOB;k} zE$*d_2D;&xjEnIck5)E&ShQdkEh-B1zn4Y96k2US+(IjCEl{DjLzHOljxJ6;C!wS1 zDZ4P$jTiYK(538c8x+N<>RD`xhqb_=d;c5So`IwC-%uyx`_sx1&lvA+ONSSuJ@dl^ zO&D=Vr)`e-FZP=iH!+oZO{*(qPrD&D0ri|TC5N4D zr`o6LvK|iBRHwQZ6tIjl)x%xQodHVMcg1mQTUSFhXzDjieE6fQxs#XdEIEn9@_j7? zX)~$!jbdYw>;jP7V2%l(q4lO90uTjlmfo)$ID8e+DLRtIN9Am72quAUkW^yp1L)E% z>aMQl&Yrrq*4Bo)uC|Vzh7+4QyE@~MF>(jzxKK1GCc!)GXF~@h@WoWXG1Cdnp%{W@ z-FBb>{h6C==&*NF#>FWYU;#`l6!!73?1)dvxE8?0tbY?qwj(v0(rlaTKNQ()tR}(g zPOHTDFlE6*87m;pI7d36=BCcBhSr7-bzULgSOkQhj)&#}Et$;2m?15h?82`I{XCAH z9&Yqgj4~inFcr#6{79y?R?`K^XCxpiO5MP^BT)7i+Jkx~BPboTpjH4>oW@bgYMyCK z>KzYDN>$g<)zj71Qdis6v%0IRy_3^Pt+gwg8|o1}a>shn8R_zZ7Sq6@R{R-X0p}j! z^e#uo3}i{GM$#!7Jm1D1PHzmB%w#lXP<=k$jgVHKgLhx&ni^WiSu~fIJ;R&WFg4L! zw-E6;Wlb~(LPjs6ALt}Kl+uowNSuHxQ7XLM6%?J=3QAlVs1hc?^xkobjxKS>ALsn! zBpDZqx z?$Ut~edwY}P-;5)ga^qtsnnTy4};9(f^kJQK+4ZIIq@e^ae5(Kl@JZ@kBB)jEJ)yl zr*Bco`75S~SOG_?=7BaNHL9l5LKfES_lYygHR8#nR^%^CG6aUV0L3keNk*RKq6z8f zdakXZO&>J%?)E^L{d zY9~)RK9tSf(Bh^E$$hLmZBo4LNVdfSmW=pyX`s`kEO5c-yRt9A2T2J%RID&iSC!_~ zuJ1EzdZJxzakG26>jEK-Z9u;26Vs_iJ>@$`JwhJaaaA`}B;lUfX4Xo;M&XTOf{cd| z=ZA&Y)UZLXj-(`(&51a7k2u3JN;-XFIu#|ee$>-9YQv*2C|(S)3oFnTDGZ{VK+%P( zGJpyL$z}9*S6oF=jK6i>C7aq3F9MWIyIdA}yrf{TzAJkf@Id3f2;jc^X(ix_bwTma zxhql`_=yZY2iVU6ZEL$4I*Q@~%gj2^;ut`gO*;<|j7T)WVUpX4RuDL*wp}*L=|_o& zdCCw+TXev2GTzEQO8nSKYM?8wwD4mU24U`N=b)Y;=(u_^-|Y{qU}39%;=}KR39Pxp z?Hz4MZ8o*80xeCeI%>O`+FC(dBRH|888kLEH*|&z?+Hg=rqIJ6%zYKE>folf-2LOs zj>7ww?e)57nMj-IMn9d&s9P}NVm9F}B5|f&q+fkqY%k>g5zfvk=tiE51vAh{g4Tu; zdg^PtYJ2LNI)I6cY`xEI73eljHSFj^5O12`%F+}XDW$OLb%n*p!)FEqc`Jqrl@d3X zo`U#93|^^;4GHE(xQtlF#Nip}3&z&Ndfvf{y5zco**=ieP`7+)T=FgYVi?@u2?W1n_nE~0C68*ocuVJzlk3Kf1MXxqd{vs;uA6_Ljy#Y+<-2yfC7djaSm$ zYq5u3dkRf3CGGW@me@zcI}y2e>?EK!HJO%(Dy}9Y$tM*PCOTg8c`)2fF#&R}fI>31 z?ili)UJGi2!c$*L4G)6H9c~8|F?0gvxT5PYOxY^CC=UY(eI#)VHVD^*K^RCOn8KBH zJ`JO|qK?0z&h5V>BPtu0eVBOo3FEtkudtEIttB`32oiYJn~+*dMou}z?A|gN*Tdpq zfqx)aCh7bL%Md}~eb}@qxDOoX$yv+PDZzrcZ_-S|sn)iPqh8m7=`|QA1!%7cQ;zd% z5#r6~g^{gM(5pNq#<8`>*Y>ovqa}1_fi}i?D8y2SQ!vcgD$S-`dd<=5bt@iHA%zzqjk;w!4XDFd($E*Sx{&b2#Yrk$ zb$MRBxV)iY?qSU$o&qwIx(#4DgT5Fbt!41Nj~Hw>TdMWZ#fIxN>ER`yskO0=Fg~S@$tC2DQ8V5hT!m#PDwB4#`V!Wy zIaCNvA|To=N~0pc*Wz7aQQOhg)L2{B)z-1DXKiOgM@XFTB$CUey-N?2>tzu@;4(&G zmilHIju474b-)j8&Mqtv*=B**0e)e#>yYp4s_p3NX>O~lZSLu4Xm4(+tL9@RY-(;Oz!}bA5j9gu{5L5M zYZ%V}^}dIKdtAXQDgqRZ3%BDyAF1{qxK}yo9*&fvDY&Vx0Z+R`TqLZKz!XQFGUAa7 z(8)p8f)bZT&VgDWS&D7F-~phLs$d4c;LF3IPz{q}28IjA3_KN1sWhCG{3e(9LdHk4Ax{QFUgVr3HexN<8>5FC-JIk&RL^U#YKb!`G- z$bREnl&m|vh|Rm1r5p-W%g=*gSWl(c617*e@wqA6(NZaBi56m0YiCz&b8|z7>nuB| zslBIbU3-ISevzikaAd5uy`79jG#XP&1FkGMQp9Co_~b;=x78R%@RnzWlP8CBG%f%d z&(%I9n6N4y69LltMQ{bTIw_DVS_S;jaPuur5myw-f<-h`DE>;25}CV;)Z=L_gOnNm zS5Vo={!^)H*H}tx8TDOkXzh%b^VrLr)-pbSv^eZ7TE^#H$hoNE%sSbvZd!%3=K^ z(H;XDYL<;*K*)z@%1=|g$K(+9hvA3_PeTk&F0C0DQc^^Ki0F#82Q&*NqlkS9bwv|) z@ODr5zJpDm-+~zs-l!+5eYqtq*bhfK9ZNU*@ueULknf%X3uaPU0(QX>Cmxjq3x6Tu{UJCU_4B*p`#?HF1b0@Fg3ZRmh34x&PZI#<_rG}QOBw$(TEG}QxW4C$6> zVDTTKuCgfT8h{og`UV&fCdnC^OiEAaPHGg`8JN)fMuF}CR4^Lm9Cp|iMEzFM#mPqy z;;rDq^_q-6vjy6=)=xba`OKiYwwCs`&IV=*k&7Mu#a1^ow*;C?T9S5Q9@e9H&K%Zk z&<_oWCW9n7MV0K0a_M9=BMAnyay)e0V|w>|AC~f5T^DAHLNTC61Q7IvQ;SZYj;|)Q zp|e_RAi{b_ghA4T#QGt(z_Exh$E1|R>`t2EN1`WTLCt|HahbNh zX0soCMN8E}$AbjaH=F&a1Y3ZJTnwE=j1-HdNV(_;V+abwa`gNlJf#-|2ln;B1X_-- zwV@SPy;x7nx%2$RvuH|pMzF}oP{5sl;Ko7Os)792f zx4NNj&3G8(z;abb+uC-ntZ3399_uvwoMzn^tTmE#n8T>|!;=v*@qQWe=i#`b*pD<9V6O!1HQ&prsuArO`UHCX0mhAi! z;sz@t9xYOhv_*Gq_>+QeYZ#br8gzZyZ1$5zGFsM#4I?m-B|Y|+Bjmv|3J3XkXxX}H z1OQPNCXj4Lvm7@-UkyMkL+h~#oL$l%P*pA#_WBPy;Hg>Ng2{TF9%WbeUnoagA5Eup zV~|l?hXM8dtN@3KOV0YVj8KnrK;^8u7B5>?F{7et(b9^F8K9!jPE6*LMY?iE#iZMkJNG{MxWif%}0oc<0kPuzJLZ`#nHM!JzEY?i=%y)wA(nSqrSm2q#-*pPECg#jmn%Dv@= z)XruyrsW8)>9dexwiE`V2wJ(DB;u$7@d~~)bTke(#1gw(f9?59?o<5y--(0ZqxL|- zZRDN*moAg%|BA)S_RRmg;xq5CSg&rxdNq3>6e8yva4Nsjvat}`q;b$>9M6%afc(QXj|54GDzsT9gkFoTC|Z43Ncbzswx$$tN>+Y zp?Tm0O~;$8pnfGzoq`s%AY`W?%z#A|p{(KPDX<=dPfld56ezbrxe2_~rRBpQeE8Ua z<79dgF2uBq9viBR5g&7G0BR{`;AGlo7Bg#ruRxoOXn6EH0MoBx4vw=s(JkpGV?(Pua zZd}2wdR|`KNR+%8(H&43q8>AgBQ5=YL?yt9Yzf^eD?)fDnv$`qB1HD~_+DV>Fv(6M z^<`73(P%WP8=%S19Z+W)j;7;u5TtIWW0FJHe0G0d>1Z?>4n@Z7gr#R3o9sdeX+;Kr z?U)$~vtfi#SOEj?lXy_#VYNLP(=r*v+u@MMWAR8;2(dbl@Sw|CghQl~ia|WGBE$xY z9{izqd(58?%KHE^tzyu!OpAAi670SF3G5zyGG)Ugh(@F8zM+*L9Zcq_6&tSbx09C- z6=McK4A1l#Q#YJ)X$0nn0N}I$h>Ur}8xPSP1i6o>g9b#bqFJN)`&SX_(|5Pp+w-BH zg6DsuUpGd0b}3JoMqfcrfZX$cWyR8E^8CMeX+_nZ`F~e@#>UFgbQHzAx^@(gGD+6M za`b@l@sMq&de}7~Ht1^Z9I7P$M%mvg`ghTCTsQ#{qv#3EfdGt+N$@k!vT?SG1dOK5 ztl{9b+4y+Owo@@DWk(a1hSST?k6-86aPDWqLX7 zA?$-z4@q6qlh88*(6Rvlrm;MP=mZT+jCm~cvzSvPL?4cYlr+^12U>=fYBw!sHKH#9 zr7UUdMt=%+I$C0|seNc^T`Frk&_cyYiVv{9y1KjCM+j8|z_wEq_mmAmxd)@z>ZI!e zq{3KdD!WL4^OzRb$9(m1!) zb$aQFqb-;*kBJ^pwjnw;26Q8l$|j-UlaC^^*T8{*QN$&M6$_1x0Vk6JrOL9j2d3@V zVGu3DHI|2vm023W3zd?eNZ zl-W=04Bq}Ir%4u3DMwH1M*nhD7hB%>s^UVZ=JJpQhb`TKV54B$tcZXZuwWAV^B55j zZ{RR>)_<>V=}_gSp{lZ2X>>sukX9RdENLbNp%udbVv)*Nq>ALjK(t5~Sp3 zu+cOFjf9y*W-^*y5-A-TPET4J>FG6-qdm5M8Uz)~LwGHXi5J6|WGr)Jw8w&4vd5w8 zaBx&b#d7ymMzign0jMRR1ppuks=T)e(;U=c5B8=4aQcFcvFHL26Keza6U6d_NeIJN z$6|#^2CoF9%!xePU}V&)hGe9T+IGt9>r0tha(uk-vW=#dfOuZRwMchu=yN5UIxcgB9BZ+71x;;;{(; zZ&IC_;*9$)tcrpJ@duhoo*JL!}*6T$%0bgE$D< zLla{$M8ar5WwdfRr%O-GLfh%-Wwn6W*XO=5l6%`fBnZE4I_STzeO*iZ?&{D(NDApF9%ID`78iyqH-g5c` zYa5VA#L@`sR@TzN>C&V}wPOrb#xq$2urMdZL_oc!Q>*0z(TP;D>{WE;L|@07n(Uw>UiwG6@(D zjfci3?f)s#|A&-S;f_E0$+Q2rxT&$~11*|vfL1iR(rpE3+(W~r zHHb_Ik{cS@04!b}Qu_kXsxE3fNoZjQfVV&c^r3Ivh-;+a5KQ^k|6KLLeSQdF%w6;q z)*z9mjGknQ7_TR-tP!J)=}CW&h2@Yvz)!*R=7G-6)pY3v>O>czV?ixV*VNQosL*No zd@DHkBA0&!p_E%?m5X4CgkY$&?BoEfKvTbFE=)z)g+R0%*JKC|LBp{z*(hr!-1)=? zpt7QBF3 zSvhI36fTibCGIm&qGv!H2>4>i7}Yq~5aw~;K%2$5 zp=qEp^PEJc$1pQld%%4f0$>orjF!@epy!TUx&*(O*MFLxamz6+%;{fjMJ8hl@MR^- zL!t4|?zV;gFMbNP|GC4r#kP@W|9{bvWlJRczjDcX`*qu~ zMo9p$ZrCu9wO}>yFHxlmQC1djw~^iTTxN5^OeH%V)6)8(=)uB~Ty@^tYI-0im25I> zM>7(TuJo$`PURWANy|4!tVOYqxp&LmweN{=@k>mijZuGOx8Mc{%&1OG~yN0Zb0p_w-gMj0+V%yc=7!0Pop0Cnx zX$jbuO-1d2tdlf{jc8gMiPAYQnxt!#Xj)IDbUQva2GZG-vTc$N1D9TfV75a`Hl-%(PKULECLcEVj~Hi5yGu_0ybsFqBcMwABV#pPiq zLx~4o)zB%F;;adVQ^K@hyJ@C6k=3XshCON|gmRbys){~zPzxhzx6$$w(>AK>J_@KX z!c`a{6-IJYI3+#er(nWNN9`m)K%;VWBa_?a_N2S%Q~1-P`x7M5GX_vDbSm8u5eQXQ#p# zQ; z^^}s82Vb#O_j!(XRdv;oU9y0swUM?AG`wvlJFppdO#O8TWMY9ZxVqs$1KFSMm3Yir zL;N4u3N>R?^@Uk1Aw%`doqVu^Oe6wFzOc1$XmU*tCU@a!;o!JR2vVPAB8nu*>H`Po zlCC>?8n)<0)`4QrS%oMq9i>hOS{7^qr)EtuE0UQCfwQtko5AgelB@efq{&pLE(tby zdA~GQnH0x!1b$w2qtaxU4k(=vZe@Xs0ZCBpOiFj?9p84B=^oPHY|0a>n@CE5j@p)X zXeFTGX#Fr=RpGvG(MH5~%PLg*K{TF8cSO5+^7FI%gO}KX1*)LsMj^5k0B8bY6aYz* zpdyz*bZr|F7MOX~(CGvZBb`9C#kxfbLpqUP5cDL?yvN2aKTNJlLF&9v=13=|fH71+ zif0NH6a_=jab$&k>!H>u7}#NiZZ-k z+%#a?Ov02Dx=_K)%uWzT{}K~<+n?sq}tRZ>2q&#fwLoWHkcWEbnHo0Iu* zpU98}GX>uMj}F7$0n;4BnBpf0ID3epOA;hilY}Q%b9xf*q;bfxXSvy3SxRR4-R=A@ zZ2u=^_M5_~3iKeFG5QN_Bj^54)#8d}OBea#zn1KY|Jo&=bJ|;1g$@eMm@y;Nw7R}y z#*F{>Y4qRRSu9=sfh_%#m-e7@9HT%o$DfwOu1Kpa1LG zUtjyspDJE_yl1~_4!^kfil&X7_50M;Zz(yK&ARP4Q|dBuMoe*IskpLOBV7oHyKnJb zyRGw`RcFqaIcxUZeL#)(rRG<&-tGC-3xk*XU(MLud3WZ2RdxP8>+Z~#_uZ?;|E}i1 z)(?w7@R!eRs5vld_0F8@e^+|@UH+D5&ie9RrMEYvJO6CQb#qGe#sgxCM(oH_o+ z!PdP?KB#E9KDu@91CF|8hQIOITekmQMUUUi*|I%cvcG~PXI6dVlZux2x#7&J4S)aD zECos0&${3Pik8C%w4ZhEH%^?PAj#bsYtLZp8H_!Hv1c&$491?p*fSV=24l})>=}&r z>0p4%W|dt0#Y4aM_)V|>>b|vS?|<1l=9^cY+BtmuXWqRt_2#cjo_jp>?k(57`IQqc zJbK5DGq$~Z$}@kv_JcjoZ2QH&XIO7;8=L?3^DF=InMW?W^sOteK6vjHn|}P%&mOqu z?q%=(+j(`}O}{?luiqZe-c(oe+{JBwx6l8=#SG`~o>{fw8-MQoD8cBT`wtj{p@u87 ztG*h0Y}xWHKfN>k_TsMVAM1Ym!uA(VJN*~8Up{+j>do@YJ03pc`C;P||32@*x0jv# z=<6$=KJ?<#2S4$fz4kivL;n+6`^$>kevrQG>|aFx^zK7vee=p+zP$d=C;cIQ?rCkb z_oZJxvueXRPki=pLWTb=x&QvttTW!e^yQ{&u70ig(jUKYPy6Q65`Vh(@>`c~dhLn! z%bs07y7t)xgCEWA{Oa9TzI5r%2OfCn#^df;HT!$#{N>#x!!JGf(AoQ+UESR|n0V!> zS$}-~wjaFo?rEpJ`}9-qzG)t@apxo7dHt7r?MsVi z!t1|v@8{m3O*QO0_t5nE(@);GvlZ@q?3r`-I&?hw)C=1#dik55-2aY`E&2OR^EbZx z$KTeTvGLVEzIA2KB|m=qp09lHfKOH5ux+68s>7I=eR|H8?H@b(s2dvzk(_3*>9+Lm z&N*bwYrp&cqp#fc_zCYEwduwePUt*sM|9i5ot>qslJi2VhcbU(j#ZvJ2Iw|{&10cHQ%x%SvO#=_?+ombAg{CjV{`0Rz>eR1LAj?+@N=9XI+=YDD9 zOnCjP_g!-7z8L(5xu3aW{YXv!&exv&=M{h1`mYtu`f*Lyog1zgd2+|^?z?Hlrh7*2 zKL01}7cKu&$A#zq^)~P!f>ra}(%WJ0L7`7$sL6({Kf3?txAgt()k_kOZ`|>RMfOXF zEPm(4D;_i!|F5<=bL|ZeeD;QQHbIF+{O11NeV$qWuOF@I+k5Y}fl$x-$E;Mhy}tAF zLm%v>9Qc#+t$Sbi!W&DfNe_?Q{PN)seC2}&e8qg{<=DB0o^kTC_r~uz?N{v=e>8mE ztXuAmj%QBVcEW{6uf5?PfBV_l7ryba!6(n3TV0PKy%O2F_l4j6)6(NHr1sF}mp}19 zddb0ens0@-tbFm3KPdJyB#eFn;ZY8-vzjhp_xqG!X-XP!7`#ZlKiw*BL? z?mFNTRsTHelH~#nXHB+U^_Y*hI+Ww30Y@fN;p;wRn`<$bO$9|xH_wMSYtykB4uj=fR{{8g9@7(&-%N<|b z7=P)@zc`l?WYz2~+pDig9!y4OaPwIYU$Ex&-@ToA;fxha2isqI<(>!DH~-$A_16RD zKJ;|v*SCN1;kUE@_{C>0UvSBDb^r5=eF+6F*{}Vq=OP!~i>Xs{%` zbI^hhFZ)W*@85p%v-{6Ywt+2w|K+})`sD#9+bF{Dn9EeZ{Z;-xuE6_}b^^H`asd&40P_s`1f({jGEO_)qFbUUbdvpWaUC zf5*q#&pP6(-x?zQIcZMo&mS!R-lhL~`14Pn-TLBz-CtgL&+J#`WgdU<>yK?*V%_!5 z)d#IV_z$OTxZk7`!>pO``nl~(nlMBB0sQ#Nq313<`0=;@`ora87xsMhvNu2X<&S-? z6KuNtt#gigeM|bCAKz&7A9Q}`&DC`3J7VkJ2mI*jhe_A3n0?ZpFTU{5i7%&VTmDTNhEZ`(-*BdcKz@^m(18~V~w-vweIaVZNF*ox;w1Z`+SB@6W5?= zqNHYo$da=;yXvcFzjSNp(w!IBvA1qnd1g)3oYRkbgepvO)$7jwud+70 zOmN(1^$*Uh+R)M8xQ+DwgR_5m%VQTFdeOUg{-oc$`o^EnJ7MR~532#?vo8PEr$WzN ze)Fe3|NJ3Ge&UZ5;nkbZtlAKo-*f<`^lj^A-J5PX_qBUZPyh3Q58OE(D_=6_?MThm z!C9C8=2OSMaQPE2e)P)zLq7C2Y57~NH3vqso!_|~Gg!~&moLBQD<3@ImRn!_=R1jQ z5B%)8(U-zC;Fej}op;ky_x|II4PztgPXEZKsfy@`Zr%IBS3dOMI6~q#XV+YR>RZdN zZ2a&+@816E@%y~`jTxJJKeYFyBX6&G_~u*xw)3e?pM3WV|4Y;^BGYnPkKi`ro1eY0 z`?9Ov{mcD##m~*|xayEuvwsqK`L+kYzU|SSFaF}qXP-ah?Jss8KYyXU6`uyg`GpjZ{ z-2ca&n5pBlT7GoiwL@P#d^V?Y;xboI_U;Dv@KYt|E z{CsWOugM4=gOuq}FEzFxN}aRM_w=JXe*VVonc?qV6#M;W=hc9cSs!WH_Onx->o2+Z zizQEf>AOVrf!AlxMxv6IG^6d6ozjnq+FSqaff9qOLnR!;znKd7p^JdqE zH~w(N7=Z3_8vWMRc4!C&6 z=0`)19<_bjJ2wnH_|BX0;|_i9r_?n1#tAhCzIgM(jS-?X_M3a%n#*n(d-CN!{f|BN zFY^h+*;}8!Xw9XUzIEjr4?XbbO-EjJ(W=wZ*m&Fg+MF%h|Gexcb1)9i{N%RHZ$A5x zhi@Jld+29(-7xEseG&Bici!=tL&tA?$6R&Lhlk$S=jrpPkbf5`*gIf?X!*l7XFl+q zhPV3S%U^ouQW)Rx^1h%3+=VB;(|>XMJs5hRr>#w;p!%O*j1f`%iu217AOY&Mi;QExrBYzdr5= zT=V1|m){YKZu;r)8~=EEw<&7c-2r>37M%sAKkk52~J1o zW2o(0XaDkV2YlznAAVx#k9tnJ>Y5!$k`DRkNb=6xjvAZ)v;9x{ceZ)iNyPg5_L)^1 zw%qvV`PlZ@>%qmJdL#Mv`cG}U|I5#f{$(HVrH;+ZOa8O7=j)ICyXB$g%kJI#eAL#H z`?jBT?(a^DVCMV6Z9l&_banaoo8Nimh>p{`fAkkb4;={D^VSZ?#8j z2u+%1Z`pqAFQcVct~e@3>wndH-uZ}qYR;MS+9NNG4&L!^WHkMK$F;Q|1vOhsXT81P>eKrl`pvx1 z2iI)+&HiLdTj#r`Pc(L{OjL7{=|JZzPh6g&#hon zm2vuxpT2ro<_mPKvbyHLtLmDUhVgpiLkk~U_SP@-xBu7v{`c_&oH=ass=mi=edUQO z9=v40xrkIbzUIKIuI>Ff66?+1+Utk$o1Y)P_Qjj;zvI$JuzLU6EM(X|l^DF>^z(ne zg6O5tOnANV#OM*2(VI_RfA3ia5u^D^$MY&>tO~weMKAd`#~j!z4ZTw(_r>aK<(Ny5u_#-&y**60DPE&Z_)S z>FsySF5eqL+3&cmKe~VE=&vtYerL~xU%hAb+ep$kzc?37;$P_g+vi6<_51yZg>=rD zRU4ZAI0p;VReLR5QSs8|?=KsE+PZHHQ}Mi6+rD<{`jP#I9{Js&j}hzf*z&D=pYZH~ z`&@>8R4+uP-P^JKJcaC4(d(W!!bb0I7h?*#$b$3SpS{dqf z&8Ox(_59cNGwwO$jyt}-6!+=iS=+v`>Gmhbul@JP`HQhVzWQ=xc9pbT_@RBkmp-sL zbMW;gPd$3Ud28yw`Tx22>bR)3?`=4D(3ydc3@ssyASDeV4WojJNQp{^bP7l}qm&>a z-5{udbV@TygQSGg1H#ZD()E7MjC|jFfB!O^bM{_)?PopD+G{U-oXI6gPuufnY@0W7 zF$w|yy}@5tZ#k!*h`{6z<7+=_hOg@TY7dRtj^X{07dG+Ow^eR5!rIoZFN1AbUa zK$nAdva-olls~GL1fB!eaC};FaPptT$yo;SL%AiN5`=(0UvPGS1U( z5emG~$N}u#i&)o-Fv;yD{AT#2i=yYB-nRb(dQlo?BpD8R3Nt?t{ub{gJc8c4`gJy+ z&Fs#`1*+@ZXRF8oC9}EGG?9#*=4dajywy z>%9oIAgOLA731j?VY)1H6k+?9cyVe9EkM`J_e#g5P@%zQktL+jZ`7 zgm~|;{|k+yJLZs}Gx5YD`T@1OS!YapN(9ZxjhtHqXeja*)~l1hiC^z^c)&fOHU<%$5IZ32LuUgS%9Nqzc%OR1j zeHWACO!&=>Bop`PFaH1(lYOCJe1Be ze#gOuV8lVz{`}QJ@{#(HiILbjs_J)C|4q!I^@fh;}MbEGP{T(Pf?e1|r$FhSR6GXh z=^-aJo#($Oa7)Ro@Z+C&f}-co6UjCNpQu9ZJ7XBT(;N0bgiTZ8-GB@lVBr^B*D6|h zljG=HfgAqYppd417|l<+tri7yB z*Wdo|881jtO1j;0BlXPbtfODTXj2Hp`y2ERf75DM_(lcI*aL520!wXQ5>eA#`5e#BG>141*YG~D3yH;+iRIjt_a{QLk}^rwY`m7(GhKyu zIsB#PAoyWQ+nN<*m#p!uR+ul7EMaL$R2+=Lx<+k9jwkKDIVp74rwHyV%q>{5e6*dP z<8ox0Pxx$@zp$Qx`ItBif$Y~t$MCrb2zao@XewV&`0MbS3%})VR^D{omA1jhxDF7J zFL6#rfloAoxEK7e_FYHo$ue$<$62+cqyjL9GL(#9x(fZHTXTNm?z5`QDWZ#8KVKOd9Pj5VrF)wEEYB$LTlwP`Fz4o8o+IjPxFBm7`du4s+Ltnt9`ie!jcOsGB>Sdf)cR$%| zOG~w>WG(^%yx#ANYDPj|{$B-$VkU`aNtClZc2Q_T4j|D5@PL!~(>$NsGAqOEO@0=` z9erZ;SGo+8cKcxLC6krow@C_X3c2}$quWM;`my?zx_5ANYbjSQ{9Za7@H0HXsfm&O zQ}6a*1N}s5-r~0T~Ys|L;f{MA?}${5d*DCTibn+V2I;Ps+6 z>RpW2ZeF$UFje6;B$OWnBei4!>M_SZaLKdKEOnLf;2LY$N z{}$EMl^?9!#O@u(JCxmSf5hs|Wb4(_M%B|E)>vm&{bf$My^2|vr4c6ed?jb2(0BfT zgj`Or(0)49{sR}>ISw@dhDp`u`MdP86Z8zk0Hl^JlWg>g%mzEkAGJ)fu%0&NxB5BE z7yIrO@S3LDWz~%(U1;WmLeq)E8sm4@r$GV{!#9`}PtsFoh?(rfD|R=_HkWGr3Or6) z10zp8J^k8KTh^~g(gtEMpEd-f1O>hv+|Lr}YTgEShysTG`ULR3E`U8Q6zD2a&EWK9;7MS=FYroiO6L#)Q<= z0V+U&4!Y;P?>?E*n3;@wm0n7PM9tU`n^hNcAA|nO1Pj>c2vC~NGPJUcLRpb~<(#}d zVE3o%Ov72%$I`JdOV=~P`kmnC*_oGJd>N_y-zFbzArl-02?LRhAqsXhlkqkQD2*GHheZcW6I#M^2_M!r^PZmh3)ph@0DzB2G?;Iu;_Nd z{5Mr?D(4u9q= z`JUx-H-s7p4It56kRX*t$M&rr?P;m-NIbQnRSS~RZE1qxlV_joGNvEtWGZ3|A3>sC^{v zPnyZxq?|k<>XLFb$GHOmoG5?PdqBA`96NVaT~FFF1|>#v+e%!zXs@}_o>xk+_}z}_ zd#RUgPCSTDwPAZbzJ5FTAV5vTI;4d)lZtRKl6p|71$B?VVH6++ zBq+t5Uz|0IT#$F{$y5gPsh}=V(>=A@F+28!k`>}ZAx;%y4AubTcBR_^+5=lt8`)dm z9AjNuMe3y~4>Vhl7gq~m5g4ix{3(I)a4sqB0pQx>KzNkb2CuOH%fdr z0a?oZ8O!E(?dI^%*-xkx2tRB8=ii^?_}neKBFap{g9Tz!4LGCt(GK=Ku<<5u*Qpci zKM&{>eVbSjftZaZ2A%Q8F-7`!?O_Be(&ca^po|`LVE&S=#bv{X`|8xA|BPL8FhGBy z@}peer%iVnn;41xGkNC>>ih&LtzfGr9fkF;O59`j_u zvnEiH)Hr2ZK%j`lYRpjDylfdoYhcvPJ{sus=#g#nVp{uWXK6V*MI!S@?6p0` z-Ox8FP=7mf5x~A`Gh9nb4fkp1!Y4}p1z(Th0V(tib%z29h9@>r-~xZNKO6gFP@Uv+ zeUB=QL+vqIhO;WzxI(uh%_VlTFN^dKlT&BT%GuMQU&DCu9IJ(*FKVGeO5-8Ezu)tB z%|UZ$d$Gm$c95d5|M^6q?KE* zJJXg4x=cq5NKmTN--G@l>rmnJbsZ--AXOR!yWzh9W#mQ7(%x`5XYmx#@(#baAR{~jQ@c|~8u@|c`W z|BsDyFz~K~QFe^#dc8uQ7M!x%kS74uPyK$X{6R=E49M z6wwigQqtYdSMp`qp)@>7qFdDWJ;JlKSg=ls2();5x1ns*w~B;0(bMT*fqW8I zcdf1@6QV-OWF`J*zRfLIrBP`79%FWx&P-CxUYc!Qxh_5yFGFInkHpL*AA-E<(~9fK zi{Ehd;-$)rCurSv-@P6hKX~L$$6T+;H%RpLlJ$ZAcp1x&IY z$UIn);r|txofOdF7zgs=T+QvVzI0TdP=fT%aKCmP=Z(sn9Gn?D0YqpK5(Ztyk81^g zp5a!npy%k--7kc+=cL!%IY@cOt*0hUF-iZtMYpgaUT{cXxb~#8s2p5_N`GO!qMYd&7~+04 z=Kb24tH_2szlku_d}-1v^Hqk?iILdNi4hq9(#wO8t&{X-cu76J?C3&N~j3 z@Zv}l%dW>CgeiioZ0yYv0^MZzu1YOBx4iCJS)ssie`B{jD`@%T#1?xph4ME-l=Su7|tn+W(1c7;OMv z$eEGkWk?q0a`h1(g1Jf1c5cf zn7O5c&7ZHDaa>X{+Ew_(**+u|J6lfsnur*nqsuKCmoOddo72t?i@uBgLRHLCzQn>` z#NWtrz=!@u%49FOdl2;bV))rJhDeh`rbvvBhP|Zo3%`Xu5_}e$fg~0?C~kB3&>wYy z3k>kN1TXszr*45PP_H}m@oR%QBY^~%nyZT3q{viGF2wGq^m9kH%;tM zIMeO%+q2=5hg}Zp%hoAG#K09QEjQDHLX#%O@j<=J3TOZ$Mn=p}>waX!{M$k(q9YsA z^(rhq{V2bJY2=TH3VJ-hXi}&AYNS`75ET-I`~q}2z|xuY*-%IpKxwaUO?RtMe01KJ z-7wSbz^s_pLwMribrvbKIwi7SAFXD0u$|TZoOk8z@mvaX8+X6os@n*&!N}yF-@f>q zW4gbv-WG2qs5pB`zx@=+b32%KEb3@|P>t!KnLa-1l4FsqDc=71>k0(=OFy&r`KV!n z9FG1HOs=J-B9{47D0zr|ZL`j^8$ZTj0= zwPH3Y5%cz%g2n(Ol-r?TYxAOw!HV4OxT5Url5Q%1r5Y)BQ{^>}w!$aPYp-XLjXsgs z0IjzNIc-=valjReYhsPrpCTt3b8NSR!wcb)-P{`-_{7Yz?sDUgeU;n|&fWeT6GwP} z+>!E+-Fd&?&8m28WhFMNru;&xY30A6RRL1bQmm^zYSh+=qnr<>`9Y@@ya)>(JI=%f$-P#9*rZA^UzNY}mh48ay^YM`>=H_my{xtKO8yG73x_#+G+y?KZ2I)T`%?U{s8vz)MegxBtSEm>Ac@Xcaz@J8N+R9Q zuk81b6YMg2dx!AlKsSn}t0?4C^k!#u-_19>H->+ua?EaYc89T$vkXRxu0imOS0g7D zYf6l-iG#K#$bGAmJEf@#QA8~2dJNxNxjQVH5hC3OA!O>TQ>>>9PpmUSd14$YhJyD= zPU{y-M{e72mbWe7jm}4GR{ea-LxdQBjL|?vt8*F|7|zCYTB1oO9{aW~#tDqvV73!e zw8aAwoXJXD-1=w8)OtiMF%? z5R2b&cj?r)xSVg&bMp)Si~`-j>DpMa_*tf1@6p&?&f*s7o5a1$lz3N@(XFQd1nR7U zqAzkXsNz8RquNOrbe8?ZR(khGzq7_aolzxa9(z((Ip<-lggMfV}T=$-?Gf^n5A? z+^*4>-~Vj0B#wLz2-bKvN4I~X*+ix~!1w5aRS>vo__;F4+-@;6ftF1mQMtiFPLs4h z+RqyQV5aExXx|UfB8N}?^Ql{R@kzQ&HOrKGlW&<@qjAgC*PkB)k$uce&p*D+l^&u8 zAW&0P6g|I}leIiZ<}!uMo$%rJ}&}e_P&YOcjIZZMD-}z>=F?{Irkc_a2(ea8o#T?z@6W68Oc{Xc?NNN#u zg#xI}VN9|fGsEmn8`>jmr)-5GTcpxLIA%ZH2U7;Q_x@Ao5UYpuj+-mhel=?-6n41@ z^WDjBUV@B$dd}IO2rW*6PefE)KHGZQvQLtJXq{`3(^lj3TEQPx1c}AI5S_fB`xuNV&goc2ixYP?fquEAlR_~L5l3GdE>7|G| zxaFdr5}Q?5^rre!A)~m^n>*p0II`D%alFQ<0CxR;hTY}0e}LCxJI+Bw=zWr}8ELY5 zM{UaUnk^m>S{`3CZsDx9vY)~46zL~hae&T9R|S?Nn*G)*Y+y-J#~!Klc?`~RsAM4Q4ZDk`fly*~Sj>=D4q&*M>AcGu;b>!|rP}5%F{!`nF7HDHyXkRDa{zJVn z*M}$UC}b(nLaJRymt6KS>x>Z+co`UWZ)W|qoPL>~ zO5P(SJ^Z73$LK(Jn2%;Lm4nMy@14GqebXEZ~LQ?A$N}fj+}@p)_fEI2l~NZSnqm{ zat;i^RfyqSnGW^9^5~HPk-haMM_bAtl=%i=2xJr-t$c5kEX7xeEcp3VW7^ywztwL( z8Ui(z+I3>H>PMYnO8zJvlIx$nnYN=ft8+;XBTrO@tNJGWBnyw;gT8bh?Qn5;Gd86$ zPE)~$Y}7XukZIQwcCk=k6;a6nZ2}4y=Wj6vl*lLq+DeMY>oBV)(Y~c-Hd1ngl$1qc zS)B~_^NaHhy3TDk{+R@Cd0-nGpDx{ zN|T3KdotQPDaHOzk`)x3cs1rvn#sjCM+WbMox|I?`*UWy;x5v0NUu0Y{`Y9`HId#W`!k9c~YA*#6 zh5Qb*r$n51444dD!BsNlU<5dh7f~lu4>=3IfIFlE=yFiFFw8^@&{IM8;v1d5M$YLC zgwnjl=u;INmYwClna{z3!}$Pg@2b?99J^3l*%W2W!9*#F4c%Z?v|?VqNen<{>C}S4 zruF0j$Rjp~tb_dq4bS$Pcp4s7^yby-!}!`E)AR^1%3lPq*N$et-eZ$owYjN+4z?h( zw3JDDl`1NaPjjK@i=ZLNNE9*#P%aE_wwR{DU4L4@1^KERvY@HjZ5qlz4Vo7z=%o4N ztaSSI?0#{NI7xbvm!Zu&(SKn0H4M=~(eqnBTcbioJwV@nw-y)}*;3<6!vjZK;A9*M zwj8c&sO5m}%ZR?ixhE%Kai7|P*shIx^KdZ7DW?3w1N^BpSF_7xuM`Z?QG%J)JeBB; z%hyt%1H*dD+F6%FC<6mA04cksCjJEU`ixMXFki`jqf~&U*h@so%8C~p$88inKm3Uu zB{J$cS}5g|0+;-xAdB?x0m@SE?FMkeoc z7AuK1(*Pj9k%BgR0qJHkvw9_=Pvp^4^9eqsTWM9db(vobUnXSo;;Z7xM;iz7R|i?s z8E(+zpc3m^kIXFuM8ZS_uQ##w4u8^8puQ};6x7o`Vt$nZe};2FtL5f? zpifmSFblh_;RydzxIgkS+va!L*So6zL}*etSF?Upn*c~KFuQXp(gl3IW}c2F4fXq5 zF?=o_N;-4w_%q_=WSb1bO$Wz5YMn4;BcoyRPb;owq7vaC=bfZb3tEXt-=joET|sXq zR!MOli&xF(p%kbWH9dAJ(&B!0DzIGzgXkHjpzk-=~&Nj&}cMjLERe@j1t5^4#{n9&IR^7he^82T01`E#^t1- zn$ZND_Ea26N7pk@bN1Mn9sBv~>hO5Hc2f(gtB#zeL`FHHVdIT&{A8{hi;?(>t@N_N zFN&lm2jLG>aZsk*Vqf<*|L5uQ^)lw@&7~dfI?hy)BmX2k{I|U)Q!%xc^j4m1l}M2Sm^y zauEaf2PYsk^Po@*>h6BX$Vm)Xz?tkNUHjf=wALI_=cfS8A9wPL#$6V!9VX$+iST6{ z4H}+q+rMVl9N`mkm8w2U`spzvKVS$X0$f{SVxWc!I&A?HqYHDIoNq^f@6b+6RR|o6h|`7*Pldko)01c-Vt}4bEvS1R!pKYv;NbOTzdFS= zQ;l8jAt#j0yCTY*I0p!2d@~i8AB7oz_?zmba10ya8b*J*=-%_=hnPv0fcq)t3V2ZO z8_!8O7KyelM_?wY{Avr$?=XKKO2j8UMF7T#qQlkz78l%`xduo0W4n_Ak?%ITAh&#g zqUYE6ZY2nT>ggE`G-}Iqd$!c{Q|EUBs=f4&#+_GZsg^&Vk76L`PGk?m(0ojUSQ~eL z-ErA)Fs+h40D=C}K+*GOY(F1@{N^gbwd`)@_K0gH8M|oDtUY0yl;Vi=$^%7nCL_RS z$Cn)+zW6(@opFvD)IhJ8Q^&$>_nrcfL7q&qgsGbEP5?ymDkA;cPCj?Tte#=$uqfI= zqzN*1O?EUOJktw1t33EICXM@4ia+MvBl(+BuXW}4T_jF&m!Em+WN8H4v4iZHWC=wFgSr5u zmUXkQGJ94}0~`ppeB+aq^k5upmzhwM+C*|GLwa-b4Y9ryi-%?^U~=pSH&TC*?I{>;IIy2a0uAwql0I3N3bzP4qsribfl01JCjcbQYUiec=M0E)!fpK~# z>Q8TavdNS$rg~#7f7}Nv@rUuhE1k*>hQKIAxkwLgZoM4H^SpY`rPCUV-sB8IHM=9p z!dlwM=ULq}NtvJG=IDzbrzZM6%c{4FL_xs%1teIKZzdNLP$HYJ$KFW=* z<2-f7ZShT}S#1!CmDXUk5>?Op_gP$*ubIsyk{D?1-^V_F*O2tb3uKB0D0==K4ND>j zG*xrvWVpDk-kZ8e1BmQl`h_OLDeDVR63Usd@8HcJrw8*i;=)a6K~GnI*l-h!D_q28 z)$+>0y$GZl6Z&BIyL9~3g%3_SU&HmJ>uU=0#A6C+2(>Ii=*`M@xn93q4NhLhc5Vdo zXvy!HuMx^03;!!09Im35YxPgseK~udWNTfagU%1Aj-IXEcEY(3YF_hD@A`MAmNg}P z$I8sU0iQMXz20Mu-b-XU;;Z3M^hFh?SyV_hQ}oA?wrAfDTkB1!i(Ua@afU)Rd;6@a zH4B6q0!|W+jmuQ&-zUGsB=i{5?ynUXR?zB5Nqo2ohFxn&EcTJe6UhhusCy6t!^4N) z!W6KQTnO2$>gE2~h=*BrgtrUKQaSsgi z07$5Spo@jaw{Cf?B$op&fMr9xv1VVRh)_Aw{{&>o0mUfSO?mTjagwimyx`&wzuu7r z*BT@%1K+cqAW&yLBd?w|SsKl@n!D8buYf((HK)a2rxB_j_y}Gj4IC}IlBZ3SzseCw zY31(sTm78B;@TE-Aq(_l6=Jh$@yb?K6!JRyFuvAMrSWR-@H?$5)b8H9Gqnp;>dN)5 zg!k-0BE+tEnJ%ndkMt$j#%I!E<_a zp~JV(3A86w9|~(osh0T&1#dUeZ6iUr1@e`dydHB$I7mBx45E+#1lq!uNk$i0&IN(W zbE#DwG>dBn7`vz;Ck$&GJ|VCzHiT<@)QXvf{Wq--LN2Ax*wW@;eqAwBNH*;OUrhn( zsl*FuNK|GwuoA($q3^4m+I_2Zj0WoV^it&je_x&$pRonP=-TIaGejFXap0!g z6BPaHRpx)_7a|7pdST8@xn6c*KGT7YuDZ0Ko!YND!ipS$PbP%&s@2TLq?^g``+nRW zPbBdb{h)bgrIWRsl3)iT{}Wy%pmmPv}lp$&kt+q zzikmYn3d<0n#m2f9)r%2Nv7$o!AFTiL}K>s3|neWsq*#Fjd7ZMa)NT{Kj{Jq_4b@4 znVxs|`AzFMWf@zzLB6+WuGbAtL>WcTf72QbMR0w@M9ifWM$YLSRWaINsyFP!YduV>!XTXiA7Ug!wkCt5%_}Sz^Ht7h7#fHx1(g2WIYD;Uodi!PN zQobXIwWd-3{)#K}G^NF*5JF{47VQsb=N`McBCEY?mal$5n3IG`J`}PqdyLA0>V-xv zC@fZU4g!^D6|_DG`9bRqRPqR#4)&e6NJh(#;_Js_b&NFEMx;28t0y(y!Huc*je2Z8 zsKB>i|4DOgk$#($Vz2s;j#`6HNPXCQ8+2zY2OUCiFJ|;VJ6;<}f684|LvDuLLrS;1 z9-uw89yW0OcX8t|=)+~*c3-h{mvpW*xU}HTLUJ3Mx^mTDZx@DKN}D+A?i8!=(y6#5 z4Y<8&TGyJxn!XP*0Au%epvxhRWBURCdBEPUW9ef%lIi}`J*$F8(Dsn8JtZRUI(-YF zXxb4T5b1kn{h-)YHR5K-y_uw%Jz5o-*@r}>pz(Q$&8m$Rt%Hfs9VDkUu8*Z{q}0b~ z+}+#!se0PkGF}kw6{CzHUpY(GGsbczHQvK#DK3{Uy=r+M6akKe3LFWhjGGu>rv^EA zxS7nX_K(*u9MSj8rwm5DvjRGiH~tpTqV_NEmsp+f`^NtncZYZ;7&aD~-%%U@#Xvg0 zM5zG~sPjD(eUX&sl#)N{A;^x_BbfTKswTOb(ssgP_*{gP>jeoGeg;%3i(=WZYw7%U zh*zk62V*B)jjxpQZXjw!_zUYvp?sD7QIvRmBny+cIz|IxNzh1);(eIU-}|bRxi&>U zrfnEk)#RHYw?KU4e!>u<1ZpT4SF=k`PaPByqH2U~OcLUq3$WCj>6EyWtiw2$;{KLk zkw+w7FE+EJdEBeZQDMZN;k19c6Bd)qai{=tZYWoC+Qn8S{2h{yTODgpM(;KRefO;c znk}Y>e~d+?w)9gG++PRnkn(t1#m?;n(R|?*r-9*yEB%K_dU;NepmjN<4V3W_1Er+W zVrQd5yfXpS)ff|zi-F_Yt+EFI0Zimc1A>FjJU!3CGVYw*ynxn>mrh!gACLeb(A2A$ zWNsINSYU|zi6Bx5@lJwous5Gri2Mc{7jX{8*KDw=jXSTRt$YN0Id;Y`Ll)N4qGMD0 z$pHvd?=gygJxkhG(I2%0*&W?j()4g4Te*3apLMhUk8X2#u2KRCE8%kLd(xpB%nfc2 zHo{ULo?O3x9&giks=%rZIw_oi{Chc8IF%QPLcRt9Z;#tsoUjHdq#)=h+#Ph9#y%!r zTL_jV6!=|(1pxj1oni*RVwW|ApvTXqP81)&eXjoJa^nM@&G|Mr6RjecBL({C;}rF2 zz2VpmL>l4Bgu={b=RbEiCLP{V|! zWQ8ggQG1(kZH=pBz2o=hS_f>?2C^s;MiVd`L4#(9t=G2(1U@YMwVvhDW2WL%x1j zXG!w#8sZUIGrT{u5Ncl}QGbIbFDr&%7c8K(*XNQwPRb`??0%A09K<1g$2pP+Jpt!x zo_Ae_r=n&dT{rzMHkCkC`>)q=WzuQD*AsbK-S48f`^r$S`0m7};C^%Yi{A|+c z8#^xDqk-}j>a28H!uvPs`fqto7@{N2Us$i`GeNk!PMvV8vP^QKkgq^)p8EI>Ym_9qR9EixGj5I+4-(mN zK}i=_b~C{Q3cyC8?|glZkw6<#&+NvLjy6^bHt+M+FW6%fq?0Qtc)!-(aN;QoU``=#%561C%D*2YYXiywDn_TH?N;3&D_Tp2{ybVd&IKm*p0qmDF zH|6@%f|xSDHYCU)Gi>veOnMhZh=HF}YC+vosSFh8At3t*I7T@QHlXdMJ^RqX%KoKhvdoSp=KPr?Y7W+urqgDku4DgWiuSzULUw*28 zJC(ct^Q>-4M3JJ)!lE?6sFj%IJ4%O-^*k1S{_GQiPH2rZQ*sSUMf6g2yzv*-Gygb< zg1%SGtSPiw{FN@mTN5CT&$Nc^^{8lME%Fddl7&f?iSdlz5#MH7!(E2>$}}_uYnVCu z33L4al1sEL)VpH6SctbOKwJsjh(9}6vI#N8if(`O<6f_%@{t7@FrOm#&}(5)4*auP zkkS#^vK;ykY_(|8RpWq0sGuT1Tw`W~=Pb2RZ-8(SFiIainjq`5kTljO1pQN3zii~= z;_B4Z@$ZYSriz&kp9H;kXD-KU-%jP;T&+LyA!SoKdRvMQNf#hGV>-;Axfr&?MKpPZ zj*lmfpV8lEzJo*|8-Ol{r8FrS&Zq}yZmBL~EhCuYN6bIp57TY)l+0=u2?4sJ0%fNW zUN@g!WBD3~^YF?slPn~Q00RtHGrOm!a|EglV$BeF>9U)$G%2sT3h9KAi4{N8;VR59iKmu_C7bvNsbaV#orS=Mlz56aFTOuO;7 zS4A0r$?uu0srdxm&n?`7p7UYasyuUSDC9e!%K?^ld6G3s23;APu*2k}m`YlZ+72Ic z+I`q%z)DX+7=1eC*A^F4rPa2d9dA&B3hd{3xqUiY#oZbgR%8-b3azR_2 zrbId|3G%(A!vXF4S4;@YcYtuW-{FxktZk7#p6;m?VM#KJg+Q+wfOqv-jsQoUn@2y<=jetKKVm;Qp;(rB3c z(uk&oU@#fsmhdkvqm9>Tpqj^prTz#y>pcAYBGo2H#UJGjbUAEkhsh%~0jz{!L$^Sf z-Fz$#g)JrHxy0Y`#Fd_xFzwfvm&;2s?RwET7ZqER4GqV6^kIWUCr|S=RAvA;ix#?M zR6I#~l9#=0_s7(Whqld&ycW@=1diiPTHkayoGRdA%y_C4=9{viC*lb}W)W&ZVRy92 z01qlHS0e*k(vPVZJ8FqN`i9)I)6Z6$yaeH!$-51PI`+>&U@3L`EA^RA|D$& zD)Son;b(NQsCbewxE=Y!28s0HN4?o(>Fz#u8p`o8oj?BG2G+=lxWQCwa_k~uxZ5If z+-`m;+7Xl|k5Tmej|{@8IYiFSrX|dZ}VsVD0TZ_xm=7~a7f4!wZvO;_UeiIj# z;T|Ms(f9s%K7oP?iOBF5*7F`=rRaEtx}((4G}2anw_xm0n_0N)^4~@z-vPtt-eT#Z zcYCuZM3yZ0?0U)~h=>6S7+13bBwrhlpxMWbsY@8vwu&4l(4O$;(3}4tI7~5seO|)0 zAal9z0UJ)e&GryktZhE=uTDe1*&G&sou=?KCT+Xuk~KHEJ=oaHYd-!J_m+~dzjH`b zZm=|Y&8nJc+5VE22T8+}^TQDeBnk;9Hml}(T&al60R#j*SaD%hmxHErX1lJmi{WlG zlcf-R#sE1%U*&c%FT^YHg4y^_7u|~t8zLwo1oSw{r{R=!>+X$F4@Xp|Rx`bxRfa zHjiHhZ>L+d5m51k0OC32-jL>HegMsb*9PNAsK8Mkk zmF6qYUL@)VJ^Xvao!y~eYZEU%cKc61-3jZ9-ctp&mIjKxD8+wD#UGPbbz9fn(mzn= zJ4FOJjDh)(U37~?eAvUWn%Jm=zjdA0%XrO@7 z-3FpAkE6&wW<9P7AA&vbpop-vvN`B^GybCSVW{UmR*D6i!ty)(-{(QU9%p)Q%Vis! z{Hi|zg~t9bbn3^1XMc@NN0VDL?xHk5EMds2Aak52)h!?i^HuaGLaRWznq7eIkDzV( z`ya`bEFY;0_`H}peYw13QF_Grw_((b4SK1rZQi?-dS~;ZYQ3!Y*_@4o94L3CfG&s0 zfo%i8gKppXWID@W?^pfFXo76o-Zc^5znwT_&~$XN$=mE2L(RoMeye-M% zVZGo)TX}y>4bYwvaX=~m-lnmR^Yo9Y(qzqVgRAFe=T;kcw(Z|%xm(NI=`r_DzAvJY z$jab^P7&aLEnMPP&pzQ2m00ytf7Lri z7H-^U9t$EuyOP9W$Jon=i2)pixbq9Yj8Gn@MusrweU<``HR*K&*1sdrL(TNi_=P$< zMsK$@e@1=Hxg=n;VFvomV-$T+_M<8`)XYPC89=H)E9;fhSKkjSD?~G3>lWv2Xj(}a zbQyzG*6Eih*>T}bb%z3U89);|${$q+bUCb1{`4n8my_(K{+P;N9ZW55&SE@Op4VIz zZ#=MdKI8a1j0(<@twrmwibYy0Y#kEIcy&$ra0KS@~%)! z&T`=R$TMWB)hwkV>k`g8T3ta{@>R%6o1l8>9uL5q+z#e1uH;JzlHdHV5aquH4nW~^ zS*J(04^Lxd@$Hq36JtEA=;_|Wz~bzx6!gu%Ezcxb`v*saZ4db<_;hX@EriOsn9|zb@h3t% zffEqjf)fKcIw<-59rfyvku>ZVT`!eY@uoV>b0enF{m?0Y=XPL5!KeRXYW3^{m3r@^ zt1NfJtN&j^c7?{>V{1;r@P&fdv7fzXQ}%Zwsq?|NLu7TtuGbDL_3HZCv~FKXf#^~o zQ8RupuI3YgOJ_*b%xypxPB*EuEhh5ZX4`p))QoT?-Jb|u1h1$NotCRqb+$>A{>&tc zaNGZdF6w==M}dqoBsQx)?rnHWwHS-(R!Z;Ke!x>(8Z0_dG*+0FOv*@gK7Fo|rNv|e zvxK{!qdT3t{dBi(U$ppQ{-2xr4!6(^oUXMzdh+?-`oxY;UatGzI$ymi%taf6FiZNF zZS&>^A0K4V_);D*Py-5^$yi1~G(TS0qzaU?GMtuJo#>Dj>L*gzpYLO5wamOq$Hiai z6ny3{rdAi@WD%oZZdA|{17~cRWSlfmgyp@=+UGeN!t$jptJ)(ba|6R`i$|h=At-bF zT=Z~_h)CFrcq7;EH-CBF7kS~|-C3NKXTr_;7QE3k1Dlho?`>d9`hRUt2l1}Cnng3| zm2bH8)!Cht7^tC93+i4QViZSTf-zZ%Q>R}=418km>$${m)_fRWd#MK7Dv16dUId;;d}c z6;YNdfMI1g#W7B;kG*mK_bmZ*1bj#|1L`*&X4w#pGtWqK^Sg(`xSn^yVueww&;<1{ zr{YAlW6+^&&k~^t&pd7NA)%$v-%L|FJ%-xzrD!wdWht8#OX)3_WhMCt&hLzdxTPC z7Z;g+MEvc$re=$;E5e19as~PIz0atm84H!s9Ao=efzgtVF)lz;+tVuP2lJZY@7*wJ$I zG@WC_4TW>pEyHBcO2^`!Xv>=zmYwDE_l1a2BBQ8?&8kx$Ry?CzOvRKeeU`6~?6fP# zCY84{kh?u+uvI?qDR~zuE%-^bHLgZKQQ^jBy9IfKd2Pi5f0QdE7F#BgaQAkm7NE4( z7j9`4jWh2S^V2GT4PH8*`4Ny&;K$QvQy$XRi0$ZFR}H6yB05U^h4q3sNBg19>NA}Z zcUW~2epUVS%K7u$%4TWS+nw>e*Cc@cIMPcX`N#)8m)HY>7O|l*p(n#Aem2 z-#)xWaD`%alf$u!07aqFi}BU2SEOOWQVi$2j#}Nwcc)^4zmJt+Kio`acXWe$mdPrV z2>k}e)of;a8y<{u!ym`Xhec1^Jo@_eUab_*lg^sIS9tA5m^-d4}6o#EDG)VF57?_@hQ+Fn20lp7bUg;Hh1&H>XD*eLd`TvWOj z)C_^X*G19uPj2VzK|;Cu2S=nWLEbJxx&syuUO}LyC=@;aKQWe2NGO}2orPI1-9rTyP_DKnF&3UrA&WY(WUV_( zAmGK+%*$nZ_1?rP7~+1uzp$Q;g-R2QTHol$AKe#?I@i}X{kG4_-KIBOho8*upRvKN zU+)bG^$9{@AW+NqbhoZOAbq5O;07L0P<(({{+hb>e($#DMuXU(S6%qV!5fiji_~YE z>pr^@7EODeC+{&+8x12ig-5G91rwvNPwY>Da4L7_-nF|Q021bu?U**2?#}ZKco#Zx_9dJo;!cF8 z`InP$t{4#*;=T@sfq&^YEFC2Tu%!QReCEZH{YV`+3Gy)?-Lh*PYU4hC+8kypTh{wf zYY3$!g`0hrA>-`nSN;hn0fD~1Y6MTXnE@^fNX%C+k5(H?BoU#1K?<N;2T?TB;Pa6^5@c{J}*YxB5XCn$oe&Rl40ajo=AwtjJR zdZg!bCckIuuL7Oo*HWhOCjI-gH@c=SCwVKBa=$Z{C)x0pDn5xY;=s-u8U?*^$7pG) zMnRdT*TZN*xhFTt;(V_r^Elv!ZgDhZK}J|~eoicVFc1S~)FnYYCQn-AGhskvUqg28 zTh@JvO8cfU2g@f^4;CoSeUpQfcUnwt|GlQsVUzYBwU}<4BLo_&hG8fQP@teh2az&~ zR~Ao~sM3i)k(d2c)o=Un*|MT9T1+u{4Dtx>oi@w))z4{#buE@cps@-Vh9cfL#6`3@ zyuNN>9=I3Uac{?NqkL_#&3vS{{G46%P;0q4D6F}ivGsY8EXW!+kM|U$Sx_L=ZWG&7 zQ_?EjhcNJ{9lZ#$Y4feac(DFfSN&F3S33Mc@*L%c0rI|D_TTlIM(dRV8Fns0G8sf@ zT2ig%%4#BJ)yxAxX}6c#Au+@|)-ceSI`gsfo|2|Jq62zm* zCx1Ir5m4=6d9*4>v0K5pFgg4_*@Nu-dmjMugTU%4RT|+xjn%&f;-3yS!w{CG{-Sze zq%&KPWL~u+y&p*Lj(eKk+3v=@K40OdumXu{-rxJ0M(cZi4kr1b zi!~5vg&u~XXr3KGgkFHzO4XKPh*^KD`_dG7=&vaL{iqd$In~4I7px?A!p1MNZm*?i z@bMEQ1bX8MhM_2Lg86P{B~Z-o_bGSj>Tik4POI*>JB557a{jlA1WFDu(H(b;&^1lv zhw}>weN-?6*DHTfJ^D|`3qTKIae3)*Dz}(cTqZzfxm4m+in?Imd8_7F)tN@}=VVe4 z+Qj{xxzh$*cKYu)O?e3P1~_hP3o|P78sOxr%WzFk@j>SW*o|SZ4Z*>iy$*MLAO%2v*tH5L^9D(hGkUsZ($9)T8S<khSejScQtG7>1(C({z2?uLpJ={%S@TmFJm`E2QlhtX$Kdou zFIiqpUlgl&@6G(cA4N)1fYk}v7=R>SE7|@rGQ*~Fb;(d}{pEW4I>+!glk@4a(=M*A zU%qi@6Zl5!EqP5zLmyh>=z*TaI|`&)y}zj5)<>0VYB$lJHJhkC8o96U38e(b7naSQ zGpvdSnRb&l3d-7V`|^C8ALd`jJC;0#p@@Bz`9WqLAQrK~=uypmZSPigpWJts+B~yu z!Se~fCQ)%^ZaO~k<1_6K^}fHBZSp|R^gkcIj&`}IDW7f|+U2u2rm6Jg>6h~b*5Ti5 z>zbw(wNa4G*(Da{6Zk;%|G2sif2jXIo=+#79$9gvVed_%tgEadWbeC5DVJO%dmLY6 zQzTnuyUcL*NkwF2XPyz+BjY&Z_qeM&`uzc~d%o8D^?ttI@7L#jw2TiDd}S@WvX!1g z5g==`4V%fL%%~wkB>v+{#Jl%7sabzd|3J;cdIZZ7?*?UqCJgIV)BsIWOzFpQZfKe- z6#_GUGg&iD5hyKqxj9p@uInrz=7v_3Wu+spe?>r70#m+cCW5CcWrz~ey`py27y6DG zJ;p9J?+Ls=XOALMmd3`VPbDBgS0LRT6YErbXlhPNK* zMN<1;j)3NXQY`SkMVUINAlqU6+yqL1^@LF#nJ}wq%+HO1#-^CuOg}DY+7qA`8D8Qn zjbfvJ`ZTp-e%D8;w_B*twSArq(m6p#3GeGtC;jA!SAz%+9s`18JTt`X0f_b_KOAgw zD#M>P<|cfqE%<)IW$MTAuGfDw3!f(kCkIsNQCV<~kbF5vyu43YJJawU>v1Xo%v>0V zU_{@g`(TY*sc<>}{K%6>*ym1j@l`_>iHe9i6VtNhkf0-| zkJRWfuz)isN}m=V6|o&uA!nit3o!?iX`P-sDlSghdVh^|=f4b(=~uOF053GnnhJq& zrykZ#OClC@X3vNmnO*M&WiMR!h$EYcAtf$!^OtK&9XwJm(-h*$>oAkwi(`!xL9{2u z;b0T_oTGFftkX)WCW<%fPLY11acU?1kjb3qPy`iBQ5%&ge!*Wob%~(gHRBS^+$01C z$Fa28gPKJp9X8i+*R*$M2*tV5E*SFJJ2M!s7qhL=5leu?78YOShNeNO5Eww}oLZVF z&@S$ow#UV}N+%bwH2C|Lu359t337D6fR&P!N%)rfqP9EQfsLN-Q30tczwz2V^HVfGo8o0QSC4hAXxpZ3r#kof@}1krcm>Nxpjk*^ zVBTm49jKYF(6t@CE#TWEU@5(m^v?D~TweBD@|sWQEZm<=2_0yUP`uuoN*M5OF%T?0 zMaM`n_lC&w&Uk9y{&uA2M;ZD>5&I|9>f{ILE3kK+j%lRu&b9CAwxkNV1qW#OLY07C z4wk=Ga|`xJX#=2DTGNJ~ zx11PIonX;XxwjK|k5aKOyp-+wi*Eslkdyz3~M5m&ao2nz?zQ zoT zJ~+5fgdf7Bc@_R^z+}#Cw8~|xmiqch^C+^uFX+DwDz!c`i^TrA=_B^Xxyn=sjOXJ; zZasBWRpxG5N}SiS2Lg7(h@d(q-D{*x7VRdkcbafz6}+pN?lzH9l_rG79YI;snKrD4 zd{qNAOCZ<2y`di@bpI}9@Lq!T?$quWv2l@?04u7`Sn-T!HCkX-D=^ima?Vh=ludvN zsDTs)&eg<<(8ZX;SIeT;t7rU2Ee7F>3R|~x=tbqpQPKwnBOm8_eKA|+DoI(JUQ9_a ztf$uUg%*I&WA2|Pxq+V$d{4E)oLehR-6kqtF1GFyH!s;+d_?YBAN0+=)5rUJdzWze z13JcE;l8q#4l$P3(&mBW;pDiQeTntzDgRLgFG7EDpQ-18-CyjzCGb&Zy4xzJemkdH zCYiT{#$i<;g@J@=^?Xh}Z&ZfIbkFYy2JalvcaBkXcYNP{#V$qZ4xIl65?kmkO zq)1D?cMN@X1mzITM~OZg$Vnc>4oz)K_mr*h< z+oc5wO0IUW;?EFyDu9Cr4&Ia!++#(300la@ifrZ$hdbnZDwBSlV(wDhUh}tj3;1~N z?T4`zA3(I8#lEsb0t9>kh?&vVwO6cSbnfxcv!{0fSvGERa+nc&yu?dR^op1EQ*1;e zkN`+o9m~FWS8qoIprd>G%w>&ZC17Z29Va((ajWw`J26AD!f%1T$SO{Jf4su3h=y0j z^t%+*Iq^pTXs_H?c4+_NiUVyrZ{efSogoQlxw7{jVi|WrW69cPVlDElCEcdj`duG5 zC)|U`9yann(1__);*i4{PHw|~sbpy~h_1&xo3iq$pPd~%>$!>+*sXg{xck-_a*+zy zx4^P5`Xu7zK>EztUZHME66;NyC*r+t-iT!rA=`QnH7Ec(@3x8-*d!=pxPCT=CLclh zFct<9Xin@yImJ-#Y)`=YN3v4l+$TL6AMaV1|6~&*3tOSeoC`Ek6z9I~z8ITMnz@|( zct%af7wbbz$0hbX71SpXk^Z%xQCh9p4fL8aZH)djD`t5RUV`poju)@ypt!cyV(I6jU1RdCv4BO`vS8EvBc5a)O9)LTe6tch}iaxzB33)B4IyKwl z0#Mu%(2Kl`f_?=0!gil275r}6j2PUp{ulruH(;DsP`BqspOeJizuXouMRKulE=vKu zNDlvT7_nG27KyD_xA2U5+#>wYXgho3ZL=Rakqy=1?ZH-FgKZ&-0aI-?Q_q&WYnz|? zsnV*l6$XY{dR1hJNjgp_RKO#Ubcnm?cp@B~CV zy1JB*lW~r9pLs@22_gAMy)#tCM#$zy6=L%zEO;2+u1|Wm_B-%dy1rrIT!sO?$bG&s zA39#X!kOg+_mT1Fr5eNl!wcb3IGKvYGULCeQ$^c;cXu3hx?`JRenlE|6>QoNAX=5) z1vynTw%~3(7QJ+?Y_D}~PU0vzqPf5l+WXR~sJZUq%d!Z~(%Er-D6S5K9^2FUxX-M4 z0~hDyW8N;JIA2pL5kKH@$3#DaO_WT<^nt;M$Hx^yd*ks=^c>lSBAS6vO)3N?>R#N_ zZNMaA`$n?E%Iordn`YJ-2<#ZyK_93eCm?TqGyBknF5qyvG7tb!q5?R)Y`|IcwCMu4 zNJb}J${nQhxc$qU@{pY`N3%AJ^B^|YIFo#3Eio^&FaYpQZdVq+ z6Roft9%O8iMwL$fJ04AQ_*N}-MPmg{PG?a0@=Pav$`LAryb_dC2_-N!e9m*PDQ?f` zZf&%6(E(Hv)I_%r8x3og`%Yaq7w|dKF#jB{D(5R-SxdBgOBp+$a_aXw?zcwm+D);Z zUp{7k1Hj7Y>bR%0#b1eY8CHA)qt2C}2BN9aW85aN#p~et%eAuj;w?i=d%nqUD8yA=&-n8sKwJ!l1*s&n;(HY@?3?}{uY{p&&=TfRv6j7 zhH#907VY5aHX|W2z|_gjcooYot{Ts0#)q2wvTHF}#N)Df_aei4{WDDg6hP=;Wgkhb z+I}@7C(e2Y#7%G7pq5`d)Wst2xec9+=W$sqW%%CjH!0!NYM*S? zZuLAkV#Y`-2nRz7f9dO(;?`N))y~U1PVWtb*s|nTc92&CE3501ukR{os+;6aSP|^h z6t0?sawXtklU??f!W+P58|LCFHd!=4jDUWKus#iWaZo39~TUz7x% z84)$&jPR8ul7$OysG`JuHn;AkWDml&+>3%55bc4H9_v~ajnz$ZZT(XDZ_jaw0jxxl z?TX4U;X_TcDKiqq2(NhN^16>Xk_ooqb$AczGfAwrc>b$!Daw-e6H{Ser~x9LUt+l- z7@~1rcJ-(7gSEaF64ey69@by8GnMnZ5>tt5+@sFZ-B)_-!SPJl#lEtZv3Vlp!gSrr zb!847sZHge&SGVPZ_Sn6(PI zfzZuwA&Rz>ZCb5$KbC_*_pnwma=ogd9!qJ-WcnLR#IpexVZfy9cQ-aUZ@BP#u z;&!_j;)Hmfseon>^e1sK!S2u*Um+5B@M$yZd=8f@tacKVVVC2mrSNULUbpsjV#QQ< zX?g*52BF7t4YcA7O>ri_7cbb@u)01T^Jjfbm%~JM2mdmDNTImRBcDirMS72Qa_PBI zAu!ExrhZ0@m?xgFYwE_R>lPY@Z#Eps$7`P$U4147&8C(5hz#;yBqN;7^h)i5*YVS4 z)Ei0tW<9T>AO|5!j6}CwUaHu8F*f~l{izWtXdi)IB&&FSw=j31<32Zwx-kmz#XFDt zXWTI|O9+IvYbYJnNQrZvb@HBMAR+E{0WX-yL)@?$XBgv|HLbAh z;_Kj$x^ch(y<<-jo0?CsTq%n>PmbPO;AMF&2MNt%ZqM-hvnVzc7;M^LRG6)w`vUvU z>2doC{|WDwH(i9h?xU27^ogn8?Ga-6)rePIq4Ri_2XS+ef&a`jd%X#8`)g}SsD{DDUebPIFdc<6&y zTMLic*M9OcvqPsc{>T)CgX0|6KUq7Y&NRYaFUg+W$j_Q$$ZMfFH~y#_N{)@ zMcBAXyrbIs;MG8oFc%zb5-fe=Z3s}yFWX4lS+=+?ym1(5#C-i~#4X8Hv7_oqb1nLJ zccYpvno%JzHd+G9x%Bvi?z8^goi$E*Bal_{y`3QpzL9_Zl6pO^9ADf$yAN+FWF{6% z!FRm5f|7R|F}Rniy=#QBWM}k*CacF49kM%2fzojO-di2(^eRcG^R#TVIB!xRFc;?d zXO93rRs@;`ReoY=P=LZTqshL%3dc45mJat5KVP~s%$Uq|9SEj3ZQ!$pj+vO?Dgqcz z@k`SwUY_2VE%X0&V3qg9iEZt7X`wK)4I4CDv>56FHF~TQotS-;c1UK|jtE5DdeS7) z$^AAh*YfOYmQ1*xc)nN7qvvf=yr@sq=rLxAwBqF$64Y*Qwu4^!_T5llN0e3 zw%h(4QMp>Tt97Pg+!nw?uxUevncOWCE1Y59*8#{0qJDVC?A(1*a-?W^3x76|w39gP zt)r{!lExM-Oi?gTyl`vT{G}>Cii)RgVJkap){9GpT<=a2t4-)Yd)o!Z%6<+ljjfd^ z+h(8_DP*EM-^2R4)aiv3_e-p9rsb`}TE4~JWD#$^z*y-zGg85HMy(C>g1InIE7#Hw z%(FaFsdAb}noh9^a}ew8jUu9q%H`<|QmlpOO&c;Sm5NMEa0x}D(&{$M%WfZf zb{~?BV>E>Sj&>%Bj9$+1>9CHzh!w{H2h+^o3x!*t^NBL@ z__RdX-BOx3jfW@Pm^BTs?BcDJ!IhoBMEM6>ui;~Ldk%AZrZW*yXURd!#wLn*nayTN zhi}Wc=+F*`bn-_%_m#Ee4q(2|iz+v%i91qgkaxv5&YXL&ARq~4yCI9pJBC(!d)Mv z9<22V51)8J&WiuoPDzdrX*b+n4)wYGv$igjEB z(Z(2K+4DbSFq!J1Zk-toSG+LU#iQwWwXw)Q2*{bUAz*piIJPB)`qiLb0>d>&8P^FKEj zkDFrlu+h_M^URmXz2ZmOHP3FRmBRM>>`eZ!o$(hM4Nrqq>Cy$@;5h46RZDwRYAZ$= zKNKajJID_+U+|7PM*&-(*i-6yerjjDaq4)l%s)0+@LT_Z<0)~V9}w&$kK+EvbV_3jTj4g1u9IyJIK=h36pmODa7?%PiHKY;)?H zwjcPBa`i?0rzqLoSOSE4f%V~m8;QkgF6PwEwvxcJz?l=l%$nL*cJU%Vy7B>H_XAdz zvbjlQ6+MsX4$OL_Tr5r!Dy4}-oB=@1LU6E2Tjg`%Z0w{xkH7Bm_|Ce*8eJ1z6JqV6 zP*m(n#Jl)RRY?1=k}R=6Lhk^*$Y54&Ob^=wi_OY6Z#Dsw@GbRH!j)%~4$lXY?X>`1 z%2G9n7~Dx|`^T!Chg&j%fg|)oQtN~(XAUwD4yYu#J|W8N-D?%8XIHHRyNV z>g|l2dOB1HjAO5w;T7uq(*!Z1h`Rl@R)_k5<6dJsN((l;Z#@0^o~7;#`bxSM=V>CB zxxv>#3)E5xcYV9yH2vuxkYJ0Td+aCuVV*s&9cE7-iqKoAi>}LPS&1JpNU#c@Tc6aU zWynA&SU(0^8={;d{8Pn>yXqHECu3ot);pcIVVuadZ)3QLtG#P3=Lgx}NKKL{24~@3 zM>D$Q&g2}zr*gEXE5n|aQSgngtR-D2cX}GJ+YMC|nQYosonA?+IqW zka5vzTDuSSUeV38WXisjeuKP!NFclZ3-x2b#&^Q1ZJ*&^Q9;wffL`Q1bja&F;IPro zS2L~bJL5F>M^2XL+E6A(g-F*jG&t=UpTfJI-;|?eQ7`CC8)B=WAL)5J$`;qP9eQyP z6{)3sXe$?G$lVncbbpxH^GI9F9;S6Ez~S@}P_rN$Z1N@dxlkrHQ6#mUq#}JNNAM4U zVscUXbkfyvPl*Ip{@z3Eeri(!^eZ)b?1nYx7&Wu&!>@PaN6_^W-u9nMPzl#^$^KaW z(?${fA!oeD@7Z#MQ%{`=fsr;=)0U!6Nl@YKN|eQazSdXE{*PtLUz$gcarnw)zy{5~ z{GS&Y+AC#AQg<&hfVlhGlzALzk)K4YniODwO`uF#JsS(mi6JUB9Q8{c9M<{3#BS7mvy~fJdc| z98M38o9t1fkd@NJH4@eTTg>i&MSmy=klay~WyMM~Ic*Z?Me<2REdz8zl6!unkS)oV z@##5flJ8jMG(lPQOoB$An) zuC8Kx-11XCS@Vd1+uXzELFv| z#yl{e20Xpr#RD>fqP9zQVRJjymf*3_wFbzAcGJZeV%hW41DK32pzJl|?t8sguievl zdF)>#g$;#hTO7KAL%!66Sb8r~Auz>8s)|>rdpXSG*Gt*AwiPnHRUW+4OeDwI6c@xI z5ute0w$-a|(v?t4)W&T+#in~KK$QIgXM3H~i>$EkpOEQNt07HXeZ z4B_=bGJlNv=12I~C}NwidOS&X(};$R<}q6ii(rngtmSoj9`pm?_cPs=ZS!!?T_<{u zuwJfJa@kOEiObYMUkxTA@M^MFdrVX+0 zxyR{*i9J8Mpr@&C|M6Fee~n5pL&8Y<5nFzYS-MVII;L?> zJcx|X1lNHf3pA>=WY;D5js~_|kRjO1tH^?G5I3xkZ)t_z`nCtQT+qiq!aP^ub>t_P zF#IEowoFNTApJheQOZsVfbp5#+B^XcH^6uq?t6cO;_Gmu^@0o`(@;hF(9dGS&P_s`%={`uQ9w+Xb8J{d%wZMx6GH#18WQymzz`N*Zk15L+aC2P`P*H2q0?sT&8W z!{8xQiK@+H?Qwsai{ouEdoOC7{N%#c=*R=W`l_TfbI&?fhu>0DiToGr&WP0?ut7PM zXLU^}(9_iDv1U_NDe9B2%RS69UF?=aQ!VgtEPu2#IgS|YgwqnO@-f>RKA-mj>d&Ze zAGVChx=3L0)SDXrR8+-_w#a2Ed4uh|6QU8r62$_P!cRrbwiF*blgz#|o;A%t*Uzg@ zi6q3;X2YJOV!DY9~!AT^|2Y@wJP~C-DRFSB7mUc}z!AaraGZ z5kYO^fP|!BSG|7A5S?#HFpcuPNeLAci6T-Uk{7=;ApYNru>{CHl3`@JDZ{N zbQBT-MKxqG{0Vff)ad_#J^fTcvdraaD&2@=rvPOboQYb;Ii-oDA~qTp@=q>evUlfk zcack7#CB``ezX$m1yRKhk{?6(JBGW4%NNiib;kZMx39}ihxey?>dRFMe(l)!P7cZO zAsm>;3;21mFi^zSN|p5%Zv1v?>)QQr&nNcxesp!6E7F=I-$OqaMpjRlk_d%K#P*GD zooG4AkUu>ia*ME^u>O;MYums|;96;r%>mqrqydQAc54Db_HNy|>f7nMl=g}nyaWH% zS5j-q1A`ItLl67X5M|X^uAbsuGUfgIKkr)#Y9pW?*Bz3>G{g(X{Ot?ztOr z*W&6wV7=!onMhzbp`-q!j5NTq=O;Nc8L6Ty1lHS0QUm@iJkNrYD2FlBujZ3kNK(UH z`5^;BtNeO)R0xcIoCfj@$oulThZ!tCx&T&fYn;6iC3>)~za_J9>(-e$`M{@clGgP> ztA(wR$!{Tng`Jz={s-)fy|={bTB7Wx#`MtnQiI8xY283CGDAEPca=_2aRAIfGrz7E zEz#H17x_c+f}w}zD@yvO*$*S4vqsSsDzZMBq>OYVhPy=kZHS&zQpoyD6d}1vykuTMD-JhryRcJN zv?}+1R{5%owp=i+F2qOw8!Lh7(U%V`k9)6p%8}O+%x_a9H?U{X>X4W?Y*NP7czT8cWU>D}p&iH4K`!o)xW>^HXd}S>!&2i^G z2edZY=#)ilZtN!%jYb?=WU8#EUjXu{vKLz-;RC;2udu zVy}8QR%U4>Rqq@I%}9bvmuqDm8=8_S-_iuI^gDYJZ9{AeT#gKyW2Pfl#;f3Yg{|AY zW>WZdjkdGkA3`tq^_~$oZxc<=C!mOwOD8>zn-XeO#zTL1aT!puFz{Mlaa%}qLBZ}6 z+(g%8IprTmb_{nd$Sug+)XLkCi0BtQC?G7R%VS~M{&$u`X~N{?tRtIVL)n2|1 zV?^1q4M9BbG{x-Qbvj1#@c2V=i{M8;v{@fc(y^|3E$+`gEdmFd40O_pz9o(O;6FtP z?J8uQ_@@xFVD63ck;L}zwJwnkdCA|nomNe=AD*ih2-Kc|$xfa5*R;3@dAY8%Q#<2N z5=ZGtRo|Qnf$4mu4rij~cQ|vJq+`H>SEYL;_r(y3E2BXXE@xXV@Kw4lF6KnDtLB6B znF|B8a?J0%ug1PrDmf002W=$v8*SxBaUN8vS3Gkv;m^gbosedv{5XhN;mk7owQKyu z?ON@hb4XDULCfn0J4ML{QX9xfWzeqDG{ZKyHUmEJE4NemQPw?Cu&G!h^b1ojqG(z zV6zQVMig2~x+tjc(iBTMhm3{be}v72pQwRg>{l~l9q8x|5~JI@CXXn@kticiVjl5C z)@OOHNR4z1qaHLuZz;7}eK-eCp;98<#m{g2All{mWJTiR|D93y87m^5Ht`~hf4i1i zT%~;e-2@PH7r$OOTZu0~>@!x}p46e7NPMIV8^T9?=X#0#axJgHIru-a18NRd@*qzxv(L+7)vaB{vi<2rfwsVJgiPV^kvo+g^)abZSd+)9Ou z4f_dy*idEs(w^Cef9uN|W+AsD~ z3R%W!z9zc_3h(S?ivMyI!eFtl{(m@<-dc%N+%K^nb9;t|?npR`N#}qU*edb<=O+F{ z3x;LS-;rg$Esy%_asM>upX*Mf758m&fRCUC1d{Ku(_2+f;fjxVaj5}x^rj7bmWppo zjNpXtH;+Bn25K2|b5-b9xhVzk!q{7)ISfeykZRj>T}tY4gOg@150}?Oo_@Ot992&m z{C!LJlQpG(NUH>rhtEMuQ-=xh9kHY5_AVJbarWV~F=nDdU_wsnv$L{b+~>=~nEsTe zqVorX4}VG%_&@Pl=W!SRO(J0%_{-I!86bNLj_zrLzIrN8g0PtoELgiXb^y zVS>x$&OR5^ZJ1r3jUD|LX~%IC~pq>?Kpf7#n%*G5+ z$_rsea^5*A1ZK8dpWldCzQ?)PVbF{}SR3&0GP8^IK1QAp{KVGpZgVa3R>x-u1L2Xb zuFHIWv=RygLXR;EzF@oyKF+=?+n~yia;wT}e8fsQ_{0X`u+8pmKM6P9C6# zr7$qh`sO`DCRA0H)xFnEyvvITaU%L2lrglA^gBljKe@pB~K+Ht4kfwPxOi1!>qdd`~fjINCOY-a`7L*gu z>=)35ymIMQlz2X!=nnEeX?`eI7!Ee+X>JyAL$wC=B?uIQMTZq6wmwqM&={@ZZTVl# zrlEaHmx^ROCysDN`TELQ0&2{6wEvFm*rzn{cGT{LzaHjB985@#Snuj*a&>&()$n{X z{Vqj#-Pe-V&sqd!>m|m6wqBZ0rhP3flt%aTsj7Q6 zx|I7IUm&urVwfls#3FH`Ux3mf8fxQai$!w{d2gEJw?+$cUBW+_-MB>rX-P|VLA}ETI+m5Ez2(AQMSov<;Lerkr$le$80!pO z0eMRe#$|X5&pmL)uc0^>xNedI_8Ii-+-QRHhSRPL{bHW!8OO|mL`9U)p!)sfUCaG)XP>Y>A!+`oTwhts+*lSDMk?5=guD6fUZ0(= z3>@&f5W1bmUnQKveCi!mWOG;vLQy)U)abEXE7%V{iNf!38D;nOv~o7vFye=EP(N)$ z%C_Fc?o#Le@~4QXR_=$ub+BmzpV`GLjZD9ODR(*aXE{}e@r+8tKUF9x4L?ax%_Jk* z(I(AYwqI+#`~_xy#!F>3NBwA%9~4A|z|b1Kx;p@fA%_vW{Y68G8CmC~=1R~Tv1u(imH+PiMeB=+Hv zV8-X1a4_Uev!k{J{8ONct=H+Qk@B*v;WaYORj}ga>1R%lj8VZBK|zn$uCr(wVcErd znu8T0upztjCADEzKCS%@q>(=I937_~!M8+2J;gSnRk(0#c>rt(1B1qCO&dPvlxiEW z%KvbycJN3=FWv21QYsSZc@auE-PVP-M6dJrqk*{NDp`41BGx5Muc04-UL+kv_tz6* zLS5-KDV`H>@FGq19n7s~hc6|-OTfodq$SrYLsOv_s1TS3tZ@!6fXMb5o;i^p>vd*p zdM`e+U7#ePZ?IhBk^Mn6hCgPRV%f#V#_5zIupOlZQ?4R3bw9tXcpXmn9C}ZmwXOQb zzOyrt18Z^JbK>5>cMy8)t&wHzbuh7HMU)p?oJbmov#FsbS2bGx!ij#i62u9VEX}K9 zb0=q*HLbDi;#vBT0{gnrru$`vql7Pu?7-#VXQg!{OmoK>ZhVeP)c z@|+(IhP)qNy=jD_xm%mE{&{{I0@>&8aX;`Lsak!@H=}->(jlQA*%S*^1EI%+^)(+F zTI0sd%I|wgRqX|_NaTCorEokwDpX#hcMB0LXX~1r0%et zmm?d5P4I6K*7N!Y%eC=)?ShwMb&CJ!>Nh`vfmj8G?0Km_6H84AJiDPmUS%a!JnH^U zFQ6Gj>x^{18d{`naG@`abksZKc$KP^Rvqy1bm~fvN(qmBm|rCNQfJ-W+0B>vRXDo; zSPKK`E!=~%1eWJN(l}PX87^LL3S0{jyK^ww+(Yx~wTTmLb@6=gDKNJdU_{(u&DDMy z+(ts!yh`ykfY}rq%GduCmDMm5Y7?jK)}Y@kcX!DJj^@2E=Mj zy8q~U2il@jr0n^DWs3<>`FwwxbB_EzWq_jIb*Mf$Hy*+exz z(~H*3(ka&9Mm#0vaV|>IgtA##|1dJ#40uRkVgPs4u67NXlLw)}FoaimK>vzy= z9Vc2M+ZQhLPof1e7Y5QBKFTpx#|68&{B*Ug`Z-%PnybyA`5(DJ=0ja{H?h&v@j6bl zigozunO>_&5N$UIJ@$bLwFXkR-`0w7DYLKx5>FIdFBsvbP!}!u&pkEuE>FOqJUrJ} zqA3t6PldoB`Qnh&ptAEnqLxY*^6SuBUexSR@WGn1wpc+|Tu8;%zU1Jya<6CMgoCqa zXwwBce&1@3eOH^b)Ov0wKUHVf`tT<`=75~;FNmjLYuw)Y#9obgIOiFFi5fk|ZEV%X z%INy24d*GbgmdYswP@zxGEUpiAb(vdQn>po6UXv!aV^yf53O{DT%rPeAccWlbf6a} zPZf=|SB#FYS zLpexiNw;{`kL*ca@j?QAqsjk)QUy$QDlr%W!-HsdRHzX0UQkXWl*av0ej*73#qfK3}7QUf{Q>bjK$ck9XMrJ5!GGy3l< zDHT(Qe&=a(Y;;zKyJKqg`fiMf#4`Z2XMknb5nBV?2Xdpak8T6 zLWN6{ivxYV8Yx$urmdY6kan;?AEWMb!oiyiXZKK0bHF5W*LcyzZTU>2!$aM?-nZnn zo-Quw*VlkB4@wuL&el`*q&Ued>if8>}?Vmtiud5l6}&FnM9vq?O#gziZ&Dc`7IW5ZW%3K4M>SDLP<8aC4KpC% zA$@i2cm?T0UF-glX>3l-!#Eh_E6|gmXXgjmNgwLILBMXlX}ZG;O)~*{ku`T5nNXd? z?xr9m&VAWqu&?gcKr=Xu40oFJsEOx1T3{C(WNehl!h7{^sVIYLY{u_3sKDAilvZoX zb!!zDRzp+B`QwKTpCG8GT$=m3+k6YQI5VU3f0)writ@jM)~g5nN2@0Kr=%e!kI2e) zldfYtacF^EfvMqG_oL5KkD&I&;o!|t*&QF?I{3K7zPeJu25z&qbT)uDPy7LS_v8i& zL_MVOSTo)2iSiEu`nI>3IXgtC5EyHzis#UH>N1XD!}dHSe2rXKUD~(LtduwO9-DL#X z=<%ErC*3W>K*e;X4flRK%Ay>>#BF@Y!9}jNdTqt=m673rjVWfxx`#65*i@66*MCjq zf`d0zpgTM$Ug{mGyNhF)&n8%T+UMpMHH()iD>}v$XE+_a$5X4pC z1LchLm9;!CyU8e+2(@*YzPIEfg@3KDyUj6w3Rad75=0@b(1khb+4v5>QH2LQqvJbbYan zVABRK(NLf3oJuGj+Y>#{uGa^}x_9o+4XC7>I5V+Oo}*db4w;^fZ}l7X_`2+*Y*)3-zW#V0NA@^6FhgDP)}hD;IRX+zX!|yn3HTd4PvkxMR3$*Ix=&V0)3?d&ytM z=kIqIjfwvcK8#poYC? zIl7c+vooD(L(k{w`#=VBzo(7WvRg*mTB-#engbT5JXQ1-s8vFzRD9ToclXepzwu%1 zBR>@Pk{Ugx{G-u_FX}Z`_HLb3yNC+D=BRqooQyp4oGW>E|H&fnKGpHLLHUelAzEP9 zLSS72W~8R-3zY(Tk)P!+2SBfbyvrx!)~i>C6}+`9uHO(kegNChTX#DbtwC&0Z~j3& zcxd4|{73B;NzAnw8#60rOcwhJa#x7$pf8+IesWIyJ^*qTf`j|C1ml=A z)p6ya=kupOFImI7qqGa?|29y9saT2Wy>3ObNNiq9s`wuRZv(x^fLoWJL$8C_yyZ($ zAax#f3Oi1X3K6f;6F!phczKby?pp5#W&0Gg2D}xOL1$!{4WhMSEevcCo^l87L4M6I zo|RwKTCXMyD|j|o+>NUz1lmP0 z1u1=X9u5h}TW+~Rl&7nhfl>ycTKD;K?HB8`)zxb&ymn%m0LYQvwBcu_6Ax+?{B2mN zlqiV+v^LvB98hYMM_p2}Nmd47(avDe(egCU09Ga{Kn-l#z?w573h)uzb*GuEwe3@O!NypeE1_#F_wuOUkh%JAru9{0a z>Njdlcsk~vwGmtS#gE({#HhnJR~Ebm2_O#6Iqm&`&yg{iDF7EV?H16B41eJ)j_L(} z8@lwS?Gkpu$9nw^BJ|e`Ir$Ai)@B>&GetV6&mQ-)^dCQA=8UqyvWv?lpP`E}$35-K zA#MTFf{Z&GOX1sg3k936D!&~dLvc3KD=j1Ycb2~wwXM-aC>G>p<$*q*1gL;;*1|vp zx}b~hgC4Hvl1h#fJCg9}eS^8_l}HMC;gY=5klGsV3|1^(M*VU1EU{l;=~w_Yi!9ei ztT^i&eO%|PzrV_6YD3nUnYO#u2)F%Y|M6qA0emIJj88C=k(TiRk&8D3EBQk6Ky6Qx zH@tG3+pd)k#w{lI<$~SC$$W5!*ihv7jDx+eE-2_%d1=^fd071i3Sle^6uJ8SG_ekS z^CS4TUs%uDZ<-%$Hn)C4o^m&rkhIyBUDvg&*TwI4!b)qSxH+L|LO?I_=IsqeK#Vzi z*U@7+{_m?-&Yp-+@UJ4W*82316st4$p0v;*7{n!<e#*-LV8Klv_V#Dz=`&QX4;0x;yMFO zz3wT=@u7)xjpl~XNHTAVF~oJu>_Qj_kCv|#?X8WPuV1}RFDeRv*g4_gz9WVj%z`hW z3noKz94C@G$`(tdjf_Oe3ir}?Z)jwG+NOSXc5rCh{9ja^w2jck^>!lag>6Lg@VWPS zZz%6ZHuE8K=O(eyFt9RrZ|dH{O9=@RE46FBP=9Ll82j;gSAdUknI?8!SF@Dmi^D^a zD^daEF*U&eY~i}YN$ES(D(POkXEFq{IB6Llyz-T`EJ;@ig3f{L>VBQ~tQF$jJ<|y5 z@9G+^ly)bNRHcam$-~Je;alolJjRpP#2+m=;krkPPa2&C(Oyu)vgZqZJAXB;2PlQP zL@JJ-j3sqw>gJWgczURrEp-37sn-Q5N?UbiyGf3rXUDrSdbAP@Z!BJ!q5(4%+f|?@Q+c(%n`@s+wdrz0$eHQX- zaKv`-d$`#9bjpLK=A4C#xR7lPA%SkvVLAQmbTqM&VA=CQYK*|&H)4kjo?}^lFFrQ@ zu1ID!dTgX8;hF?lccKPTXBICN<%z1X(j4|vDHbPm)W2ORAbklkZa&uy<4Yeaq4LakukSKn7D`pvX-j(A6{{;N8XAGoFh=!n*>~qg^o}Zj^_s zPXY|>m8IF2gz=XQ%ECp})TpocLPbF6G4nTz@gQ>nBzmHWuc4hzdHdvxy?_O;GLGTy z${Po1=?2U(ykE;h27HT(tJ#;Zt<;e#a7m^I&kL1`_(+8T6isjLEJ(AhhpFnls?$BA+}i5kGn$?m6EcU*XW&>eVrSBNtY0S zQQycd`TNwoG_9gEp|M`0_HF0>*r(7IJv}K#&W@{82n^e}#|6|Yy6T$kjJJWoxapln ziBI+q&A61vSD77fRCtgv!3gt~f8_Su=p$ssSzTY~4{G#Svo+TW(}Vx8C<6}(JC&+z zH*Wl;()Iy_YIqX)C|S_|&&c6CYwVs&kWc=G{lk0NIj3kDAH4CEwWMEBN`Q(`pHGy* zkX~{18YDcrT%8$Py7q{?f!_rgFTl=vc8W;u+Cr2&UJGA}J%Wk>p~v)dUzyUK6vTzH zgU{xkPjJ>kWjvw05zPy@=vL)oqlNIdYLnjZT8SM8-V>AReO(sIgLu;74y)tDOB%wr zMK&eu%)}0?J%ZY*%7Vy?Qmf2ve2tS!Q;pz-aw<_FFl%=FcL23Nb54qe@NJiyge$k4 zpHLnJ{|S_;6xY~y`T{{2IF0bZ9qSf^@R7ssb0RTaoAkzJ=tkM zUWID(+JnlOSSca=t+<-~&s|noA>z@$X&E*3vFzesi|G=ew}3k9+`Y&k+sDe+QNDn-*O0rIc7tg1bt(jgw}t-!Kn=!sN4~v}9=OyOS(R2& zRf3(B4$0xp<#9_E{ab)Z#P;e+ z#9eB(rd#BCmq~Y4*$Zo9+HGp8FzxZKcHqbG_YbRXj8wo+NMWFePq?aP+8Al`#XfAf z)B1<15Ec`D#55gm4wj!{q*J~QG*6wCFJ*-bdA zMK2V7jZB7bGMZM~zGBKZMS+8iW#1srU-5;S1HH(HJz5_??1IR-^u5nI=j(N=oMzLG z8Mk~V+dB?&cdrch``nhlsa;oz*_IDJf|{i@Z7|AuCC#Omh5hutA>^EL(#5-tn9HH# z&&cbK1@y(?p-cs& zp0Sifgt3!trZkd58`-A~*|Uy)qLL*<_I2#**ctopx@YwIynomCU#^*XF6W%bmyaTSgPzfHQdlCi1cXzm3s8a#=TVH{Qh8m+-^Ok^}v$RcZ+|raff9!p|>xHHvTxm z?nz3}T7LR7nb)l_#J?QAD8K@{-i6AYFaC~ilJU>Mb^kYaUcZPu>^>(S$1N-GvbJ0z zJye+{$4(JJ^`+I?=*=v`L-W?{J>e7p68Z$_ z(O@yhOB=6?USxbc=`4;$KEs{Eyt!8Y8f^?&r)>U}(aQ~z-KR2S>OXFI zm$%c?e_Fh{>Hha%uB`?RzWzo_xHkfcxIsQ9d#7#mIx!lkQ40U-b@^LJ+T~BXVO>RQ zxuEPnN9(ZUeBRLxg*%0;f%j%zBIBt_M%FR>nz;Xx)f|%O!M0I7mZbD!Z+7A>Ub9I? zi2l3na-P%xAG62%;|6Og@-{{-dA~gYVV8Jw2x0xqixTs-z@{E`Qeo!IH?8!`pMu)n zxg<`hEY#9=r2lW@AMFlVDNaehc<1masVnF^uS3w|PpPLBLzwV&kl0jw=M>Y=CcCo< z!PG3yNp71&sorpL>izeI#6$x0uLsRnxVAb?qEx#|;UowUcJ;`|bcGPec|FjJzsLW! zU((<{W6_rEFInpOi%AokP5JpGaV(q}0C$3z9Z9=M zRlCCI!QI21fgZ1|o;o(aSugSo+EVY^{x9R-2e@SUS6r~UK5w%-Y(nwsun;cK@8@wl*QjVAHX(czWpa-lxeDum>vnB?CEn3ii)g182g*f)-d8 zcet1VP|cWpjA~=Hr44{PDdfVBe487=q(Z%OcY4d*yNQB9=T?He;)C}5Ee=b;((t)9l3w!CySTS zYVQcNNq2449(lbjg|Wz-ya#+`$)Mcnuv!IA!?zsz6JBa%5ysT+Zk$sUTDg1jY5hO* zC?u-CDYg2|U_Z*kBh;U^6t*SRTjP-sq=`ejfEkpBA4iS>;EsXy^h>y!aF(macu_oK{ag4`K zEuxb5HjSs>sKIP{C!fMsP<_29Ot9Sng#BMw4sQR4ekatUwJ31WZDIv+gfj&n75Tbu2n?*5&2j9oeg- ztXJ~?8AyUgxCq!6;YIVL(iObBR3o(Nv=~$bEFl zwfk!Ff9G8wenTVV^%>+O#zNrU49Yu{d|3auD^ar#%U_gAjDt<`hX+D5qu$w~nAh>JJ@JfR)Of@70VUZ?Gf_SOlsZX-n^1rwiD&?z z1i?bWv!&LKr4IJYS^52}+t%iMCv$U;;an5R@mVWNiw$J4LcUerRz412ei8iYx(S~! zdXSCTi6kev^@Isu!Pd~JRMA_H?5Vun+Z5N$zE*yvf6piUB!W$yB@dob&sCBtB7d;R zGl)68h(sfKn4L(bQo@G-UtW^;)VWbh)~=n`OVr<+DjezzslYx1C>t3ec6|;OhR*oi zSkLJb#&r;1oHt^2B4x+YSebzU4wzL;Mw(h?@3e4_Q03QTxzUBa>F-ExeD^sQGmf|B zgH^Gu3JO`;wspTAGvS|t`4kZ;fZi)M$LaO$ix+{;E@DpG@<%4u64mNUYFuOicdadf$>SJ;q zn_$C=3Wv%bLp*@58SAwbeuF0;NVlBU@GN`6#0;o%G!v#nUv(gm%?9}SqC$Cn3h=R`GWr|5 z7D3spq)_D*6U3_aQ0k$0j{n?;odP@4-MdJ6&-!O@D6V? zA*}r3kZ7b9eqA})Hb?jLC4`U1ssK-zZ?D#OMo8_UWOHBNO3$Rit<}3oeWQR|q^X<` z&xO+f2wT=c^JTd>_iN(FS3$w}h4h2Q+5{LNlohxw_G5mpj1yS|xCE>|V$nkZ5lsS7 zJ@-Yh0O%%4gh$8SWPO9K08#w60&f4F?8=CNt6jrdme^gWNDi^zGyjROV3#9?ZOhWM zn-X!$)fV2~XPqXm-Y6q5F#{ijboXQrz%|%UcW$-rsQ3?E|r0>k(+2f*=Sp?vv>?FIOPCFapOQ&M*Q>B4;J6f zS~wNIJMwR!u&z{di|ACcKsPqU*`5uA`Dc2rtL7o`xx+R&+_W|F6o1Hwj3(R{KsKe&|eG?uEoa%!xK zWS;mN{N+qPg&i~Di+c^t-+{U8jW8pfdq8!{0CvcYCIcA(Ng~|j;`KNY^x%0wdQHyN zkfQNwRF@U`KdzB1Lt6T&KcKTHD5*|0>?W1Yi+d2{%i%Mf$9vjR>aah_ zFZtl1%%-yI?5>>1$mci|=Je+`FnlLV)yI8lX?oJMM{@Pc)vr(8b4fa~nU4_Kvvtc8 zpMI~H$#OpDj=Ji|eo|othCoJ^<4~B@vCCrm82r4>)%uE)Ec>5tMTy^YT~mnRVw|{I z&t|P=ft_vfMfrB^u)w6(UBcG^@FrI?K|AxqqA((ljBhWGj@U?iYZV1_Jl|gE7cIS$ z$bE<5nP~EKc9pF>+i=gr@EviSf43Cd&CcuFUYo##hr{iwjSvIP;4{hQ9EwI*<#U#* z#|Gxg=FiUe?6o2e@xXo~BQF2;u{cbKtMJ71E?x8hVV_PC;n8lMqnD8v@mT3zzZA>Y z+Yi$RMR#sDCiL}La5NqeX1p0)1rI)8s(RkW{#&$USITa4gE?GzR7@9#J_vMUPZ?e~ z2tE_fnQZRfDX_a&IVDAV>T)um&~)i~Aw$cZ@mRmyM{>Hkdb_V)4jdc+!u>3thBM*6 zv*c4a<#Lu`-%res+3hB5SJgiia>)pFS9{6`V;$D99_cR1yq?QT!>&wg#@$~OM-3PV=Xi!P>2%dPQjEb55qR!RHMrG_ z{NuHz#I)prx_~gY(>0&2;?NDywoSAifSkNOZv6pAv&jc}!Tk~If3n7ALz`q611a&6 zEQNyoKAPLbJYrpJ!O9DoilU$<4ov~`DR2`vR<5U7gXro0daVGtP0x26E<2Kgw+s3+_YcY zZY3Dry>PF&W^60Dv^0Il9nHkJG++GuU0le${s*%+IPf8MeNkKBYa}zE$k|M&zwtHr z8d3*8s}t9oN)kzLk)MzbOSaj|PmUOs(c!4}@<}E28>N{bu=!K zhuc?==3}dlN4`PR97g&KvSewezuufQF-ltf%I$D-^YvZ&qDAl}1#^Z|2$x=tGcM2U zrC<;c?@AEib|MHQ;t4tG=L&CxO3%ZuA^`8-<+x&}i-(uc2*!qm@u6{3X$~7kBZJP( z^;3;qePyceyTrHwkdvdCp#3Sb1sREh06y<-#v8I`ft^hEIc2<=7z^XLL&@>8ujMin z%WrCV4TqcU28x}za0G-Q&KslosHX5B2{iE)V9{scts{81&GZDG*>k|MIOwXl9qua~ z&(Kmpn0J$VOwi}3v$uOGtu7E2*FAW3fEl>TYF{mGq+5&lRZ9M;;keO}X!cFh{9A~i z;qJEnqp~Iu1{K2B!87_RXIe_!;T5T=)*BF2(rS&WYh?ZiQS50jpHiXwvxyav`kXvq zQg-v3XWaV(5$iACI@%^z+<+E=WX1n}F`bpF-tp|iBY^Vt$kprOR}sj_WE=_;>-m-& zzXkX3@a$DvXo1W29Sj&ISN&428pcl~-b^)U0bdOv9W= z^x!9;8!I8c2moL%kEyq{=5F^HDH86?aweCo+Fvi^bQNF39w)K3s;BbRY_~{#cmza0 z-x_PL=37z;{jQ5cZvfrc$qrKjxF|Uv@wOs%;{`nHPIWUS_WRGm$G-6@cNom5i;dsD z&#$$H*LbzlX}P3e?w-{{Q3xSSfNpI0(Bd5cJk1Hy(-A<8N~PDEj8jGg|o!LDI%!7iigi?S&;(CJR<-Q+|FCGj+MtpCV`+dH~F)Y>bUrfUvv5@?P5q z-a1`8stOA>7bx-&to_jca}T7r>>XUn+*?L@4s!FpE3tjhKNjx8ijYjjp)eQNWR!Wn%Hi`<)}wnOst`^CM-OxnD>({{MXB%?<0I z`b6oercBzRE)ykC+9@H^HX~n$Aw?M0VC^4sF-o2A_d`HO_;xL2Riijo`H}el*p88l z)?l1Gd!S`3)W^e^}{T5t*gVRyxd@Mw)$^{Sq6nMR#puKM>K~_X`FL&HIkSozkXmC~M#7k@KT3gj8-aOonVw65Q zk>EtWS4?;Vj&~)t*TvG&w?RDiKAMCcG=xtvCdwo4;mftW-~vJT$AMyiQCrIIfm1SL ztfD9YgvFbp`KaI30=04I=RlJTKS%8?{UaBU@$oZ?xuQQqMUFG5k^|5ahl1ayeW?ym z0kUO%r~Y1>>H}ej)EpcN^MuXX2LLya5Q9#Rkb7>YDfw#pSmVG$9~+@-lQN7o+`_{T zm{_Qesc5bHfYf*oUo8{Ae6GvZAPg}PibG-a-jop_C#_uo<9lP)Y$1b9(`|G@C`&m< zq$E^?@gixn1b%dr$3kt&FMMtRhPmb4SXdpmVR+pIQpig{HT_7 zR;E*Vo<1t}8R=T;J+Jt|;*unN0gm`d^O}`x)z(w0Qh2*D_2%h|5BJeuylp0g<(_;b zf+pHQ5|edwb2v3B^wFf1-({azrzZG>5#tzS@dUV#rG9JE9~CLQ)r;NHm*L5z_8v;jQ>Hr7)AYh-Q*X`t zRF&g8_-Pp_;>$_^2)iUlg!2Z3f>&_lT2Re&<<>WoRM+I)c00NF439uY{Nn>+f=Yep zS^L^mP>)Tp?cK_p)+tT!0*(pK$!cHy7S?i){#M4?A7_QoF!vWbGmj1^mnusuoO{mT zpt`cn{cWq?drOB^Uh$};rSx%9N0vIIVTfNbI25K4ejEdU;nG;rcJm5cU7B|DT;sA< zL)n+lAJ<7nOBxJ-amD8ZlY+WET?IAR1lwe|WF00|ZY~@ZVS*Ij7|oZJpICSqhyDRp z1!z>+D6v&ozc{n4d>r08kehPnOf{L|!8n4e{9POmRb2&D*#z4@dyYT2kJ&xvxnu+U zkMqS0SaCw^>QT1YM;&!cI%#E|AlFu}&H!r`78F0`oKHi<2UQOqm7M$L$E40H9Mr)v z;TNI9XljpQMWo`$XV?h5{$0nMHcb8|mEIXh;eN+OU>Hd&4%0F}Dx|!A%_n`Vvrh(Y zNpmmlqdz-FghyWy{(J#P9tScLWQuf2q`|Nb`&RaWV~GV$ABq`c5~ZZ&ecc0gyU%=7 z#qg-)+&0LXnsCUTIe1Em835}fh;S1%bMxb9;%R_VA3+HeVX>;Z92aP$QyF1gc_Sv~ zw&6c-rl;O2BTY@Fd!OyMeQX_#A$+SbDG(8Z4uUdt?FHJECD}#5WZvk($fSBD_L7!~ zma9Z+19T2VPQWQ4Q?Id9VNv7m%WAe=lOpVR!?o*h8%`t|sm$y|n#p)^35Sk?lt1?u zTNlk;NNs!LXW?TrT~-^2C8J-m9LlG{2fUu|(o9ES0~QNzFH=%`SrL*?A*_ST=m1Z+ zCT6GyUnGiT+YUDcOwiJtLKL{29j?&}15WdH`q=-DBrFZ9pXO1l(y|-q56D+u`f?i$ zSz8$*Ji36i07Lw`KB(V=7ly0LEIvc+^d4vn3aU+9kI`Y>M_&Xp3(rhEHTmmHIabIo zb=NXrmPmQSijXAYP#ESgSzCaG)6^R~DlS@ibiE~Skg}4kYP0n-7SOpy9~6itK<;WW zG1=-Hd%?>Tb?jg7BEkN5luz=9p_@8EgqvIqu;D^R;>mwyY(%XB;Bv6C!sW>(F4ySmq2Gcf}f(0q+&k!WN9ULi`2Z~d5|&xOHn zM>SOQg}2LZ5FpIIgj9*7^nd9%U-naOzQ4}oObo|nRK`jgdV8^?yaj$^{7vgA`f$VLp(-vv~zx60l69Y$_m z;C#UrXf@^!0uk_cWk^%q-TgIUIvc`=quj0^fpiirG z_xzlo^k5k**!+i1-)Yp77zug~OEQ8?d(q~+KRUYl;nSrjdV(jKF69_OhD8I-CoAO1 zDb5UtAGV)?6#zOtK{&- z5|Ed%Y=-8`YVzp6fqhyEg2oog_>9Bkw(FW$4$QTh69eOhYkRDe@^dQ$O&-5EFUm zzHV=~q9|@GDX-yM`*whAi2_}*bXisfyFBOqI`SWImw<3rHzhs?T7?_5ir3wcN6O_h z*{VL`tSCMB%_cD4XRSA0=N!HKn|uP+&~s|G?vFYaY)BvvM5zYBZP$$H4;~WX(YG{5 zHE?JbmXeavKm{9nDo+7$$Vxw8DQ|a`T+pxUlP!V9p{Zb1b*`1g;$5Sa`{4n_Tt5EP z9O}ou;y5?pI2wmu2lFX)c8>KRY~(WW%l7@(_nD7%U?E24UhySrn^p>yIppS7Mp;Q8&Y zS$Ex;x$(OD?7h!E_OZz}n2gPKzG5GJa84D)9g1wnK$cf*ucm5I00Pmylr#r$&g2nbcr<|O=a2x;Y z(4<1Q_heleZ0Xlrap*suX#LQm3Z5_O;L+KU&v{Emtj>tOZt&mo4{$lw3QF6&Ot7%- zm>)&=Q!P^~d0x**5TYTv@m}kjJnkFs&pPkRJ$<29?2EyYF5*x36lcdA4iLXVi&4xO zxV|bK&~Q9>qt|v#<2(Gr&n-61^n;GMlZ4zieSdm!vXA;~CraK{ncERJz;`pC-4}c> zB4giwz~@#UyW!BK-d*1J$n^})J0&4-nkkFmj4WuRWcjJ(k2H66d=VYW)EXkj7Ee?y-#MRCPQ{Mk_*%rVRTCXlzaIeA5C)>24%48wB{ptEaS^6pvrAGvC(3nS?g zm7hN^mIz<`xEYzCJZPbqLwwhH5WtU6`9vaw*=4*jA@x|OT{6(`$)%v%L5>eQo~{>2 zAs&`N)_8>!@QVVTlB=IvT$Jriq$z4!AaN<|jIj-~JM13{oZR2VEL#W|K@# zgIewCJY|CNiG?|^Ym^;3UO02_W^Mh|8Omqd~*`{G;3yZ10 z`xI&#z{XauRhf1joblx{2>ibZO~#w{^1L|gXIZS0l|;JB zb-baad+CieSFFy|BnbR9IXgWbS6iw!HXz-d*u(5^#UNX^GMXOpq3f1_KDJ-jz$ow0 zx={(8gHcGoXapFfgx5pe~5x2x)zupE2w@TtC~E!LBTo73*j?jc5Hp|)z& zL=Ml_QaH(O$2*V8@%eApn-?PfEPv9`tlz_3p2~}oV}nEF4qv9`@Z1$Ady*PK zT`_|qW#%T{=3nFLx8uvxgl27_wDHLP8S=-0F6@kIR%;Qqja4Q?LeHMqlGiFoo^N5*Gwsi`U^x`qRpTzsdamQ* zL=WMStj8Kca zo&4Cr!3Z4lH0P$wG9Wg}ORt=oarf^52*$U~s_bOOdddD=q=xqeEjEtFjpktu4%LV_cvrO7aG$+)s|5_I8#@ zEg%6exKa9Vk9(3ZvBZEsVV=Q+4?ZBsHYA&TYYX3z88Nkd-_sfegp%2bx5_6{!4tlP0!qbxUwyo=` z*{sOu6BwoV8zCoFr(+#_(qtGjuwp`1P~%1|6JH#I10iMEdoiU+^YWU_<;kKaYe?C; zJ#rT9kTLg}tiZ+)|7F^s&&hZ|0!RcHB4-+bv13vR^=*}M2Dzd`w zV)LS%`BQjimjL}XeP+mD0RAnz1{c2d&GI|+e=4Q`?bX}2CH_rxjzvS(hTCwKk_D?? z!*jf+potG?MA(|WZGhCsID7X@;X;^ps`PRw?!NU(d>%&$9Yn5OT%;{bfKNU!5`V=X#of5r(_g8& z&k^1Kw0F5Vd^sCF)#n^i7{4};x=kc!E^M#aoY7Wir~a=PWmrLUR!Y{@T8Ip#3Gt&) zgl^4H84tRY@VDI$M0iv=O@%@tDBkNZ^Q4~4xu#O8wb;3eo_nX}YH;MAEY0?6K2hO& zrw@ldzO{e9R$rznN9Uw%fBvO#$;!yr)lJ#1?YgUxX2?-fU`oD5$Jhb+4fH4Q9OnK? z>tlfK?{nc%ylHr+b_&asi8U&MDEGJNeZ%1Y>itrfj0^qqTq%KL?a#J_+VJ5*0g z1kIH2e@oeN(+}zabqBg*mp31;PPcRdBWg9}!jb=#fX;f{a`lb|e+j?vI7Fz?Of%3M zt*Xf#s~Rg~C<{|noi_kNDD7)L2d7}S53850ZI;2BnS+h_DdR6LjR>9#PL6PEfFgNR zC(rxi{(60Hv%5u(vJ*(g9+-b}3|`CNQ8~_=xZwMf3lQ4;jnxsck(x2_a!K4YjTaI? zmB;jd&YOO+?r|g^J(9913qfCy9>ppiHtt3c@xS?K%FGJf?Kkkds@v4RbEjtrn--MV z{7?r_JVE@O)HJ2?8mtpJX5L=|kN&ou!2a^xgBnL}{-+hDv=GJP^Ep~EY$Ralk;UpN zKJ@Hp?NaV>Zj*h}U*{K^OM(Wtjpk@2X7FawW`EAbMS>nl=&Z^?&|K&bn~jjbpN(%2 zZR&c}4Nh%eKTw`vs*=CSV>2~fyIQz9DA!o~9bOGDu(Kli7F;gZffZ=q^!%x>ap}9z zg=xW@ZADYlG|^;)JOejM!|38+bARELGSsqKUnH>(F^qmUhv|ppa>Y-7Y{30yp{hA# zfWBqVHM^6yBGMLGI9HK2c^YlP`#6_vS~v~~WOaF9T0>T0V5n!I%`EPzN}YZiAo#vL za~(Fh$?b2Fz9H`aGls(8@K>IeC2m$^qfuSzKY9#$jhT+$Sg#_=pNbE~S?DK2+Nk-}Ox9$|Eekg$ukA6;?7mS3ANU-BiJu0?RLsQq6 zzOC}V(DWNAp3nO)X=UCSgIZwRF68udDW%(9-&ku9=;6>xj>lBngJ51`WqF*8Ik(Kl zF*4vQcLe)N)F%`St>f@OX?s?Pa$x9teNGHT>zwX8O=Xg0O?FK zym(U@dnU1-5oHn}>d6D21Nvur1JS=pS|2Jjh)S?WnLf+uMr&mTBe2FNv;@Rm?K^9X z4QN=1-F|h~v0lTahaDS3RoTiG*+N_x1x5=?hgA`dX|Qrbdcgl``#fb@NP` z80uQ{uOxVf6LM3nPSFRWsx9{aB2cI;?3L0yoY46S2Q1Bx_HNNheJo+*i>~Laf@u?p z%+l4XKdOTkJ^+hz?`z{qL7waZaVRLE#bUb2jIcheQp|vbRpk*#$`?fJ8+aMx^*a z5Mh-rMqApL@8l$Q0`B)#p~Mc2Yu=MnmS^4AhigQE0nuUI)W!x4hZqgstT zIs5l3k_x+|BDY{y&H%>8Hfo{?s+*mex6~w((erY4cb~t2?-8-`ym`CE?TLJjOx8S{ zkfxwS){F--EhDK(L-dg*k{uHbja*E$A6tcuH|s{)`ZO>IQ6UD3cAgLZiV-gdL<6;1 z`Ik8aJ3a+-x+q1e-Q|zkiQcA~XB?YxlSljFd|~CLp==1KaSQ~GFByxb$FaNHA8VDZ z=Y#4w4(yrd0xQD;5-k+Qe+R~CU|p6=HOtc|B%T&yB0oW!5BxA&0!I7XvZbO35-&X= ztkYRoWHCdC5sjYk%veEH`YO`rNh zsAytv$MFsZX{j^UD6k-3STzUHpa5NTXFX#r%mke*UiJU%I>>y8#Ss63E50fE@O)YO z&m)HsrGTi>tf~p~zbhwB!_}&;0WNz1`ew*Csk;Ym^U?`Ji9XURx&gm-#PvB{4ZVLg z*C@0kjydzewO%QR)P4D>6k@Wa+Z+U)kbC00K+d;JkG6=5cZ{a9MC3fa{!vbY!5xX6 zE8$afvkD=a=Q_ID?R@Cmlt`el?nDRD%+B5%R}f@%+$0wr*C^>aj+KRy@mtV^L5|}dt`G@q!TrZD_%F+SXAX1 z+c=_lK6)%nUl#19B9Qu~Wh%m?6mvp&^SgGq9sBdkI+7>ipw}D0B`p5ss4FQjIU70^ z`MCTYE?fR-oi=#kZE$6LxOu8RQ-;-ph?N;vhy5-=fTw|%za&_I-=*}!5M`lgaREnE zR4;F2@EHo*NQv4)WrH^e*E%!W>~8BZD38JyuU$kP$oIwySlmC`CgN2EQ3p>wGi5&8 zf3g{K$dk9H;Mgp?u&Ub8>bC7bV;V_ZyR280|Bxi^rwU(KY+0!NjVou7Xi!$OtY6i~ ze}yuPMQeF1{L;2Am<0SM@h?m?Y~>~(w1gK2?U9N*POy;nz;29@-Vu0#^WVV(LC`Nv zyQ2Emu7X!D&)b}px3(|B++CdCegQPpooBtD(kJLWEqjFziw>zg6fq2h74;c_`-RX2 z@whUGQPTTX;-||DcZC>~Z|$OdzRgrMKfRBr*6LH``b5a^0`(seCM)(=oo9ahxM~IS zj~~Pwdl@^G2tD~MhfC(>MeyZNIr6plH_``*sgr!GF^g_-BRg-31jL4(l>ondXJ6`29|NLtq5e|B2+ z86o{8R^~e?6HAgQhUaCq5MKzZk^PeBR=eB^e1`5LOC@UQjQ1OHG2VEWXcn*zU3LwC z_+dZ>I`=wu(BTFm{%U#NK%>w54A|y9?f^E}v$+T@&|v~5PZjI&BQdNNq4-Gf&K{d% z+OzUgSKgC~RUdT6{(D)gu@OukE##T!$UF@CEd0scf1Uej_(7IsHWZVd($U_zw*JTZ zSU8KM7;&r~#=TPcW?qrEumelYe>E9x1PA7`zBcXF3i{sB{j|`k(NdkzjL!6#Fngun*~-3E*;^rYVO*D%Pj|NCBd0P8Q}^Y* zNmse7Y^Ul>A8iK{q=bFyc5emEGx)3ri(XalQKmaI3qFTx%(D$^aU$o`_Pv zDs6p9x9eypSdXBTZj5zj!UZw0&aN$>I3Z?yJp<@@o0gc6$T|T<_?P!uA6zJLE*c5TYv} zoL5=jB052Gz6&o5whmAluj{OPL^tNf`1$p!?xIH5dYZs+ZFfvPUPCoC{b=Z%)L+_B^rqF*rbnsacjU2r^g5BCdvnbu^ltsC4%%XrbZ zXeK5SjHOnt>g$b0X#iUvV01`TS-uv=ZxFtSEg2PddzV1S)8TGlTf;5mf^d{W%JsYo z#|Rs}B{M(QAxNCSb~KfV;^#WMDEpdnO=Zr-7bKF8D+B@Ypg_yKk*srSZGPg8>Rj;a4C zAI?r)t{xdW$QtwpG)l|h2~&#fx@<=a&LB094nh5rGaO1s1;56VHU0ncVMfLE zPE{F)i^vjqa-y#o1iMLcVLgmA>1!)t|kM4^>Y*KN1yRY}`F$`-q7U5TFXuchjdge55Qr zg_{R$vV>$R?~Xku|6BS*tj~=M2>mkfOC^|>+Tc`>hcw9Ulv$z>Cu&)riuYDQRw_L5 zH1H&OYIeR&zk&ix{AOraatzt-^$N>fM5Zdf+IqM|S-s6qn(UiP$Mm_wmC$gzLyS}c z?;#A6{b0;tJo*0isX?j<;yF*q?Z!H`Jc&ilNWxMrlGA2{^# z+lsf_bpj3!Kh}{PDT{WD*~u>klqb$J%JLdH8NcecyG}~(H`J-B^A{4l`SR&Vfi~KL z8i%i^hYf2z!_Kp05%vXIeUm*Zb%S95DN`lNzvp(l?R=wozCTzq#knV{#FuZ%eNAjd zulo&8qGaOM^qr8?n-7*ZU#LvO2(^pNbQ3xTV5IOT>#tXAE;fI0jc=a*`OB{gqvnjH z^CkD*oZGfjj#WD^Px^$a@$4-T=Us*b7u!jGZ66ZA>e1|md(x~jV5?;sBqep=uu=td zfUN9JOM2{O@u@un zDD6+h7eeNr&Xjwl%6u(WXxP9-H^y`>&daFKo6Ku)#J@;17qC8pmNM${r}yT|WE3|_ z)BL9RrC=&4$LajosN|FP5RD0PedW=q!n)}i~89#obOU6U78!RGsn^0peOnNCn|(B`w= z3q${47S78h+-?C@tbR+ErBa>6F;E^fCBJG*(c|D%67X+9{f7n}(Q`jA#H>kb+)B zQl?+{R)V5ImaTTFSZI@aS#zYKz1+yUUM47XRiEV#(IVs!{)&T*t>Pp0za2nn8=m0B zls+7vC+4%)Kh@yJuPU9fyl><_l0@~a>3avrfJGVpNCof2*vMB?lid^hr6BAa>s|(z zbh(T+E^IZ0XpG1q`lS`O-~L#RNqsKK+z_QeVyTM(!hFsKq<4J-3g14QHn$@&#S^L+ z@{fJuMG9qqyoL;9s9Z@DyF!HvdT!hzip&uwWj8GJ)9M`Ib(hD2v-LP=MgzWG=Q)f$ zg_=pMH~oR=A76q^_P(>CjL2iM#iiKYD$2^FBlKZrPr^ z!yb{3)$=$+DPm?It8PA=RcU_^2DKzr-6rpltNBS8Co28ypg!As(~0H5Dtt$GR2~KX z(j@-|j{PP`u$@f7zvycKxA{xO%Z-!ZZ$9sniWQE^?}OeOj>?x+tv*W|&DgA-)E#^E z8)KG5(jvTu^EAGsJ!t|QE#{D7VUMIj=xpLFg(pmnnUf;*^{?W+u`C7*{=eR8rwDvt%f=plQ0{eot!#7(s% zKzha;1Z*4b%^@X!@rTe=))72jyStPlGM?k@@wj@cFo0o1jiaq~amUKYr4`)=+StuK zUWVPNwaE&if_af@gTpy9S#y+MGr+xM%*D9d`rIZwH$|~cJXRVmYhf>bMxz_X_NcK$ zEw9mzs7i#y%{6qRjC+=z(70hr*Uc&IojYkEJ-+O{Nt7j-;qvQT`KwMZtb-iZ{tgLj zv=DNiUsgq{MOwA?(0HjoYQT?V1l7y>o+)-hOjE))CvP;EjUU5@pgG%;F(#vgCvW}v#FD-*9PC40`r!KeJlHBuxnP^y8<<2*!d;SZ!-wFwXs`OnA`hQZ@_HjltsJ_ML92QD22V)si7UBB6u;nw)VAlu@ zn@+w!*eT%`Wk3%bM`HDG`INI>UAiCZ+&-W0Kl5t6u6&*mn!rhVmnA_4FX*^j`g@&o zo2?gMZA)ULZYoP#ictzOVtaB9E(t!p*3}Ql===5i&a9s~yg|HJf&*R5!uNpTS-eN;e7axBrDkciw{ojDS0v2Jq89uFp zMf&iVBXfd2z8@=^C{SPwAf;JQbTMcnNRPV?*ED!subTtFc2Po~lpF!Vj^%7^!69A? z+v!d0BQTD52LA##_wv46U`a?fsoErSxEtlkxq=-1$XRp0lXU>-t7HE(M<{j&{YcFo zH4)uV$a47O16LumO2v2X0QztvSO^j&~U%zU|}*}TSoq8$1zV+sVoV`-V^ zz)!)?ywQ!VL!&vGfSMMoIHux3 z*~57u;*;(JTq<_wE2f02qUz8&$H9uDGQs){Y%uL~bq%T~;yL3m|MiHMZw5JkAb){M z-jr>{>5tZ4RF9Ta?36;BW(4!p$!?42I&`6r$+@awnSTKq=Oq#0#4fZ^V`D3?yi!`m zpkE+6`76x)FPBW@abHQ@jkVf#Z(R?|xZ{>e=&IR{2z-xjCrnvgI}<>wJvUXPXAJ&x z|Ambh)1Cd|VB6;V-6Fq`=!ey?2-`ZJ^vF5R`C?eYfaA4{zRrY-hmj3bX#JNx^(br? zNcOH>h&=-fbk_}BDA0_K+7JAXIF$r*;_=v4Nbx7Q&{s9Ny<~y{RY2rPh8xR!c}+ZnOT>ah^iLrxKhiWKc`kn197RX=L?!2=%E^ z=Nm^696fin#{Y5vh>685XY~yCDIV6kDlLqH=|qQ5V>e4Jj5O|oLhK9LB0kZ=e`hmHY(M;+O5+0jLNU|zzG5$ol4+@ zBn}e)>c^Emx^|fripn~ap1V}7o_psJ6TDbA&_(6e{fun!7&bNy`cMQym@Z?^(1=XLKW#Yp^h8u0u0|#3kLKoGRoXoLANON2( z@8^teC2yvqRc8<__x!U1D(~BSHHIVd`Ay1Mp06--?NDjl+T68GOW=u^ETNu~#lEU} zJQZ^73e($Vg-)1I9I=fV{BDPq)biCJJwyARw?tQkl(d^u=b5b=@y?I^Z)!1t1^q$) zY>p%}HChDvV&i@*6cjvl2VBE7d8x$`{P~Me*0yLwO-Ax!T8w@vk0PUVRy2( zPLB+|S1j$$h6;?b;}393QNTYJMwt_9lM>87zzD+{pf%K-Jx|Ggr_w~+RSq$ehOP_^ z)0`KkEXUxMmB4YPKz{K~#_-rk3Fbdy0}Fcp`4fZ8ds(YWp$On99FS$D3t&k=ms*QR zs3N@n6D~j3OVHq8Qc-K=!16dy|W|J^?zu$|5bg zi8u66958VhBnh4DeT0D5JV-%60Fov*@S&t(GUn~aPlIMMxC`pRh>4STFxNCP#A!FX zkLlx2D01)ZrZC)pE43Nf4}#}HC7xO)CHZyR0O-BItvF5Dj9vf)=?nDdjx~&2qnNlLSuGY4{_LM$KTCG>gG^u^=dkCw`;wHA0`6hTm`FAS>bKd%pb>{`4=K5XlsKkSj+w}Ug=0x8!$MkLa5k+bXs z@H;3N9JoF+nZPCa&`EGkCIS;R-`2r$3cfazoHJ=bKHYM6MA2yHB8JNlqer7;z+G-h zV#%NIiOs!EAQFDRezy(u>uyt&m*6_AzcYabO(V!+e^h}<71l4S$ld`b$xKJax@m)t z8OVTnuv{*g@yuX*Ys5qYH0@mwGyqyco7-`GqA!8$oE3WyLKHmWPS#4o24c;|$>2<` z$P`e=sTyt2_;dLN@_BfzB8mWfCe+Jrv!*STX}$mgBen0_AHTnZ-)utt&bxg#e5(Ru z>8^HA2kC@&Om-%av=9T%p4$rR%mVh3q&ksZgGyD@UxT;CtDd^6Is%T*gkW3uw2~KV zxxnZk^Bu<&R~x%E%#YM?bjrmLx97y+;`yfMkUkxh}?Dwa3qM*n zYug2|&x?Um9_Q~RT({GIJ3Nhpk0oIJ_r{w65MGZPf!v2XTFECF|1%ly%?MkGi!9p> zx)GBgpB`Y~eRmZqaKDp1_$iwfu{8nBy}eR119m%#fekzA@795jvzoxebl`#14IF4H zR=v~tZM#MVs`J(KNNKY9401vXJD`oF(U;ucA~5y6G3lO84|)KBuVs-N`o>o!`chYG zf!wg^6*k)E_}Phv?LvZe!0>A2(>4v#e;<(^>~;nL2E*Id4$Rt>7gRnLh_rkLyn

    Q=fq3kciD`Rh}6QJxumFCq0j zu0#u5j{@FRrkg%b0Ur-RNO&#`f-ATefV@C%T$g%+5yQ6am&v)XTU_L}Yy0NINK>wV z4J&5QbcAh(?*ZPv{TE>y#2FM;xKW5axyPYxMM(MiN+5=_o7#7{r6xbcq8}pI0>=5W zk9UzLV@C}7h-UcI{W%NUw7C z{%mG5k=cLWfzU*3l?S$dxj%P0anXJCFzLc!_PFzo(e`7!6XJ{(QCPFud7X*8`H6%d zDnXy7Dtr+muE=3cDfd;8PZWNzvIwbT<6GyGNwYR&#i7z7API2^pZp3&meRt(K!1}X zNQ1B0rO|y5>(kkyU!Z2-t%`lg&_CqaGU|!ge(Q*6LOvuwsUJLef$P}>9p~9*&C7#)RA1_VyTOu)pc7ZC?2BoAYFO8_1oRp8HzW^}BDmwE z4x1{ZS}e~3i+n%%NFn#@T;W~2QQ(_ahWiI@Fa%;bAyE~#5im~xzRV6p>`b3X-9C(i z{h^z-l!0LXeu_ExrID%o#-?!^iQ!D0hU2C{o7=I-cT42)4eL`^nEvK{cT}+7^=9y1 zQZDUNB>-+IR^=q|oYYWgCVX{p9lV>w4hr6eA|6j?MWN3NmbO2EPs?Dy9WG*VwGyz% z$L+UO-ZtR}Yjh2QINw}GZTR;xEUv?QX_4n-lqKHhIl}uJ&n{Gf{rcdC+s(GCualWX zu{XzRT;ihvP=Tt>uVZD#;$HL_VlAG}*OE<7f2wg!Mf z+Yz|~#A28Za&OB&i)^G?ak2aV+bD6A1JShj%){%*I!@ELTb&2b)3a|kG&`?cHG{{m zk??C%^ox38xMXTpYIl})P4^VJ?+ivn7Lo-$%osfn)&&no2${8c+_g;{J@1<# zp5rHj9;zfSlFT&kwgxnf-Bl5e`Y;Z$8RY1M?NSqM0F-QVSqinx98)E1+|TQaz`@2m zk>{QSEgPoTiH{-_Sve`4GGK$4yLAjj6*iE zBTtiUnF8HblyX7mPfM>S9@;uPvwhZgB7~5qw5A^ONHCDTG2JdOCg*_|@W_euJHa(W zEFs}j%bx8?@l zbs!^Ozk6OFKqTedQRE^>I(_!WUcY;Q(*n3U8HKWfihTzk4?9j*_-OsqkEF8QCrmob zq$C76xmzv{>Rd!DcSab5%wmB@&k>Uk_iI4K=dIgmD6T7_tu8EOvOZzW>&ntqJYb{Dg64(~QaKCU z??vssa1Yq|16!O>wg(^vCdGR@;jOl?HX-VUvKo;J$!9zzO~7p$*#@j-)PW`Yv1@V@ zS;*ZfD5&OhyEknM-i9Eb;}4Dft}_z`9=AtOUnao#WW9-)%tefjGF$Yi&s+;Zqd9fR zPv$qX01HW=?Eh$MnN8jfqhH;F{Uevl$kw(@@24PlAx*E_CAQAcTnYRFyH?RR-d)^< zfXur<5?5zN8$P`ZQcFNN+3Q^7KriYP=hu}9nFMT)vx5-ZLAl$>60g_2FMvV&k0tlN zDY-=+$L=NT8%xa6fc^24HqhUINs~4c&ZJ+ z&AKo6vUXNelY+oJQxXeOG@OK)i<`ozB?9NmMxdlEC%GH_{l zp#K}z8XNXsO+fQ*PW=%}=D5vh|9hBF<~TFb=wE6b%l?lvts;XDdLNckY9B4=GbOOC zg;?NyQtLcD-73p0YkD4TnkkbBEVq2M=$uX1#HZhdS-R6Th7j+Eu#H3)WS-iTwkwux;JRnpZB~u>Vd&(PbKGh z$~W6C2BShD^m7%2+J#oA z%QKw5uUh?=5>k=w;{8^PtFjFvgTHl5Ig=AAvrlB_oODm*-RiRUz$K&o#I&a^CQqaw zvnV$wM}O%a!th<(A;k@&X0xPy=(mF)&1B3sQ^FqslFMO=tHRYvN_oqPK4F;HkCRSX z%H%O%LWhTS3A=jX4~Wx5{7m!I$#dT=_QfONrz4u3$Dl@N#7=OOfYL(Yi^+g84QYvi zhV-2-bH4Y=R*6w!R;Y|$@2#%zZDiuqXD_>pn-R0tyV7EL)O}! zqe^!)hTEa8p~=CIt3iK|X~_0M+#{Ke&R) z4AX@lez@B))fwwmMdS$~cde-3`pbnn%DHQ0boueemKir!N~nP(c6o>o!^aEfHM>L} z>I+2IrMGgpGqrhadF|p~&6m{ET|ZXPx_$hD*(5t&tAxwZ3O#V7dORi+1T=J5_~To1 zYVIm#E(NxYGh_F8y)7$J713}Uz*c4u#ad&{86l(Si1wir-U*@`kVYS#q-pOQdv1KK zJD(R4RU%_?E+e+A^FZXZdadO7=;T9sS!LI?`BrLQ=}VXoEF?T%uH#zCzE0%FO*xHQ z`+wLIwBvhYuIbtM`6e>Ik}!!r;eT$Ox$V7H%3Q)5hG-1TI9Z$sGqyu%+JlE!HtJ2^ zHrm%QFa+)2Ia%)oiGZ9(_k`9y?w?N4&}wMyCtKJu@TO^s8;3_}Vqw2(Blypf^)U-d z(nR&+(0KEjE;|$L)tNQK##7~Ek<;ClB3^nj_ntwU7RRZ`#F!iP)8K(S#-I7>l;lYp zv0)jPnd16s!S675)H16o8x%A*9lcz*zd1^J)mW017;i(LcOnt2A!@1y8As(D zCH%Y>E2Q$SL&kpywCpP}pTkjp`1p$vzIZAsl(p1SDhPDi1^ryY9x}zr!p9GnjI~^; z_{YcXwxY%@+Qn}!_hn$nXNiN5`=!b6lY5nM!znf0f_-`3YxCmKV$T>hdJ`M!@dokI z&Kwh#c&-z0Fz7NR_N33`z02uPS}uM9<=L|LKeq7UO^VRBGpk|M(0h1z;#}!no(T88 z?bZTmMpk8hMSo7>>Lu~QxoVTfOOQNcJ+QZ>8$p6Yc3j8cLA5IjH`r zC(N9#nHxR6^=rpH)Kc})qSJ64Bc5n3GLliXH-vYdggn-q-CvxO!0XhYmzPd9t1vh; z`t$iOBGhQ96=nG%bY+&FkIqCSAMU97=GC1p9X>qO=<2Y(3So7caoZN|lI``BSABh* z=~azfvY=V3ZOHxpB^5Nha=7V?_e)hqS(QrARP}esObxpBU=5rTUAw4aM=NYKIN+sr z*j7I5GBoOG$%|C6nn-#MM6jR3Dyg#(8HEvAA|S5A#XLoSq^7dTF|d0qK74N}x0(xt z=UVn6Mf-eOv(3@E)OW$E`;3V6$-~A=aLLX!qoq1Ea`*J(ix=&!REn&wd4AMsxMEA* z+w{05s^5p~zv+Me2I>O6?6uGlr_5KR3gonKMF&`1AuG@Tk0dyd6YYy1RJ%cdOS2*t zf&(ha^U~(q>N3ml$DGeo^Su4Wx(Rr@?9^m&+-4JpKl;^qI)nbTGsRW$ycy7!4CU>d zaSwmE(iG^^e|*IdRAj!LMXC)ai@ub|_ukobCW5HRqzU4B9{a9R<4&bVIyuEb{`CEG z|7`8}d2^~fX{L8PvGeBN@`su9vYWwMQc%&hb2#)DQKzNzlkkh6ef5iu;awc)er}K- zcjnnmuXiSW5P+Ifrkmya<$e>dq|BCm{N>v#?6|5{qCQ*k4mr#3@H=G9w$!&Q+LDJV zr=nzHq%Mcku9Rl~p-Lp3Fru)U8Xe&-i+?S=NJyH9dFJIsZZLH#KIqsVj{5>%n&o|S zUG_eU?ZYPJP|7N%f1^x}bK+~sRk}&RA->OoaQ2t}joaBJQtc_pjcJ>az6jkOF*s)~ z4@BeKjHWeZRnhl<7qK-(?cm<|*G9V9@cC$Qo>(R@0S~#X^;klGEVci%1N2C9h8_NU z5FdtM3<4WRrJT{_G(~xI#PdV)gZI5jto}-w6$HKi^SD1^)Dx+ySy!tTwh>^MZt_9u zY?Nz1fkplX;{=CBt%oxsgFaNm!50{>ax1c9R`^9mL?eo4M>9i))7@RM%LzaHB z2%@4k>RlzA_DR@8;f%%HYW=l87&=n&H{oj1`%P-nDlau%!&%MLrR5`Fk>p92FnkmK z59UZ(21H%ai#+wqTnjUpX6NUP0btIL$&@#;STNhL7kLT0A~ZSFf;x0lf}IS-cbk-* zp5I?1NE^u?w8e>D0zHhlcD&-2G!QY;V(6wDZ)CZotDwm#TwH~$|ZGtX2Zr` zd8z5tKzi$rH7ycs{$Y@T(Y|aKfJ*KIZ4Tuw)X0R%UMDjx<#5%=6rlzX&M+s*!29#A zidd*AbXFn(Q?s;pD&HlkEF|I4n%-Vky{#H@TT*(N%`~t7J6__6RKLh|`uACROKMrx zy$vy`eYG6=pBrgX7kb<8s*h2E$0majLzJ{adTz1dKYy$pJxSsf5JoufwEo?n5#5YR zSfo@!b9dh>C*Irr>l_4<2()^Ar5wxfp$!(opY2W2?>X@+0^i?-;0wWqm5Hn*cH)U& zA7zcr&jbzyemVxRDgHCBzfoM-rm~GB;S74CG0au%F|C#?VP=gqTWrKx!YC`-_s2qMi3*%x-4zZz(L0nr8e0VBlR>K3s25vRF`X4cX$+nW#B{ zw61&HxkCUyNQzDMKfk2ZY{;?^??T)f_7znmu`RxBXplAix`|D65%XQac?I$@n**)M zkbyzWCy(7@`A6F5e-*}eA5vwWm1*`r?6T&Lrz`ptvaK=;UJX$i8gDc)ItKIy8~k@@ zkD4A8zOZ?3IUWw^1L2}w#@KQbr*9>W?c)Dau^&)8xX%5JWeE;b9PV@#J^8yEPB;2F zTi!9;k2TIsKEuQ}Mn0s3dDYdcH`<4*$fZdR|0A9@wnmp|(XBB4!i?}&Z>JrBq2`(W z!6HiDcXeE!<A_+li#DA8l)$!;Si=Wqv;@_^z zxZ!mDg9xlus^=YK(ekA6dRUe99e7o_VAgvJyCsoCnk3jfb0uW(LoVq54z|RgS)VeV z_{iPasw14;AI2nU-Rd$jzK+?f`fNSK{e7{_|8@kVxu7t=_x@-~*P^hpDbhSpwOQIz zQCh5vvh;T&$TEmzHGLp*!l*j;@aV%xy15jYZfvzdy05UB>BiT?s36MoSDK95Tzes! zC9h4f&ludqCv=s_^=a}Y%PY=jtaR^L%L4hWS8FckFhBaltJ9Jgdw*xB-&^_BR)&RKWyQTa>;GoXhWwCOx1EF`$W%fTN9Pf8dFB^k?8)h{U%Iu{tm&iqvSXfBVw_P*hAPw4l25T4F-D(y!>X{dlC!@QRF62-rtrIrq) z1MXTS{5XX#o|Un^-9O}Enm>8U$D0*|a0;goplu5lrIOP<>|s@?$sBwQEB;R47y}xi zDrxfu9`LoGtP} zsiUQw{)rfz7K5qy?hyGuy?e}y{5%%lr59bXB&cpkTt*dGNGV zQ?3Uj0TIDc-cn_;SYsid&eNZ|VOD=kBA2wasz2Up{z+teLFv%iT=X=Hg|$gmA9Kp} zLX$|Qeu5*0P=lu8Gr0+|*4<9!s$4|Y@tpbQfLjmW0o_USZd>@WD*Kd7W~Kk97elXY z?%s>J>FqnzRykY95$vplkxG4Q<5hhoBP(vHcKEyq(VKwiRc53j(ru;7#+1$cY`F1N zB56Q8QPr5RsgxDj3ImQ$I&}hmT~>Kc0df}c3Rg;Dt#jy@r`59zbet@4FB_NeXO~`n z*O%RekckC-l)Ays9Ru~(YtVbF^2ha1TF$#Dzroh{&X6Z|)iD-lK8VZk-Fd0UqNjg# z;L?ReiVkmr>0SJi>=g44iHXC?g9dS16Fx(&)LJgp_?ge2O6pQBc@362?29X zD%xBlX2MwQKq0mz@AObW#2sw4i}xK_x@c-~cIL?Zp_$nu7fsF1PwhQ0I|C;L4+)y! zfkClY8*>ywp*x6sD6a)_k{=fdXFRP68O%6My=IM{`24pHc0B%NJj7=g*ObBBb4fS> zPiIVgggwuv0O*;IF`~xr#%^CBJ(xvf2FHvMR#Hc}NSOGr(@%Tq6Yn620;j=6PwGP* z@qp1(DYYqX=G&;&afITg#R{7XW`eMF6@uDA`)Gj5);jyKVllf|yf!ZEeiyfpM+gtZ zsWb5dlAy)}l-ZqPYIlS|3u0U_Ay?WR&xv%5Q!OD-jo;?jyEStXxqsLKqWs^5uInyP z*Jn zg@;2X4hK+bZH3bQfOdVcwKsXbDIOlwuK1nr1(Pv`qZ@l~@T?zjyzcrTe$os@KJkj; zGuUG_%nqJ;>MbazU`_7QP;FhQDiXtYh$7rVtITHLR#h!mvdUIAW0Sgry-Y5j2}&{4 zp-mbq%p_0SXo##kVHEcBX+%+rcmx!MuE$XwfzL!#Z52~(Xm(u})F!T;RfkZ|ps8?z zJv^4u1z}8}fOzbyM(?OfezFak+#J zI$6$Ng@*0HWZ6(c(`3Gw*L0<3eF-44 zL=ZEZK*_4YPTEK6D4!ihgVBM+o9HJdNEz2R`qT}zm9;MH^A!|em_A98T2Y*oFsMMo zh9ifcM%)Hqkyoq0E?7hzs z!9k2AMq_SawJeZ5&bQ-YN;}xY;9qG=J2Jiw8t-)h>EZY>V)USCKcMS9$7@*{46g@t zg?Jhi-~fczf{F=@D0YqL1Z@>rj2%gUu4t2hY_KFQZDp{B-CpE0!i*o#4vE^N8&0t8 zKheQ#kP~g3_=kCfhAa<$?)GY>X#amK|T&5)&n+fIU?P3WpXHSAowOG&*wNT zm<}QGIOlM-2qjdOkne5Eo%l?gIbK8pw(7fJH;svBLam+~ihin6yPrXb^Rv&p8X zN4vXspOff_hq`_%z&1Zyp$~y%F(wKmJXY^|Hd|yPOe?i7u!>{jK#3V)AnI`#vO|6q zA{ZvE^@&`T{gY?^6s+}~p)sA>^cdIg7(bg(b0~EJIX{Q_SQgnNLRSpV7l!VXA2=?` z=M#vb9rGL{C8+6~G*(_<4{`KBC?euF6HJ#I+>}O5Vv|4xq|iDE#{q#HN2F6H(0GU1 zL@Rz7)nSd{f{8D$t>Wz_fD%I-^D_c=6hHRL*)8ax<*b220-V7e3e&XH0Us$xBBhss zf6FyfI2Pr}A}}+(JOZbmAq|u(z~U6NCp#JF&yP;pSRs-8#>WBf;E1W#d;z8|TUsiZ zYM!*6Va>VYN}QJw4x+BV03Kb3y9j59i>0^1=w(qm_zKxXFH=0&LvhA9^10p{n5UjP z1txZ2-*JQ;jH!C}@?O>+(Y=TZ-%QRdWN(d8^oUAW5u)#ksH+ zE)^~FR)Y2hH6bqN!+O!-Q4K3V3#;zlsnFtx$Y-ZqU!KAgfcMZgwD&%us>{nabjD?{+rPV@NH`H(5b8|}W#pI8yL$E(myUKSC^$_)m7q*>d zG!DmXEe*E;gk(@dH4`e3zy&8S^@Ro2YBf7vEEd?q)oe98UKM}HPN{xq1};w;zQ3xm zTj9n(aLef^6uJ5bMQVfcMA?*ZQz0OsuRW--xEeMiGkekWlw6}+X$$O-D;dgRxoTBY zI=x6%Nf1E~XNCKEBlS$HQL;mmTj|i`m~k+wnKhz6I3fevd;ZY!?2;9&N3k(fq#HA@ z$FQ}I8{{h5E1Eag(MCJLdc0WpI(7o5?PUD0Q+fm#bLlg-&ps}f2hnEYtM5faQ;drN z(Vtj|6Zo*XYcuh%Zoard^M)s1N_~QO+Ta?gw<%o-K{z>{0=n#2KJkW-SQxqx1>iKf z5+U+uH^RkK&yuct1*|Y_Pb+9h=;%CnpyoBhng#jTk~f(>%+F?*3b&&gsm*kB8gLi# z;K&QF8Cq<=&NzUH@c!+L9|M9f(hh^2TyY?vIIw2KLBdBUQHmgBwC7aaKiPlN{$A!j zMVwuZ zpGJiHCP~NRXIK1C?t6}{v3amx#7yF4$*QwKKm#)pz~rzPN?V!C4KF+^lQ~m(YNc0V zfLe&MvFc+>_EQJs=s-OjqK?xDXhw<_CXsyCiF7Qckn_WYQ7Ji}Et?0A3iR+$N%54h zs6esD-H3(_%z8AjnongoA$wuFbwddKitRAS>n~{Ay>Fw587~hJ?51YCTrr1{({4Qx zu2P7FB_tah9EWA7&)nUv27-2Sdv{4jH!TwW&Q3N@J7XbGJB_|&Q~RdQ#eo$SKu2Qs zY#bkv1|?%M)A|rcpSrPTTwZ|8)`b^0b&OLtH1#M(Yv;g@7=tNIrVd8BCm>^s$B)z^ z5_B97+S(uB23a7118O3~f~jdX0~V*LtHZO9m`W4h0x>oasur8gHs)(=V=jnsWd;Y0 zwzEoVTi%r(^)iW_sc5VPl-Y#1(I%N@sN0^KVFPM#R5lae9>NGn9=Lp$RJP!ts#NmE zE-6*DlOta+b~|S%1LJ^06Xv5I=qgybY#iyz6-}2lt%t-V;QnG?WLz3XnI`RecHBLs zS%7$}Xi(UOFrQ`IIv3qWY>ueGBF-UL1i(&^G3<)r+sXG0yWhLWcMKc`!U4|)-mN>6 z6WzL#f!Z|VzbNCH?=>xvf1(3vKd1u=%M7ZPh=?7*ifn#BxX#SvzFbksf}>gf;AcA z##}BXdI9Wg>~l|)LqcK7=rA{mr3f^fer3wy@tjN`xxNGbuInAXhFA z0g{O6E|G-HQFxP34Mz7%##4T(gGL5vSafNRt;%CALcC*03 z2gk;#CiFWNStkuk;N`VrU94Bw zThzfTGTazVEzF??c3rMmf_<-?qfB0DWlCyJ!cRX)?c{DH!&}>@5DoIy*0{Si@px_6 zfQ1xQF+{agRjoFjR3e2LF^*~}ZcF`Re}V|JwM8U0vC%HBJ=tTZhS$POSg7?IO&61B zsSqL8sRuad>20;Pwr0V8f}O=C$?Y<=oC!+b#l4A(_bndUKQS$4&+%I4Ms0Gg!ew?C zzeH`KzR&PLU>SxpHn7Kue3-tTLmVp&8e{|9C`lutkO0Af`IPB{JPU|6_j`Se0!boH zgmi3_$gfoKBW6cLI+lznaXi#;2@a3|jr$bc)a`C|L4~%ike)%H=8-kEWoT`)1Kl9Io%_(5Zi0u{q#$#erV3OI4ROIQ`MO4XakC^f64Vr|pIrDSFM!XM4p zN?uTe1U$}7#2iSkC$9=9qu80ui9Ih;8#wAX}hT<)A9}in1W4dxiVuPWd4RDcJ8)6@F zk8QzQR4#H=h)0NE7d&JUk*;5k5&Z$rCzqwmgiLA%sTU#>g6zD~ycv$z7oKj1vZ3FaY*;?G}+*Wo`?e*m90L zmXrBI6{}FS*2mG+)bTDMJ$dm@K%&4|m459hyW@E10=5(ZItbx&pxj=7mbe83ZlA+* ztpA`f2`>qoR7lTGIL&$Y3&o+Qu29ro@fu&`1$lNw3)JT9JMChpQ&Xh``W`f&ib=T8 zOj+s^z|BCpNO|($Al*%`ZlNDB%u7iiv+y>q7b?xg+!lbZaxUL52~<3^u>dMABpwh_ zONj`9iE^Em2bO{BtP)QqFa`wM4*bQ>Yt+P!+YQKaJ0M}3x?C$Q!LEVe5>wUW2g?TT zQFRm!6KS2MS%_lE=fzWX7Qmpc^R#np+kGZ3 zQ!rt0FzKbqp<$5HWI1(jZBn@dLP28!B!-qt)k6TfVYkC!o?VJ-#`sQ&IYXEjNki!e zfbyDAOiZ}C0iy`P35BPWYc^KSV{f;`EQt)d43K^fG(q%ZA`py`i^u&7{94LEQw*|q zb%#>(>4&XQ!0gARLgv!KrW_p8ce^10G2}JKbUlO|!n`njCR`W!xHnmB3(4)JWamq7m_CnJkFj_hXYSo}K< zXRpZ;2WCLhu8=k(6m(s}9p%^t6KH*aR;u9S0l995H7vI1Tnr8$j|FV0KSgns>4XEl zG(0F-8UmCoWMiOYA+MucMF?`vAkl{x^H$>qYble@7HfUgj%wBHWDYU_VmcI0bBCPc zhnspQ@xc(U)3GrefcjSj~wvr3tB|3GHNq zC)HlBnZ9`i(CkSjDoA6PN8hWCiG8lB`GlgHPv}$Xs)8aATELZ0b@(UWXqZq{tzzZM zR^DW)oDn)*QB2jplP;oxa}A^AE>L?)1apu8Vc(H~!L@jdN1a4CuRXeHG4e9cji1O8 z2zDaGv=uD$PAPJSNS-KIMZ<}g%>;!>tw1IqBU!~DR!+e2j8t+neZIzlKRugb!*9Ws zDDezPleIpFQPw#%UdLlw>W_SOB$8I|bz zIdqelO>>LP31AWKQ$PUDIiC7<$tMlLHM!w+B1ANLQ{FJL=a1$TvJ#^DZhoN z-BNziJdRB7Pm0?|xO?q*3K00BAW=*1jVHK^^nNg^&Iv6p*QXu`mTUN3IR84NLs+}Q8!~O0UmFjy z%nbwi(5_lg#|?`gSv+B5{WjGzR_=AXiFk!%C z7fu$zprXA+QFO_2Jcu&jGaa7*&YWTk4~?{ya>WVoBc^58NPC9OW5y6Q9Z08zjZwp{ z?|6I)`g{=SK*7{yOXvW) z&sq7rnCj}#H7kqN%GhS@+i#q>zxqhtf;vqFvhZ*(m&1pU%n3o^7YqmK91l@m>4~F>g(~0OdBF z-Ta2hfccV9>9Ejexdz;dr%f?o5k+7Hfv?_Kaa2mbfU^^K>TNk-L6bSxAY(`$6Tyd2 zETIeYZ+KPN%$BxD#mW~g!?04x8aS|dIRuGeNSje~LOBA|4gnmO2Gwm1CqC#So`7Z7JkZ&ge_Q=u1^~Wp5W&2W2G0!c6OF93bCmtc0lR3&eJx z!;Ad3iQ7?bJ7tfHl4ctgC^5QX7S1ducFcy-%0OOg9<|8=;53F4+e{#rMvj8{)iAi)L{|UVAYlp3T~! zp5G1>4g}qSx|VQ|uR7Q)^I{Z*p5%=|QMZ^iEz{p)(4r|N^P&g%m9|iRjsm7gJ9C&&yFv2F&!w(rGV4&IL3y3U;ZKt*ZyEyfnrl{K%#x# z%BEC7NL2`VwTu3gHv_vd1Eu)&sf>m5+3bD?YPAi8_T=MePpMQ(eY0<7YPqacuyWN} z{KP^THr3X;Ok6_qG$LWD7E*U24|g1?heQD(vS^I_Nq1mJL^HW;7J^Ei)`dKda{-Sh zvvxKB^i52 z#mOLluAp=-P}*VqB5Ss>?=!~tyc1*QU~+me$un-_v6N4Qu^2)88!limwt$8NEg1qd zwS-dAm5Feuwj#gy1hP^C;?vMc)QQ<>`bsCl^py-&$aB6vhyxkhGb|?*PlTZqV?)WQ zp=7r-6j_#_CPF%tHYLn*@Q?{}AcGcC%D%{f$8T-3X(OD7aJUjqsQmbhPi>7i8Q)7& zC!_AwP0D&NWF3%+LsPRxYQT$W={+(A06w z_w}n;h%?dG|f0U z8&zXUwY26JR@i7Xj<$}1o;I3>xsED4bLyIIK+s;7)>XE4qFB7qcrtKhxTVNj)nEKl z;TbZnJwu)xD(nZaH=mG3k>f6@HM-kcQO!vTAr6y)PK8uNJ(DB;gQfvf2#4B#oE$ z7kOs=7l5SqGp}N=CRINZ8&f4@$%&Tt7;vmdehXX^v5DQs{||m%P$g*1ID~L4Wls%T z3zxAnR}ha!G29L6;108~O2%B~)LiN{ABIN3prON-s#~I=<8wE=pF-^b)oNMyJeTBF zC70^1k;<2Z%o8C+DNrA4MUO3;7~6(`Sl%kd0kM2(K={+(JGW#AT1^}yLb8gdu6Dt?Mngq4 zyQ-nNjvvrff(X=g>X0zxH!(%sz$2ns2#zAs@nPQqZX_Wy8C(A@^Dh+ybJ?JV1);%=R~r$n2u%Bhl4m%Ms}lx z0P6XH2E9PT=pss2R4N}o;|N?z&vXD@^az5oZN~;gSlR$sy%f7I$*2HqUfvS9LKzdq z0M%uQ;!-iT&gLzNygH|5s+gG~#NOD=Y zM>+tivZWpk<&lfokwvbSLR(riEZ{JV#lNjOSfl>J%mp{5y^6gCpDM!i9rkgiCh zW=$$K09rE+@SO&DZMwdBMY6tSRahXp`z6`^t*l-4L{O@;rczzxX8?#c^B5Nn>IEFN zm!h6a1QY3W3tlZa6Bg2{&q<%D(M+MgnPKMZ1ULw?CO}){M*&oo(Ic5|(=`v(g?oIB z>6i$L_Lw0{4M>ti4R{v2Ja|1s4a|)@m$u-Ne6)rGkI82taQ4Y0HUWmRhv4W1Nn~^^ zr#hC6tz=Tv`T=3WI>UEIMkzp1hNq7wqoZ^SDgtgx0xNqWoPt>^uNSeP7g4~U*%ahd zoC$v5P%xZa9EQBkfGXTM--0JKN>MMu?DfU@sE8jUyenB?8&mCJ4<=>(W`;Dis<1!USqi zs7nk$A1?^!h~$$P$7QX_!Q)p^Me!)iNuAom2&huvd2LZ~TTq0vaIG%G)i;$Yw}zc) z9}Q-N7O)42$a5o*T*Rh@k>k3EJuj=68?9GV>-xkH`Fb35Hs`IJ@-W-GOzMknZa7z^ z0q=`}4t}wyUQF8a9Vc>H`kg*xJcF({NCT*5KsO6iZ4eJ4Lku(pymFGAbX!dRS{-}L z=wd-tBCBi!IfMiPjCUQL8&ij-?<4qO}GDY7KXNlGI>~=3Q4mr?4v17Ra@{3whO%u9lXCNx7EUD!M3 zqI;BavPk`pk;tWx5Qj5fpH}xeg~u?>*%ZCTw8a77GcH z`c4N2J$V-qsOOToMXzWP0fMQQ!X&A=_!8vnZ?L7fs!P!6)95&Df84y zm9nLa-5>0+Gf=U^)i_YKqH9o&+pG!ectQXeqgDdL5K0H>DnTLb#LiirILx7%s7-qE z;+8szUzh?Z72SF)-CU}otgV=F6TeB6LuL)vA^Z{(wMive+>vqlI26JOUWwZ5AT<|M ziIDPE)^l*BUf|Yg(8ZIHlc`gyU&h zA&fLu<)AuL^u~~hY&o9Dwyg?*DwLx3oIh!mtz0#x+YX}&O-j!$Tl4%Dz{IkZ`vu6T zoN;t9#vXijiP4p$1olkG0xlGADOUFf&5L^z1A;rKL+!3hP@Oa|;~HM1-lJ>alnRLW zUAIt7X#sj~MeC7_X3M~Fl+l;l>dRL&rGlhnc=F|O!|XE1N94y&-#GK?-7VnyC9Cq3 zuVTrn=w9b!udC^R49X}(A(3LqbsievhD+?arl`1i3t=23@NPWC(;$x95Y;J-!YIJL z80tano8?iscVQSYoFW5ia$<^H4TL>`)8Ai;NU zx8rZh#FZEm=hJ;4lhW13I-fJ1= zWR{WP1(ykH5=J798D$lcl;VOs6iUi0k(MJ+Bol0^rObIhyj8U-Rv{hUI;J(t=LW4= zo(m#2)kzE5yz)yYiOYJn+BZ_n6?jxssEJnliSnX9eTaal^1&}ssKqX{mS=6k$ceMi zT-;-mEKsK&w34Qh?rcSNwjA4WaFZ5KJl&;@pi--Dx{gZORvm?o*N{cVQNzVfN97OZ zv#t7=rQ0ei#_m%oIP`c4;WbrdZD<@Om5V%=k#Qod!=g$86pN#%mT8l9RLI9Jqu^iW z%Esu6iM%R9-ZA;LhI@VGglb~1?17a1kVGh?Ofg$?Tvs>++#c7CFns z3<)Rfuo<&ctB=WD1rHE6B-}3UPq<-i=TgQ49x8F!qo}vnSFP2E3WFle#)S|r?#b)O z*-vc1pt0&kvjZ9xZY>Nż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

    L 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Π7C3PW*U}cZa}!>RA0>#+y9{0UxCs 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

    jW4}-TrU_#4Pxi&XR26}q^rwHrN+Hl=u+1c# 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 afB)P+_s{*~KL0lW0RR7Mz{AP_N)iCioac}L diff --git a/assets/kasten/k10-7.5.401.tgz b/assets/kasten/k10-7.5.401.tgz new file mode 100644 index 0000000000000000000000000000000000000000..9943b940785fc32efa7c4d57554aa67198905c5c GIT binary patch literal 186353 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.18-0' + catalog.cattle.io/release-name: cf-runtime +apiVersion: v2 +dependencies: +- name: cf-common + repository: oci://quay.io/codefresh/charts + version: 0.21.0 +description: A Helm chart for Codefresh Runner +home: https://codefresh.io/ +icon: file://assets/icons/cf-runtime.png +keywords: +- codefresh +- runner +kubeVersion: '>=1.18-0' +maintainers: +- name: codefresh + url: https://codefresh-io.github.io/ +name: cf-runtime +sources: +- https://github.com/codefresh-io/venona +version: 7.5.5 diff --git a/charts/codefresh/cf-runtime/7.5.5/README.md b/charts/codefresh/cf-runtime/7.5.5/README.md new file mode 100644 index 0000000000..312ec0a510 --- /dev/null +++ b/charts/codefresh/cf-runtime/7.5.5/README.md @@ -0,0 +1,1296 @@ +## Codefresh Runner + +![Version: 7.5.5](https://img.shields.io/badge/Version-7.5.5-informational?style=flat-square) + +Helm chart for deploying [Codefresh Runner](https://codefresh.io/docs/docs/installation/codefresh-runner/) to Kubernetes. + +## Table of Content + +- [Prerequisites](#prerequisites) +- [Get Chart Info](#get-chart-info) +- [Install Chart](#install-chart) +- [Chart Configuration](#chart-configuration) +- [Upgrade Chart](#upgrade-chart) + - [⚠️ Known issues](#⚠️-known-issues) + - [To 2.x](#to-2-x) + - [To 3.x](#to-3-x) + - [To 4.x](#to-4-x) + - [To 5.x](#to-5-x) + - [To 6.x](#to-6-x) + - [To 7.x](#to-7-x) +- [Architecture](#architecture) +- [Configuration](#configuration) + - [EBS backend volume configuration in AWS](#ebs-backend-volume-configuration) + - [Azure Disks backend volume configuration in AKS](#azure-disks-backend-volume-configuration) + - [GCE Disks backend volume configuration in GKE](#gce-disks-backend-volume-configuration-in-gke) + - [Custom volume mounts](#custom-volume-mounts) + - [Custom global environment variables](#custom-global-environment-variables) + - [Volume reuse policy](#volume-reuse-policy) + - [Volume cleaners](#volume-cleaners) + - [Rootless DinD](#rootless-dind) + - [ARM](#arm) + - [Openshift](#openshift) + - [On-premise](#on-premise) + +## Prerequisites + +- Kubernetes **1.19+** +- Helm **3.8.0+** + +⚠️⚠️⚠️ +> Since version 6.2.x chart is pushed **only** to OCI registry at `oci://quay.io/codefresh/cf-runtime` + +> Versions prior to 6.2.x are still available in ChartMuseum at `http://chartmuseum.codefresh.io/cf-runtime` + +## Get Chart Info + +```console +helm show all oci://quay.io/codefresh/cf-runtime +``` +See [Use OCI-based registries](https://helm.sh/docs/topics/registries/) + +## Install Chart + +**Important:** only helm3 is supported + +- Specify the following mandatory values + +`values.yaml` +```yaml +# -- Global parameters +# @default -- See below +global: + # -- User token in plain text (required if `global.codefreshTokenSecretKeyRef` is omitted!) + # Ref: https://g.codefresh.io/user/settings (see API Keys) + # Minimal API key scopes: Runner-Installation(read+write), Agent(read+write), Agents(read+write) + codefreshToken: "" + # -- User token that references an existing secret containing API key (required if `global.codefreshToken` is omitted!) + codefreshTokenSecretKeyRef: {} + # E.g. + # codefreshTokenSecretKeyRef: + # name: my-codefresh-api-token + # key: codefresh-api-token + + # -- Account ID (required!) + # Can be obtained here https://g.codefresh.io/2.0/account-settings/account-information + accountId: "" + + # -- K8s context name (required!) + context: "" + # E.g. + # context: prod-ue1-runtime-1 + + # -- Agent Name (optional!) + # If omitted, the following format will be used '{{ .Values.global.context }}_{{ .Release.Namespace }}' + agentName: "" + # E.g. + # agentName: prod-ue1-runtime-1 + + # -- Runtime name (optional!) + # If omitted, the following format will be used '{{ .Values.global.context }}/{{ .Release.Namespace }}' + runtimeName: "" + # E.g. + # runtimeName: prod-ue1-runtime-1/namespace +``` + +- Install chart + +```console +helm upgrade --install cf-runtime oci://quay.io/codefresh/cf-runtime -f values.yaml --create-namespace --namespace codefresh +``` + +## Chart Configuration + +See [Customizing the Chart Before Installing](https://helm.sh/docs/intro/using_helm/#customizing-the-chart-before-installing). + +## Upgrade Chart + +### ⚠️ Known issues + +Please check the following known issues before upgrading the chart: + +- Charts **7.1.1–7.4.3** have a bug because of which the following feature does not work: [“Secret Store — Kubernetes-Runtime Secret”](https://codefresh.io/docs/docs/integrations/secret-storage/#secret-store-setup-for-codefresh-runner-installation). + +### To 2.x + +This major release renames and deprecated several values in the chart. Most of the workload templates have been refactored. + +Affected values: +- `dockerRegistry` is deprecated. Replaced with `global.imageRegistry` +- `re` is renamed to `runtime` +- `storage.localVolumeMonitor` is replaced with `volumeProvisioner.dind-lv-monitor` +- `volumeProvisioner.volume-cleanup` is replaced with `volumeProvisioner.dind-volume-cleanup` +- `image` values structure has been updated. Split to `image.registry` `image.repository` `image.tag` +- pod's `annotations` is renamed to `podAnnotations` + +### To 3.x + +⚠️⚠️⚠️ +### READ this before the upgrade! + +This major release adds [runtime-environment](https://codefresh.io/docs/docs/installation/codefresh-runner/#runtime-environment-specification) spec into chart templates. +That means it is possible to set parametes for `dind` and `engine` pods via [values.yaml](./values.yaml). + +**If you had any overrides (i.e. tolerations/nodeSelector/environment variables/etc) added in runtime spec via [codefresh CLI](https://codefresh-io.github.io/cli/) (for example, you did use [get](https://codefresh-io.github.io/cli/runtime-environments/get-runtime-environments/) and [patch](https://codefresh-io.github.io/cli/runtime-environments/apply-runtime-environments/) commands to modify the runtime-environment), you MUST add these into chart's [values.yaml](./values.yaml) for `.Values.runtime.dind` or(and) .`Values.runtime.engine`** + +**For backward compatibility, you can disable updating runtime-environment spec via** `.Values.runtime.patch.enabled=false` + +Affected values: +- added **mandatory** `global.codefreshToken`/`global.codefreshTokenSecretKeyRef` **You must specify it before the upgrade!** +- `runtime.engine` is added +- `runtime.dind` is added +- `global.existingAgentToken` is replaced with `global.agentTokenSecretKeyRef` +- `global.existingDindCertsSecret` is replaced with `global.dindCertsSecretRef` + +### To 4.x + +This major release adds **agentless inCluster** runtime mode (relevant only for [Codefresh On-Premises](#on-premise) users) + +Affected values: +- `runtime.agent` / `runtime.inCluster` / `runtime.accounts` / `runtime.description` are added + +### To 5.x + +This major release converts `.runtime.dind.pvcs` from **list** to **dict** + +> 4.x chart's values example: +```yaml +runtime: + dind: + pvcs: + - name: dind + storageClassName: my-storage-class-name + volumeSize: 32Gi + reuseVolumeSelector: 'codefresh-app,io.codefresh.accountName' + reuseVolumeSortOrder: pipeline_id +``` + +> 5.x chart's values example: +```yaml +runtime: + dind: + pvcs: + dind: + name: dind + storageClassName: my-storage-class-name + volumeSize: 32Gi + reuseVolumeSelector: 'codefresh-app,io.codefresh.accountName' + reuseVolumeSortOrder: pipeline_id +``` + +Affected values: +- `.runtime.dind.pvcs` converted from **list** to **dict** + +### To 6.x + +⚠️⚠️⚠️ +### READ this before the upgrade! + +This major release deprecates previously required `codefresh runner init --generate-helm-values-file`. + +Affected values: +- **Replaced** `.monitor.clusterId` with `.global.context` as **mandatory** value! +- **Deprecated** `.global.agentToken` / `.global.agentTokenSecretKeyRef` +- **Removed** `.global.agentId` +- **Removed** `.global.keys` / `.global.dindCertsSecretRef` +- **Removed** `.global.existingAgentToken` / `existingDindCertsSecret` +- **Removed** `.monitor.clusterId` / `.monitor.token` / `.monitor.existingMonitorToken` + +#### Migrate the Helm chart from version 5.x to 6.x + +Given this is the legacy `generated_values.yaml` values: + +> legacy `generated_values.yaml` +```yaml +{ + "appProxy": { + "enabled": false, + }, + "monitor": { + "enabled": false, + "clusterId": "my-cluster-name", + "token": "1234567890" + }, + "global": { + "namespace": "namespace", + "codefreshHost": "https://g.codefresh.io", + "agentToken": "0987654321", + "agentId": "agent-id-here", + "agentName": "my-cluster-name_my-namespace", + "accountId": "my-account-id", + "runtimeName": "my-cluster-name/my-namespace", + "codefreshToken": "1234567890", + "keys": { + "key": "-----BEGIN RSA PRIVATE KEY-----...", + "csr": "-----BEGIN CERTIFICATE REQUEST-----...", + "ca": "-----BEGIN CERTIFICATE-----...", + "serverCert": "-----BEGIN CERTIFICATE-----..." + } + } +} +``` + +Update `values.yaml` for new chart version: + +> For existing installation for backward compatibility `.Values.global.agentToken/agentTokenSecretKeyRef` **must be provided!** For installation from scratch this value is no longer required. + +> updated `values.yaml` +```yaml +global: + codefreshToken: "1234567890" + accountId: "my-account-id" + context: "my-cluster-name" + agentToken: "0987654321" # MANDATORY when migrating from < 6.x chart version ! + agentName: "my-cluster-name_my-namespace" # optional + runtimeName: "my-cluster-name/my-namespace" # optional +``` + +> **Note!** Though it's still possible to update runtime-environment via [get](https://codefresh-io.github.io/cli/runtime-environments/get-runtime-environments/) and [patch](https://codefresh-io.github.io/cli/runtime-environments/apply-runtime-environments/) commands, it's recommended to enable sidecar container to pull runtime spec from Codefresh API to detect any drift in configuration. + +```yaml +runner: + # -- Sidecar container + # Reconciles runtime spec from Codefresh API for drift detection + sidecar: + enabled: true +``` + +### To 7.x + +⚠️⚠️⚠️ **BREAKING CHANGE** ⚠️⚠️⚠️ + +7.0.0 release adds image digests to all images in default values, for example: + +```yaml +runtime: + engine: + image: + registry: quay.io + repository: codefresh/engine + tag: 1.174.15 + pullPolicy: IfNotPresent + digest: sha256:d547c2044c1488e911ff726462cc417adf2dda731cafd736493c4de4eb9e357b +``` + +Which means any overrides for tags won't be used and underlying Kubernetes runtime will pull the image by the digest. + +See [Pull an image by digest (immutable identifier)](https://docs.docker.com/reference/cli/docker/image/pull/#pull-an-image-by-digest-immutable-identifier) + +## Architecture + +[Codefresh Runner architecture](https://codefresh.io/docs/docs/installation/codefresh-runner/#codefresh-runner-architecture) + +## Configuration + +See [Customizing the Chart Before Installing](https://helm.sh/docs/intro/using_helm/#customizing-the-chart-before-installing). + +### EBS backend volume configuration + +`dind-volume-provisioner` should have permissions to create/attach/detach/delete/get EBS volumes + +Minimal IAM policy for `dind-volume-provisioner` + +```json +{ + "Version": "2012-10-17", + "Statement": [ + { + "Effect": "Allow", + "Action": [ + "ec2:AttachVolume", + "ec2:CreateSnapshot", + "ec2:CreateTags", + "ec2:CreateVolume", + "ec2:DeleteSnapshot", + "ec2:DeleteTags", + "ec2:DeleteVolume", + "ec2:DescribeInstances", + "ec2:DescribeSnapshots", + "ec2:DescribeTags", + "ec2:DescribeVolumes", + "ec2:DetachVolume" + ], + "Resource": "*" + } + ] +} +``` + +There are three options: + +1. Run `dind-volume-provisioner` pod on the node/node-group with IAM role + +```yaml +storage: + # -- Set backend volume type (`local`/`ebs`/`ebs-csi`/`gcedisk`/`azuredisk`) + backend: ebs-csi + + ebs: + availabilityZone: "us-east-1a" + +volumeProvisioner: + # -- Set node selector + nodeSelector: {} + # -- Set tolerations + tolerations: [] +``` + +2. Pass static credentials in `.Values.storage.ebs.accessKeyId/accessKeyIdSecretKeyRef` and `.Values.storage.ebs.secretAccessKey/secretAccessKeySecretKeyRef` + +```yaml +storage: + # -- Set backend volume type (`local`/`ebs`/`ebs-csi`/`gcedisk`/`azuredisk`) + backend: ebs-csi + + ebs: + availabilityZone: "us-east-1a" + + # -- Set AWS_ACCESS_KEY_ID for volume-provisioner (optional) + accessKeyId: "" + # -- Existing secret containing AWS_ACCESS_KEY_ID. + accessKeyIdSecretKeyRef: {} + # E.g. + # accessKeyIdSecretKeyRef: + # name: + # key: + + # -- Set AWS_SECRET_ACCESS_KEY for volume-provisioner (optional) + secretAccessKey: "" + # -- Existing secret containing AWS_SECRET_ACCESS_KEY + secretAccessKeySecretKeyRef: {} + # E.g. + # secretAccessKeySecretKeyRef: + # name: + # key: +``` + +3. Assign IAM role to `dind-volume-provisioner` service account + +```yaml +storage: + # -- Set backend volume type (`local`/`ebs`/`ebs-csi`/`gcedisk`/`azuredisk`) + backend: ebs-csi + + ebs: + availabilityZone: "us-east-1a" + +volumeProvisioner: + # -- Service Account parameters + serviceAccount: + # -- Create service account + create: true + # -- Additional service account annotations + annotations: + eks.amazonaws.com/role-arn: "arn:aws:iam:::role/" +``` + +### Custom volume mounts + +You can add your own volumes and volume mounts in the runtime environment, so that all pipeline steps will have access to the same set of external files. + +```yaml +runtime: + dind: + userVolumes: + regctl-docker-registry: + name: regctl-docker-registry + secret: + items: + - key: .dockerconfigjson + path: config.json + secretName: regctl-docker-registry + optional: true + userVolumeMounts: + regctl-docker-registry: + name: regctl-docker-registry + mountPath: /home/appuser/.docker/ + readOnly: true + +``` + +### Azure Disks backend volume configuration + +`dind-volume-provisioner` should have permissions to create/delete/get Azure Disks + +Role definition for `dind-volume-provisioner` + +`dind-volume-provisioner-role.json` +```json +{ + "Name": "CodefreshDindVolumeProvisioner", + "Description": "Perform create/delete/get disks", + "IsCustom": true, + "Actions": [ + "Microsoft.Compute/disks/read", + "Microsoft.Compute/disks/write", + "Microsoft.Compute/disks/delete" + + ], + "AssignableScopes": ["/subscriptions/"] +} +``` + +When creating an AKS cluster in Azure there is the option to use a [managed identity](https://learn.microsoft.com/en-us/azure/aks/use-managed-identity) that is assigned to the kubelet. This identity is assigned to the underlying node pool in the AKS cluster and can then be used by the dind-volume-provisioner. + +```console +export ROLE_DEFINITIN_FILE=dind-volume-provisioner-role.json +export SUBSCRIPTION_ID=$(az account show --query "id" | xargs echo ) +export RESOURCE_GROUP= +export AKS_NAME= +export LOCATION=$(az aks show -g $RESOURCE_GROUP -n $AKS_NAME --query location | xargs echo) +export NODES_RESOURCE_GROUP=MC_${RESOURCE_GROUP}_${AKS_NAME}_${LOCATION} +export NODE_SERVICE_PRINCIPAL=$(az aks show -g $RESOURCE_GROUP -n $AKS_NAME --query identityProfile.kubeletidentity.objectId | xargs echo) + +az role definition create --role-definition @${ROLE_DEFINITIN_FILE} +az role assignment create --assignee $NODE_SERVICE_PRINCIPAL --scope /subscriptions/$SUBSCRIPTION_ID/resourceGroups/$NODES_RESOURCE_GROUP --role CodefreshDindVolumeProvisioner +``` + +Deploy Helm chart with the following values: + +`values.yaml` +```yaml +volumeProvisioner: + podSecurityContext: + enabled: true + runAsUser: 0 + runAsGroup: 0 + fsGroup: 0 + +storage: + backend: azuredisk + azuredisk: + availabilityZone: northeurope-1 # replace with your zone + resourceGroup: my-resource-group-name + + mountAzureJson: true + +runtime: + dind: + nodeSelector: + topology.kubernetes.io/zone: northeurope-1 +``` + +### GCE Disks backend volume configuration in GKE + +`dind-volume-provisioner` should have `ComputeEngine.StorageAdmin` permissions + +There are three options: + +1. Run `dind-volume-provisioner` pod on the node/node-group with IAM Service Account + +```yaml +storage: + # -- Set backend volume type (`local`/`ebs`/`ebs-csi`/`gcedisk`/`azuredisk`) + backend: gcedisk + + gcedisk: + # -- Set GCP volume backend type (`pd-ssd`/`pd-standard`) + volumeType: "pd-standard" + # -- Set GCP volume availability zone + availabilityZone: "us-central1-c" + +volumeProvisioner: + # -- Set node selector + nodeSelector: {} + # -- Set tolerations + tolerations: [] + +# -- Set runtime parameters +runtime: + # -- Parameters for DinD (docker-in-docker) pod + dind: + # -- Set node selector. + nodeSelector: + topology.kubernetes.io/zone: us-central1-c +``` + +2. Pass static credentials in `.Values.storage.gcedisk.serviceAccountJson` (inline) or `.Values.storage.gcedisk.serviceAccountJsonSecretKeyRef` (from your own secret) + +```yaml +storage: + # -- Set backend volume type (`local`/`ebs`/`ebs-csi`/`gcedisk`/`azuredisk`) + backend: gcedisk + + gcedisk: + # -- Set GCP volume backend type (`pd-ssd`/`pd-standard`) + volumeType: "`pd-standard" + # -- Set GCP volume availability zone + availabilityZone: "us-central1-c" + # -- Set Google SA JSON key for volume-provisioner (optional) + serviceAccountJson: | + { + "type": "service_account", + "project_id": "...", + "private_key_id": "...", + "private_key": "...", + "client_email": "...", + "client_id": "...", + "auth_uri": "...", + "token_uri": "...", + "auth_provider_x509_cert_url": "...", + "client_x509_cert_url": "..." + } + # -- Existing secret containing containing Google SA JSON key for volume-provisioner (optional) + serviceAccountJsonSecretKeyRef: {} + # E.g.: + # serviceAccountJsonSecretKeyRef: + # name: gce-service-account + # key: service-account.json + +# -- Set runtime parameters +runtime: + # -- Parameters for DinD (docker-in-docker) pod + dind: + # -- Set node selector. + nodeSelector: + topology.kubernetes.io/zone: us-central1-c +``` + +3. Assign IAM role to `dind-volume-provisioner` service account + +```yaml +storage: + # -- Set backend volume type (`local`/`ebs`/`ebs-csi`/`gcedisk`/`azuredisk`) + backend: gcedisk + + gcedisk: + # -- Set GCP volume backend type (`pd-ssd`/`pd-standard`) + volumeType: "`pd-standard" + # -- Set GCP volume availability zone + availabilityZone: "us-central1-c" + +volumeProvisioner: + # -- Service Account parameters + serviceAccount: + # -- Create service account + create: true + # -- Additional service account annotations + annotations: + iam.gke.io/gcp-service-account: @.iam.gserviceaccount.com + +# -- Set runtime parameters +runtime: + # -- Parameters for DinD (docker-in-docker) pod + dind: + # -- Set node selector. + nodeSelector: + topology.kubernetes.io/zone: us-central1-c +``` + +### Custom global environment variables + +You can add your own environment variables to the runtime environment. All pipeline steps have access to the global variables. + +```yaml +runtime: + engine: + userEnvVars: + - name: GITHUB_TOKEN + valueFrom: + secretKeyRef: + name: github-token + key: token +``` + +### Volume reuse policy + +Volume reuse behavior depends on the configuration for `reuseVolumeSelector` in the runtime environment spec. + +```yaml +runtime: + dind: + pvcs: + - name: dind + ... + reuseVolumeSelector: 'codefresh-app,io.codefresh.accountName' + reuseVolumeSortOrder: pipeline_id +``` + +The following options are available: +- `reuseVolumeSelector: 'codefresh-app,io.codefresh.accountName'` - PV can be used by ANY pipeline in the specified account (default). +Benefit: Fewer PVs, resulting in lower costs. Since any PV can be used by any pipeline, the cluster needs to maintain/reserve fewer PVs in its PV pool for Codefresh. +Downside: Since the PV can be used by any pipeline, the PVs could have assets and info from different pipelines, reducing the probability of cache. + +- `reuseVolumeSelector: 'codefresh-app,io.codefresh.accountName,project_id'` - PV can be used by ALL pipelines in your account, assigned to the same project. + +- `reuseVolumeSelector: 'codefresh-app,io.codefresh.accountName,pipeline_id'` - PV can be used only by a single pipeline. +Benefit: More probability of cache without “spam” from other pipelines. +Downside: More PVs to maintain and therefore higher costs. + +- `reuseVolumeSelector: 'codefresh-app,io.codefresh.accountName,pipeline_id,io.codefresh.branch_name'` - PV can be used only by single pipeline AND single branch. + +- `reuseVolumeSelector: 'codefresh-app,io.codefresh.accountName,pipeline_id,trigger'` - PV can be used only by single pipeline AND single trigger. + +### Volume cleaners + +Codefresh pipelines require disk space for: + * [Pipeline Shared Volume](https://codefresh.io/docs/docs/pipelines/introduction-to-codefresh-pipelines/#sharing-the-workspace-between-build-steps) (`/codefresh/volume`, implemented as [docker volume](https://docs.docker.com/storage/volumes/)) + * Docker containers, both running and stopped + * Docker images and cached layers + +Codefresh offers two options to manage disk space and prevent out-of-space errors: +* Use runtime cleaners on Docker images and volumes +* [Set the minimum disk space per pipeline build volume](https://codefresh.io/docs/docs/pipelines/pipelines/#set-minimum-disk-space-for-a-pipeline-build) + +To improve performance by using Docker cache, Codefresh `volume-provisioner` can provision previously used disks with Docker images and pipeline volumes from previously run builds. + +### Types of runtime volume cleaners + +Docker images and volumes must be cleaned on a regular basis. + +* [IN-DIND cleaner](https://github.com/codefresh-io/dind/tree/master/cleaner): Deletes extra Docker containers, volumes, and images in **DIND pod**. +* [External volume cleaner](https://github.com/codefresh-io/dind-volume-cleanup): Deletes unused **external** PVs (EBS, GCE/Azure disks). +* [Local volume cleaner](https://github.com/codefresh-io/dind-volume-utils/blob/master/local-volumes/lv-cleaner.sh): Deletes **local** volumes if node disk space is close to the threshold. + +### IN-DIND cleaner + +**Purpose:** Removes unneeded *docker containers, images, volumes* inside Kubernetes volume mounted on the DIND pod + +**How it runs:** Inside each DIND pod as script + +**Triggered by:** SIGTERM and also during the run when disk usage > 90% (configurable) + +**Configured by:** Environment Variables which can be set in Runtime Environment spec + +**Configuration/Logic:** [README.md](https://github.com/codefresh-io/dind/tree/master/cleaner#readme) + +Override `.Values.runtime.dind.env` if necessary (the following are **defaults**): + +```yaml +runtime: + dind: + env: + CLEAN_PERIOD_SECONDS: '21600' # launch clean if last clean was more than CLEAN_PERIOD_SECONDS seconds ago + CLEAN_PERIOD_BUILDS: '5' # launch clean if last clean was more CLEAN_PERIOD_BUILDS builds since last build + IMAGE_RETAIN_PERIOD: '14400' # do not delete docker images if they have events since current_timestamp - IMAGE_RETAIN_PERIOD + VOLUMES_RETAIN_PERIOD: '14400' # do not delete docker volumes if they have events since current_timestamp - VOLUMES_RETAIN_PERIOD + DISK_USAGE_THRESHOLD: '0.8' # launch clean based on current disk usage DISK_USAGE_THRESHOLD + INODES_USAGE_THRESHOLD: '0.8' # launch clean based on current inodes usage INODES_USAGE_THRESHOLD +``` + +### External volumes cleaner + +**Purpose:** Removes unused *kubernetes volumes and related backend volumes* + +**How it runs:** Runs as `dind-volume-cleanup` CronJob. Installed in case the Runner uses non-local volumes `.Values.storage.backend != local` + +**Triggered by:** CronJob every 10min (configurable) + +**Configuration:** + +Set `codefresh.io/volume-retention` for dinds' PVCs: + +```yaml +runtime: + dind: + pvcs: + dind: + ... + annotations: + codefresh.io/volume-retention: 7d +``` + +Or override environment variables for `dind-volume-cleanup` cronjob: + +```yaml +volumeProvisioner: + dind-volume-cleanup: + env: + RETENTION_DAYS: 7 # clean volumes that were last used more than `RETENTION_DAYS` (default is 4) ago +``` + +### Local volumes cleaner + +**Purpose:** Deletes local volumes when node disk space is close to the threshold + +**How it runs:** Runs as `dind-lv-monitor` DaemonSet. Installed in case the Runner uses local volumes `.Values.storage.backend == local` + +**Triggered by:** Disk space usage or inode usage that exceeds thresholds (configurable) + +**Configuration:** + +Override environment variables for `dind-lv-monitor` daemonset: + +```yaml +volumeProvisioner: + dind-lv-monitor: + env: + KB_USAGE_THRESHOLD: 60 # default 80 (percentage) + INODE_USAGE_THRESHOLD: 60 # default 80 +``` + +### Rootless DinD + +DinD pod runs a `priviliged` container with **rootfull** docker. + +To run the docker daemon as non-root user (**rootless** mode), refer to `values-rootless.yaml`: + +```yaml +volumeProvisioner: + env: + IS_ROOTLESS: true + # -- Only if local volumes are used as backend storage (ignored for ebs/ebs-csi disks) + dind-lv-monitor: + image: + tag: 1.30.0-rootless + digest: sha256:712e549e6e843b04684647f17e0973f8047e0d60e6e8b38a693ea64dc75b0479 + containerSecurityContext: + runAsUser: 1000 + podSecurityContext: + fsGroup: 1000 + # Ref: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/#configure-volume-permission-and-ownership-change-policy-for-pods + fsGroupChangePolicy: "OnRootMismatch" + # -- Enable initContainer to run chmod for /var/lib/codefresh/dind-volumes on host nodes + volumePermissions: + enabled: false + +runtime: + dind: + image: + tag: 26.1.4-1.28.10-rootless + digest: sha256:59dfc004eb22a8f09c8a3d585271a055af9df4591ab815bca418c24a2077f5c8 + userVolumeMounts: + dind: + name: dind + mountPath: /home/rootless/.local/share/docker + containerSecurityContext: + privileged: true + runAsUser: 1000 + podSecurityContext: + fsGroup: 1000 + # Ref: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/#configure-volume-permission-and-ownership-change-policy-for-pods + fsGroupChangePolicy: "OnRootMismatch" + # -- Enable initContainer to run chmod for /home/rootless in DinD pod + # !!! Will slow down dind pod startup + volumePermissions: + enabled: true +``` + +### ARM + +With the Codefresh Runner, you can run native ARM64v8 builds. + +> **Note!** +> You cannot run both amd64 and arm64 images within the same pipeline. As one pipeline can map only to one runtime, you can run either amd64 or arm64 within the same pipeline. + +Provide `nodeSelector` and(or) `tolerations` for dind pods: + +`values.yaml` +```yaml +runtime: + dind: + nodeSelector: + arch: arm64 + tolerations: + - key: arch + operator: Equal + value: arm64 + effect: NoSchedule +``` + +### Openshift + +To install Codefresh Runner on OpenShift use the following `values.yaml` example + +```yaml +runner: + podSecurityContext: + enabled: false + +volumeProvisioner: + podSecurityContext: + enabled: false + env: + PRIVILEGED_CONTAINER: true + dind-lv-monitor: + containerSecurityContext: + enabled: true + privileged: true + volumePermissions: + enabled: true + securityContext: + privileged: true + runAsUser: auto +``` + +Grant `privileged` SCC to `cf-runtime-runner` and `cf-runtime-volume-provisioner` service accounts. + +```console +oc adm policy add-scc-to-user privileged system:serviceaccount:codefresh:cf-runtime-runner + +oc adm policy add-scc-to-user privileged system:serviceaccount:codefresh:cf-runtime-volume-provisioner +``` + +### On-premise + +If you have [Codefresh On-Premises](https://artifacthub.io/packages/helm/codefresh-onprem/codefresh) deployed, you can install Codefresh Runner in **agentless** mode. + +**What is agentless mode?** + +Agent (aka venona) is Runner component which responsible for calling Codefresh API to run builds and create dind/engine pods and pvc objects. Agent can only be assigned to a single account, thus you can't share one runtime across multiple accounts. However, with **agentless** mode it's possible to register the runtime as **system**-type runtime so it's registered on the platform level and can be assigned/shared across multiple accounts. + +**What are the prerequisites?** +- You have a running [Codefresh On-Premises](https://artifacthub.io/packages/helm/codefresh-onprem/codefresh) control-plane environment +- You have a Codefresh API token with platform **Admin** permissions scope + +### How to deploy agentless runtime when it's on the SAME k8s cluster as On-Premises control-plane environment? + +- Enable cluster-level permissions for cf-api (On-Premises control-plane component) + +> `values.yaml` for [Codefresh On-Premises](https://artifacthub.io/packages/helm/codefresh-onprem/codefresh) Helm chart +```yaml +cfapi: + ... + # -- Enable ClusterRole/ClusterRoleBinding + rbac: + namespaced: false +``` + +- Set the following values for Runner Helm chart + +`.Values.global.codefreshHost=...` \ +`.Values.global.codefreshToken=...` \ +`.Values.global.runtimeName=system/...` \ +`.Values.runtime.agent=false` \ +`.Values.runtime.inCluster=true` + +> `values.yaml` for [Codefresh Runner](https://artifacthub.io/packages/helm/codefresh-runner/cf-runtime) helm chart +```yaml +global: + # -- URL of Codefresh On-Premises Platform + codefreshHost: "https://myonprem.somedomain.com" + # -- User token in plain text with Admin permission scope + codefreshToken: "" + # -- User token that references an existing secret containing API key. + codefreshTokenSecretKeyRef: {} + # E.g. + # codefreshTokenSecretKeyRef: + # name: my-codefresh-api-token + # key: codefresh-api-token + + # -- Distinguished runtime name + # (for On-Premise only; mandatory!) Must be prefixed with "system/..." + runtimeName: "system/prod-ue1-some-cluster-name" + +# -- Set runtime parameters +runtime: + # -- (for On-Premise only; mandatory!) Disable agent + agent: false + # -- (for On-Premise only; optional) Set inCluster runtime (default: `true`) + # `inCluster=true` flag is set when Runtime and On-Premises control-plane are run on the same cluster + # `inCluster=false` flag is set when Runtime and On-Premises control-plane are on different clusters + inCluster: true + # -- (for On-Premise only; optional) Assign accounts to runtime (list of account ids; default is empty) + # Accounts can be assigned to the runtime in Codefresh UI later so you can kepp it empty. + accounts: [] + # -- Set parent runtime to inherit. + runtimeExtends: [] +``` + +- Install the chart + +```console +helm upgrade --install cf-runtime oci://quay.io/codefresh/cf-runtime -f values.yaml --create-namespace --namespace cf-runtime +``` + +- Verify the runtime and run test pipeline + +Go to [https:///admin/runtime-environments/system](https:///admin/runtime-environments/system) to check the runtime. Assign it to the required account(s). Run test pipeline on it. + +### How to deploy agentless runtime when it's on the DIFFERENT k8s cluster than On-Premises control-plane environment? + +In this case, it's required to mount runtime cluster's `KUBECONFIG` into On-Premises `cf-api` deployment + +- Create the neccessary RBAC resources + +> `values.yaml` for [Codefresh Runner](https://artifacthub.io/packages/helm/codefresh-runner/cf-runtime) helm chart +```yaml +extraResources: +- apiVersion: rbac.authorization.k8s.io/v1 + kind: Role + metadata: + name: codefresh-role + namespace: '{{ .Release.Namespace }}' + rules: + - apiGroups: [""] + resources: ["pods", "persistentvolumeclaims", "persistentvolumes"] + verbs: ["list", "watch", "get", "create", "patch", "delete"] + - apiGroups: ["snapshot.storage.k8s.io"] + resources: ["volumesnapshots"] + verbs: ["list", "watch", "get", "create", "patch", "delete"] +- apiVersion: v1 + kind: ServiceAccount + metadata: + name: codefresh-runtime-user + namespace: '{{ .Release.Namespace }}' +- apiVersion: rbac.authorization.k8s.io/v1 + kind: RoleBinding + metadata: + name: codefresh-runtime-user + namespace: '{{ .Release.Namespace }}' + roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: codefresh-role + subjects: + - kind: ServiceAccount + name: codefresh-runtime-user + namespace: '{{ .Release.Namespace }}' +- apiVersion: v1 + kind: Secret + metadata: + name: codefresh-runtime-user-token + namespace: '{{ .Release.Namespace }}' + annotations: + kubernetes.io/service-account.name: codefresh-runtime-user + type: kubernetes.io/service-account-token +``` + +- Set up the following environment variables to create a `KUBECONFIG` file + +```shell +NAMESPACE=cf-runtime +CLUSTER_NAME=prod-ue1-some-cluster-name +CURRENT_CONTEXT=$(kubectl config current-context) + +USER_TOKEN_VALUE=$(kubectl -n cf-runtime get secret/codefresh-runtime-user-token -o=go-template='{{.data.token}}' | base64 --decode) +CURRENT_CLUSTER=$(kubectl config view --raw -o=go-template='{{range .contexts}}{{if eq .name "'''${CURRENT_CONTEXT}'''"}}{{ index .context "cluster" }}{{end}}{{end}}') +CLUSTER_CA=$(kubectl config view --raw -o=go-template='{{range .clusters}}{{if eq .name "'''${CURRENT_CLUSTER}'''"}}"{{with index .cluster "certificate-authority-data" }}{{.}}{{end}}"{{ end }}{{ end }}') +CLUSTER_SERVER=$(kubectl config view --raw -o=go-template='{{range .clusters}}{{if eq .name "'''${CURRENT_CLUSTER}'''"}}{{ .cluster.server }}{{end}}{{ end }}') + +export -p USER_TOKEN_VALUE CURRENT_CONTEXT CURRENT_CLUSTER CLUSTER_CA CLUSTER_SERVER CLUSTER_NAME +``` + +- Create a kubeconfig file + +```console +cat << EOF > $CLUSTER_NAME-kubeconfig +apiVersion: v1 +kind: Config +current-context: ${CLUSTER_NAME} +contexts: +- name: ${CLUSTER_NAME} + context: + cluster: ${CLUSTER_NAME} + user: codefresh-runtime-user + namespace: ${NAMESPACE} +clusters: +- name: ${CLUSTER_NAME} + cluster: + certificate-authority-data: ${CLUSTER_CA} + server: ${CLUSTER_SERVER} +users: +- name: ${CLUSTER_NAME} + user: + token: ${USER_TOKEN_VALUE} +EOF +``` + +- **Switch context to On-Premises control-plane cluster**. Create k8s secret (via any tool like [ESO](https://external-secrets.io/v0.4.4/), `kubectl`, etc ) containing runtime cluster's `KUBECONFG` created in previous step. + +```shell +NAMESPACE=codefresh +kubectl create secret generic dind-runtime-clusters --from-file=$CLUSTER_NAME=$CLUSTER_NAME-kubeconfig -n $NAMESPACE +``` + +- Mount secret containing runtime cluster's `KUBECONFG` into cf-api in On-Premises control-plane cluster + +> `values.yaml` for [Codefresh On-Premises](https://artifacthub.io/packages/helm/codefresh-onprem/codefresh) helm chart +```yaml +cf-api: + ... + volumes: + dind-clusters: + enabled: true + type: secret + nameOverride: dind-runtime-clusters + optional: true +``` +> volumeMount `/etc/kubeconfig` is already configured in cf-api Helm chart template. No need to specify it. + +- Set the following values for Runner helm chart + +> `values.yaml` for [Codefresh Runner](https://artifacthub.io/packages/helm/codefresh-runner/cf-runtime) helm chart + +`.Values.global.codefreshHost=...` \ +`.Values.global.codefreshToken=...` \ +`.Values.global.runtimeName=system/...` \ +`.Values.runtime.agent=false` \ +`.Values.runtime.inCluster=false` + +**Important!** +`.Values.global.name` ("system/" prefix is ignored!) should match the cluster name (key in `dind-runtime-clusters` secret created previously) +```yaml +global: + # -- URL of Codefresh On-Premises Platform + codefreshHost: "https://myonprem.somedomain.com" + # -- User token in plain text with Admin permission scope + codefreshToken: "" + # -- User token that references an existing secret containing API key. + codefreshTokenSecretKeyRef: {} + # E.g. + # codefreshTokenSecretKeyRef: + # name: my-codefresh-api-token + # key: codefresh-api-token + + # -- Distinguished runtime name + # (for On-Premise only; mandatory!) Must be prefixed with "system/..." + name: "system/prod-ue1-some-cluster-name" + +# -- Set runtime parameters +runtime: + # -- (for On-Premise only; mandatory!) Disable agent + agent: false + # -- (for On-Premise only; optional) Set inCluster runtime (default: `true`) + # `inCluster=true` flag is set when Runtime and On-Premises control-plane are run on the same cluster + # `inCluster=false` flag is set when Runtime and On-Premises control-plane are on different clusters + inCluster: false + # -- (for On-Premise only; optional) Assign accounts to runtime (list of account ids; default is empty) + # Accounts can be assigned to the runtime in Codefresh UI later so you can kepp it empty. + accounts: [] + # -- (optional) Set parent runtime to inherit. + runtimeExtends: [] +``` + +- Install the chart + +```console +helm upgrade --install cf-runtime oci://quay.io/codefresh/cf-runtime -f values.yaml --create-namespace --namespace cf-runtime +``` + +- Verify the runtime and run test pipeline + +Go to [https:///admin/runtime-environments/system](https:///admin/runtime-environments/system) to see the runtime. Assign it to the required account(s). + +## Requirements + +| Repository | Name | Version | +|------------|------|---------| +| oci://quay.io/codefresh/charts | cf-common | 0.21.0 | + +## Values + +| Key | Type | Default | Description | +|-----|------|---------|-------------| +| appProxy.affinity | object | `{}` | Set affinity | +| appProxy.enabled | bool | `false` | Enable app-proxy | +| appProxy.env | object | `{}` | Add additional env vars | +| appProxy.image | object | `{"digest":"sha256:cc5054ec572a33bb3545cb065c2082ecf988113efffe8235e57ed93d27b861a6","registry":"quay.io","repository":"codefresh/cf-app-proxy","tag":"0.0.50"}` | Set image | +| appProxy.ingress.annotations | object | `{}` | Set extra annotations for ingress object | +| appProxy.ingress.class | string | `""` | Set ingress class | +| appProxy.ingress.host | string | `""` | Set DNS hostname the ingress will use | +| appProxy.ingress.pathPrefix | string | `""` | Set path prefix for ingress (keep empty for default `/` path) | +| appProxy.ingress.tlsSecret | string | `""` | Set k8s tls secret for the ingress object | +| appProxy.nodeSelector | object | `{}` | Set node selector | +| appProxy.podAnnotations | object | `{}` | Set pod annotations | +| appProxy.podSecurityContext | object | `{}` | Set security context for the pod | +| appProxy.rbac | object | `{"create":true,"namespaced":true,"rules":[]}` | RBAC parameters | +| appProxy.rbac.create | bool | `true` | Create RBAC resources | +| appProxy.rbac.namespaced | bool | `true` | Use Role(true)/ClusterRole(true) | +| appProxy.rbac.rules | list | `[]` | Add custom rule to the role | +| appProxy.readinessProbe | object | See below | Readiness probe configuration | +| appProxy.replicasCount | int | `1` | Set number of pods | +| appProxy.resources | object | `{}` | Set requests and limits | +| appProxy.serviceAccount | object | `{"annotations":{},"create":true,"name":"","namespaced":true}` | Service Account parameters | +| appProxy.serviceAccount.annotations | object | `{}` | Additional service account annotations | +| appProxy.serviceAccount.create | bool | `true` | Create service account | +| appProxy.serviceAccount.name | string | `""` | Override service account name | +| appProxy.serviceAccount.namespaced | bool | `true` | Use Role(true)/ClusterRole(true) | +| appProxy.tolerations | list | `[]` | Set tolerations | +| appProxy.updateStrategy | object | `{"type":"RollingUpdate"}` | Upgrade strategy | +| dockerRegistry | string | `""` | | +| event-exporter | object | See below | Event exporter parameters | +| event-exporter.affinity | object | `{}` | Set affinity | +| event-exporter.enabled | bool | `false` | Enable event-exporter | +| event-exporter.env | object | `{}` | Add additional env vars | +| event-exporter.image | object | `{"digest":"sha256:cf52048f1378fb6659dffd1394d68fdf23a7ea709585dc14b5007f3e5a1b7584","registry":"docker.io","repository":"codefresh/k8s-event-exporter","tag":"latest"}` | Set image | +| event-exporter.nodeSelector | object | `{}` | Set node selector | +| event-exporter.podAnnotations | object | `{}` | Set pod annotations | +| event-exporter.podSecurityContext | object | See below | Set security context for the pod | +| event-exporter.rbac | object | `{"create":true,"rules":[]}` | RBAC parameters | +| event-exporter.rbac.create | bool | `true` | Create RBAC resources | +| event-exporter.rbac.rules | list | `[]` | Add custom rule to the role | +| event-exporter.replicasCount | int | `1` | Set number of pods | +| event-exporter.resources | object | `{}` | Set resources | +| event-exporter.serviceAccount | object | `{"annotations":{},"create":true,"name":""}` | Service Account parameters | +| event-exporter.serviceAccount.annotations | object | `{}` | Additional service account annotations | +| event-exporter.serviceAccount.create | bool | `true` | Create service account | +| event-exporter.serviceAccount.name | string | `""` | Override service account name | +| event-exporter.tolerations | list | `[]` | Set tolerations | +| event-exporter.updateStrategy | object | `{"type":"Recreate"}` | Upgrade strategy | +| extraResources | list | `[]` | Array of extra objects to deploy with the release | +| fullnameOverride | string | `""` | String to fully override cf-runtime.fullname template | +| global | object | See below | Global parameters | +| global.accountId | string | `""` | Account ID (required!) Can be obtained here https://g.codefresh.io/2.0/account-settings/account-information | +| global.agentName | string | `""` | Agent Name (optional!) If omitted, the following format will be used `{{ .Values.global.context }}_{{ .Release.Namespace }}` | +| global.agentToken | string | `""` | DEPRECATED Agent token in plain text. !!! MUST BE provided if migrating from < 6.x chart version | +| global.agentTokenSecretKeyRef | object | `{}` | DEPRECATED Agent token that references an existing secret containing API key. !!! MUST BE provided if migrating from < 6.x chart version | +| global.codefreshHost | string | `"https://g.codefresh.io"` | URL of Codefresh Platform (required!) | +| global.codefreshToken | string | `""` | User token in plain text (required if `global.codefreshTokenSecretKeyRef` is omitted!) Ref: https://g.codefresh.io/user/settings (see API Keys) Minimal API key scopes: Runner-Installation(read+write), Agent(read+write), Agents(read+write) | +| global.codefreshTokenSecretKeyRef | object | `{}` | User token that references an existing secret containing API key (required if `global.codefreshToken` is omitted!) | +| global.context | string | `""` | K8s context name (required!) | +| global.imagePullSecrets | list | `[]` | Global Docker registry secret names as array | +| global.imageRegistry | string | `""` | Global Docker image registry | +| global.runtimeName | string | `""` | Runtime name (optional!) If omitted, the following format will be used `{{ .Values.global.context }}/{{ .Release.Namespace }}` | +| monitor.affinity | object | `{}` | Set affinity | +| monitor.enabled | bool | `false` | Enable monitor Ref: https://codefresh.io/docs/docs/installation/codefresh-runner/#install-monitoring-component | +| monitor.env | object | `{}` | Add additional env vars | +| monitor.image | object | `{"digest":"sha256:3cc7b3d41f841604133197a44f016db499f3e91e26448da36ce739a0b1171d05","registry":"quay.io","repository":"codefresh/cf-k8s-agent","tag":"1.3.21"}` | Set image | +| monitor.nodeSelector | object | `{}` | Set node selector | +| monitor.podAnnotations | object | `{}` | Set pod annotations | +| monitor.podSecurityContext | object | `{}` | | +| monitor.rbac | object | `{"create":true,"namespaced":true,"rules":[]}` | RBAC parameters | +| monitor.rbac.create | bool | `true` | Create RBAC resources | +| monitor.rbac.namespaced | bool | `true` | Use Role(true)/ClusterRole(true) | +| monitor.rbac.rules | list | `[]` | Add custom rule to the role | +| monitor.readinessProbe | object | See below | Readiness probe configuration | +| monitor.replicasCount | int | `1` | Set number of pods | +| monitor.resources | object | `{}` | Set resources | +| monitor.serviceAccount | object | `{"annotations":{},"create":true,"name":""}` | Service Account parameters | +| monitor.serviceAccount.annotations | object | `{}` | Additional service account annotations | +| monitor.serviceAccount.create | bool | `true` | Create service account | +| monitor.serviceAccount.name | string | `""` | Override service account name | +| monitor.tolerations | list | `[]` | Set tolerations | +| monitor.updateStrategy | object | `{"type":"RollingUpdate"}` | Upgrade strategy | +| nameOverride | string | `""` | String to partially override cf-runtime.fullname template (will maintain the release name) | +| podMonitor | object | See below | Add podMonitor (for engine pods) | +| podMonitor.main.enabled | bool | `false` | Enable pod monitor for engine pods | +| podMonitor.runner.enabled | bool | `false` | Enable pod monitor for runner pod | +| podMonitor.volume-provisioner.enabled | bool | `false` | Enable pod monitor for volumeProvisioner pod | +| re | object | `{}` | | +| runner | object | See below | Runner parameters | +| runner.affinity | object | `{}` | Set affinity | +| runner.enabled | bool | `true` | Enable the runner | +| runner.env | object | `{}` | Add additional env vars | +| runner.image | object | `{"digest":"sha256:bcc6e7495186f1f9c3e885afa891a3bda11b5374a577f069f34ddc75142342ef","registry":"quay.io","repository":"codefresh/venona","tag":"2.0.0"}` | Set image | +| runner.init | object | `{"image":{"digest":"sha256:b256d150ff8a636851ddc1d5fb0490114d5036cc5bff357eac6a9899fea87562","registry":"quay.io","repository":"codefresh/cli","tag":"0.88.4-rootless"},"resources":{"limits":{"cpu":"1","memory":"512Mi"},"requests":{"cpu":"0.2","memory":"256Mi"}}}` | Init container | +| runner.nodeSelector | object | `{}` | Set node selector | +| runner.podAnnotations | object | `{}` | Set pod annotations | +| runner.podSecurityContext | object | See below | Set security context for the pod | +| runner.rbac | object | `{"create":true,"rules":[]}` | RBAC parameters | +| runner.rbac.create | bool | `true` | Create RBAC resources | +| runner.rbac.rules | list | `[]` | Add custom rule to the role | +| runner.readinessProbe | object | See below | Readiness probe configuration | +| runner.replicasCount | int | `1` | Set number of pods | +| runner.resources | object | `{}` | Set requests and limits | +| runner.serviceAccount | object | `{"annotations":{},"create":true,"name":""}` | Service Account parameters | +| runner.serviceAccount.annotations | object | `{}` | Additional service account annotations | +| runner.serviceAccount.create | bool | `true` | Create service account | +| runner.serviceAccount.name | string | `""` | Override service account name | +| runner.sidecar | object | `{"enabled":false,"env":{"RECONCILE_INTERVAL":300},"image":{"digest":"sha256:a30a8810dde249d0198f67792ed9696363f15c8cecbac955ee9bd267b5454ee7","registry":"quay.io","repository":"codefresh/kubectl","tag":"1.31.2"},"resources":{}}` | Sidecar container Reconciles runtime spec from Codefresh API for drift detection | +| runner.tolerations | list | `[]` | Set tolerations | +| runner.updateStrategy | object | `{"type":"RollingUpdate"}` | Upgrade strategy | +| runtime | object | See below | Set runtime parameters | +| runtime.accounts | list | `[]` | (for On-Premise only) Assign accounts to runtime (list of account ids) | +| runtime.agent | bool | `true` | (for On-Premise only) Enable agent | +| runtime.description | string | `""` | Runtime description | +| runtime.dind | object | `{"affinity":{},"containerSecurityContext":{},"env":{"DOCKER_ENABLE_DEPRECATED_PULL_SCHEMA_1_IMAGE":true},"image":{"digest":"sha256:33c343dd01e8a24f0b4a872bbe62884320719f9d9dc27b7a8fed9f7e9fc7e80e","pullPolicy":"IfNotPresent","registry":"quay.io","repository":"codefresh/dind","tag":"26.1.4-1.28.8"},"nodeSelector":{},"podAnnotations":{},"podLabels":{},"podSecurityContext":{},"pvcs":{"dind":{"annotations":{},"name":"dind","reuseVolumeSelector":"codefresh-app,io.codefresh.accountName","reuseVolumeSortOrder":"pipeline_id","storageClassName":"{{ include \"dind-volume-provisioner.storageClassName\" . }}","volumeSize":"16Gi"}},"resources":{"limits":{"cpu":"400m","memory":"800Mi"},"requests":null},"schedulerName":"","serviceAccount":"codefresh-engine","terminationGracePeriodSeconds":30,"tolerations":[],"userAccess":true,"userVolumeMounts":{},"userVolumes":{},"volumePermissions":{"enabled":false,"image":{"digest":"sha256:de0eb0b3f2a47ba1eb89389859a9bd88b28e82f5826b6969ad604979713c2d4f","registry":"docker.io","repository":"alpine","tag":3.18},"resources":{},"securityContext":{"runAsUser":0}}}` | Parameters for DinD (docker-in-docker) pod (aka "runtime" pod). | +| runtime.dind.affinity | object | `{}` | Set affinity | +| runtime.dind.containerSecurityContext | object | `{}` | Set container security context. | +| runtime.dind.env | object | `{"DOCKER_ENABLE_DEPRECATED_PULL_SCHEMA_1_IMAGE":true}` | Set additional env vars. | +| runtime.dind.image | object | `{"digest":"sha256:33c343dd01e8a24f0b4a872bbe62884320719f9d9dc27b7a8fed9f7e9fc7e80e","pullPolicy":"IfNotPresent","registry":"quay.io","repository":"codefresh/dind","tag":"26.1.4-1.28.8"}` | Set dind image. | +| runtime.dind.nodeSelector | object | `{}` | Set node selector. | +| runtime.dind.podAnnotations | object | `{}` | Set pod annotations. | +| runtime.dind.podLabels | object | `{}` | Set pod labels. | +| runtime.dind.podSecurityContext | object | `{}` | Set security context for the pod. | +| runtime.dind.pvcs | object | `{"dind":{"annotations":{},"name":"dind","reuseVolumeSelector":"codefresh-app,io.codefresh.accountName","reuseVolumeSortOrder":"pipeline_id","storageClassName":"{{ include \"dind-volume-provisioner.storageClassName\" . }}","volumeSize":"16Gi"}}` | PV claim spec parametes. | +| runtime.dind.pvcs.dind | object | `{"annotations":{},"name":"dind","reuseVolumeSelector":"codefresh-app,io.codefresh.accountName","reuseVolumeSortOrder":"pipeline_id","storageClassName":"{{ include \"dind-volume-provisioner.storageClassName\" . }}","volumeSize":"16Gi"}` | Default dind PVC parameters | +| runtime.dind.pvcs.dind.annotations | object | `{}` | PV annotations. | +| runtime.dind.pvcs.dind.name | string | `"dind"` | PVC name prefix. Keep `dind` as default! Don't change! | +| runtime.dind.pvcs.dind.reuseVolumeSelector | string | `"codefresh-app,io.codefresh.accountName"` | PV reuse selector. Ref: https://codefresh.io/docs/docs/installation/codefresh-runner/#volume-reuse-policy | +| runtime.dind.pvcs.dind.storageClassName | string | `"{{ include \"dind-volume-provisioner.storageClassName\" . }}"` | PVC storage class name. Change ONLY if you need to use storage class NOT from Codefresh volume-provisioner | +| runtime.dind.pvcs.dind.volumeSize | string | `"16Gi"` | PVC size. | +| runtime.dind.resources | object | `{"limits":{"cpu":"400m","memory":"800Mi"},"requests":null}` | Set dind resources. | +| runtime.dind.schedulerName | string | `""` | Set scheduler name. | +| runtime.dind.serviceAccount | string | `"codefresh-engine"` | Set service account for pod. | +| runtime.dind.terminationGracePeriodSeconds | int | `30` | Set termination grace period. | +| runtime.dind.tolerations | list | `[]` | Set tolerations. | +| runtime.dind.userAccess | bool | `true` | Keep `true` as default! | +| runtime.dind.userVolumeMounts | object | `{}` | Add extra volume mounts | +| runtime.dind.userVolumes | object | `{}` | Add extra volumes | +| runtime.dindDaemon | object | See below | DinD pod daemon config | +| runtime.engine | object | `{"affinity":{},"command":["npm","run","start"],"env":{"CONTAINER_LOGGER_EXEC_CHECK_INTERVAL_MS":1000,"DOCKER_REQUEST_TIMEOUT_MS":30000,"FORCE_COMPOSE_SERIAL_PULL":false,"LOGGER_LEVEL":"debug","LOG_OUTGOING_HTTP_REQUESTS":false,"METRICS_PROMETHEUS_COLLECT_PROCESS_METRICS":false,"METRICS_PROMETHEUS_ENABLED":true,"METRICS_PROMETHEUS_ENABLE_LEGACY_METRICS":false,"METRICS_PROMETHEUS_HOST":"0.0.0.0","METRICS_PROMETHEUS_PORT":9100,"METRICS_PROMETHEUS_SCRAPE_TIMEOUT":"15000","TRUSTED_QEMU_IMAGES":""},"image":{"digest":"sha256:cfb0ece760033f95f37a40ca17614d91c928b4018705ad46fd39392daf578bc6","pullPolicy":"IfNotPresent","registry":"quay.io","repository":"codefresh/engine","tag":"1.177.4"},"nodeSelector":{},"podAnnotations":{},"podLabels":{},"resources":{"limits":{"cpu":"1000m","memory":"2048Mi"},"requests":{"cpu":"100m","memory":"128Mi"}},"runtimeImages":{"COMPOSE_IMAGE":"quay.io/codefresh/compose:v2.32.2-1.5.2@sha256:9177054614f6db006a3500d2b9b8d2cafac4073ce891929d93e117714fccbd4b","CONTAINER_LOGGER_IMAGE":"quay.io/codefresh/cf-container-logger:1.12.2@sha256:b3cbe2088f8fd0c48a0fa6df6c9ab8ad9d1d3c840a57f2c89520a655e2a8c116","COSIGN_IMAGE_SIGNER_IMAGE":"quay.io/codefresh/cf-cosign-image-signer:2.4.0-cf.2@sha256:5e0993207aa809c25ed70cf89af444d9720892fb4a29deb82db45618b0cae4a9","CR_6177_FIXER":"alpine:edge@sha256:115729ec5cb049ba6359c3ab005ac742012d92bbaa5b8bc1a878f1e8f62c0cb8","DOCKER_BUILDER_IMAGE":"quay.io/codefresh/cf-docker-builder:1.4.2@sha256:8df63d741e7997902fa2ee7a0c68b3c3fd9003429bb093a3a9563b2cd2ec219c","DOCKER_PULLER_IMAGE":"quay.io/codefresh/cf-docker-puller:8.0.18@sha256:1a15c3ae0952d3986de7866a3def8ac7e3e39f668fe87fd46c63d886ca06c6d7","DOCKER_PUSHER_IMAGE":"quay.io/codefresh/cf-docker-pusher:6.0.16@sha256:05efc1af8b1196f1b9b3f0781b4dcc1aa2cdd0ffc1347ee5fa81b16d029ec5c2","DOCKER_TAG_PUSHER_IMAGE":"quay.io/codefresh/cf-docker-tag-pusher:1.3.15@sha256:3a3e90cd10801c7ec0d3cf3816d0dcc90894d5d1771448c43f67215d90da5eca","FS_OPS_IMAGE":"quay.io/codefresh/fs-ops:1.2.8@sha256:dc05888d84a959787a738caef914f83aa7392ff49c16767e612a29e180826f35","GC_BUILDER_IMAGE":"quay.io/codefresh/cf-gc-builder:0.5.3@sha256:33ac914e6b844909f188a208cf90e569358cafa5aaa60f49848f49d99bcaf875","GIT_CLONE_IMAGE":"quay.io/codefresh/cf-git-cloner:10.2.0@sha256:a3ec854823f17d0fd817d978219122e644b1abd6db778fd835688fcb6d88c515","KUBE_DEPLOY":"quay.io/codefresh/cf-deploy-kubernetes:16.1.11@sha256:b6b3fc6cc5fad3ba9e36055278ce99a74a86876be116574503c6fbb4c1b4aa76","PIPELINE_DEBUGGER_IMAGE":"quay.io/codefresh/cf-debugger:1.3.7@sha256:3391822b7ad9835cc2a3a0ce5aaa55774ca110a8682d9512205dea24f438718a","TEMPLATE_ENGINE":"quay.io/codefresh/pikolo:0.14.2@sha256:97874aefc46b58caf5b9d0edcfd2d6742db247e671424433363a1367020a8a65"},"schedulerName":"","serviceAccount":"codefresh-engine","terminationGracePeriodSeconds":180,"tolerations":[],"userEnvVars":[],"workflowLimits":{"MAXIMUM_ALLOWED_TIME_BEFORE_PRE_STEPS_SUCCESS":600,"MAXIMUM_ALLOWED_WORKFLOW_AGE_BEFORE_TERMINATION":86400,"MAXIMUM_ELECTED_STATE_AGE_ALLOWED":900,"MAXIMUM_RETRY_ATTEMPTS_ALLOWED":20,"MAXIMUM_TERMINATING_STATE_AGE_ALLOWED":900,"MAXIMUM_TERMINATING_STATE_AGE_ALLOWED_WITHOUT_UPDATE":300,"TIME_ENGINE_INACTIVE_UNTIL_TERMINATION":300,"TIME_ENGINE_INACTIVE_UNTIL_UNHEALTHY":60,"TIME_INACTIVE_UNTIL_TERMINATION":2700}}` | Parameters for Engine pod (aka "pipeline" orchestrator). | +| runtime.engine.affinity | object | `{}` | Set affinity | +| runtime.engine.command | list | `["npm","run","start"]` | Set container command. | +| runtime.engine.env | object | `{"CONTAINER_LOGGER_EXEC_CHECK_INTERVAL_MS":1000,"DOCKER_REQUEST_TIMEOUT_MS":30000,"FORCE_COMPOSE_SERIAL_PULL":false,"LOGGER_LEVEL":"debug","LOG_OUTGOING_HTTP_REQUESTS":false,"METRICS_PROMETHEUS_COLLECT_PROCESS_METRICS":false,"METRICS_PROMETHEUS_ENABLED":true,"METRICS_PROMETHEUS_ENABLE_LEGACY_METRICS":false,"METRICS_PROMETHEUS_HOST":"0.0.0.0","METRICS_PROMETHEUS_PORT":9100,"METRICS_PROMETHEUS_SCRAPE_TIMEOUT":"15000","TRUSTED_QEMU_IMAGES":""}` | Set additional env vars. | +| runtime.engine.env.CONTAINER_LOGGER_EXEC_CHECK_INTERVAL_MS | int | `1000` | Interval to check the exec status in the container-logger | +| runtime.engine.env.DOCKER_REQUEST_TIMEOUT_MS | int | `30000` | Timeout while doing requests to the Docker daemon | +| runtime.engine.env.FORCE_COMPOSE_SERIAL_PULL | bool | `false` | If "true", composition images will be pulled sequentially | +| runtime.engine.env.LOGGER_LEVEL | string | `"debug"` | Level of logging for engine | +| runtime.engine.env.LOG_OUTGOING_HTTP_REQUESTS | bool | `false` | Enable debug-level logging of outgoing HTTP/HTTPS requests | +| runtime.engine.env.METRICS_PROMETHEUS_COLLECT_PROCESS_METRICS | bool | `false` | Enable collecting process metrics | +| runtime.engine.env.METRICS_PROMETHEUS_ENABLED | bool | `true` | Enable emitting metrics from engine | +| runtime.engine.env.METRICS_PROMETHEUS_ENABLE_LEGACY_METRICS | bool | `false` | Enable legacy metrics | +| runtime.engine.env.METRICS_PROMETHEUS_HOST | string | `"0.0.0.0"` | Host for Prometheus metrics server | +| runtime.engine.env.METRICS_PROMETHEUS_PORT | int | `9100` | Port for Prometheus metrics server | +| runtime.engine.env.METRICS_PROMETHEUS_SCRAPE_TIMEOUT | string | `"15000"` | The timeout till the engine waits for Prometheus to pull the latest metrics before engine shuts down (in milliseconds) | +| runtime.engine.env.TRUSTED_QEMU_IMAGES | string | `""` | Trusted QEMU images used for docker builds - when left blank only 'tonistiigi/binfmt' is trusted. | +| runtime.engine.image | object | `{"digest":"sha256:cfb0ece760033f95f37a40ca17614d91c928b4018705ad46fd39392daf578bc6","pullPolicy":"IfNotPresent","registry":"quay.io","repository":"codefresh/engine","tag":"1.177.4"}` | Set image. | +| runtime.engine.nodeSelector | object | `{}` | Set node selector. | +| runtime.engine.podAnnotations | object | `{}` | Set pod annotations. | +| runtime.engine.podLabels | object | `{}` | Set pod labels. | +| runtime.engine.resources | object | `{"limits":{"cpu":"1000m","memory":"2048Mi"},"requests":{"cpu":"100m","memory":"128Mi"}}` | Set resources. | +| runtime.engine.runtimeImages | object | See below. | Set system(base) runtime images. | +| runtime.engine.schedulerName | string | `""` | Set scheduler name. | +| runtime.engine.serviceAccount | string | `"codefresh-engine"` | Set service account for pod. | +| runtime.engine.terminationGracePeriodSeconds | int | `180` | Set termination grace period. | +| runtime.engine.tolerations | list | `[]` | Set tolerations. | +| runtime.engine.userEnvVars | list | `[]` | Set extra env vars | +| runtime.engine.workflowLimits | object | `{"MAXIMUM_ALLOWED_TIME_BEFORE_PRE_STEPS_SUCCESS":600,"MAXIMUM_ALLOWED_WORKFLOW_AGE_BEFORE_TERMINATION":86400,"MAXIMUM_ELECTED_STATE_AGE_ALLOWED":900,"MAXIMUM_RETRY_ATTEMPTS_ALLOWED":20,"MAXIMUM_TERMINATING_STATE_AGE_ALLOWED":900,"MAXIMUM_TERMINATING_STATE_AGE_ALLOWED_WITHOUT_UPDATE":300,"TIME_ENGINE_INACTIVE_UNTIL_TERMINATION":300,"TIME_ENGINE_INACTIVE_UNTIL_UNHEALTHY":60,"TIME_INACTIVE_UNTIL_TERMINATION":2700}` | Set workflow limits. | +| runtime.engine.workflowLimits.MAXIMUM_ALLOWED_TIME_BEFORE_PRE_STEPS_SUCCESS | int | `600` | Maximum time allowed to the engine to wait for the pre-steps (aka "Initializing Process") to succeed; seconds. | +| runtime.engine.workflowLimits.MAXIMUM_ALLOWED_WORKFLOW_AGE_BEFORE_TERMINATION | int | `86400` | Maximum time for workflow execution; seconds. | +| runtime.engine.workflowLimits.MAXIMUM_ELECTED_STATE_AGE_ALLOWED | int | `900` | Maximum time allowed to workflow to spend in "elected" state; seconds. | +| runtime.engine.workflowLimits.MAXIMUM_RETRY_ATTEMPTS_ALLOWED | int | `20` | Maximum retry attempts allowed for workflow. | +| runtime.engine.workflowLimits.MAXIMUM_TERMINATING_STATE_AGE_ALLOWED | int | `900` | Maximum time allowed to workflow to spend in "terminating" state until force terminated; seconds. | +| runtime.engine.workflowLimits.MAXIMUM_TERMINATING_STATE_AGE_ALLOWED_WITHOUT_UPDATE | int | `300` | Maximum time allowed to workflow to spend in "terminating" state without logs activity until force terminated; seconds. | +| runtime.engine.workflowLimits.TIME_ENGINE_INACTIVE_UNTIL_TERMINATION | int | `300` | Time since the last health check report after which workflow is terminated; seconds. | +| runtime.engine.workflowLimits.TIME_ENGINE_INACTIVE_UNTIL_UNHEALTHY | int | `60` | Time since the last health check report after which the engine is considered unhealthy; seconds. | +| runtime.engine.workflowLimits.TIME_INACTIVE_UNTIL_TERMINATION | int | `2700` | Time since the last workflow logs activity after which workflow is terminated; seconds. | +| runtime.gencerts | object | See below | Parameters for `gencerts-dind` post-upgrade/install hook | +| runtime.inCluster | bool | `true` | (for On-Premise only) Set inCluster runtime | +| runtime.patch | object | See below | Parameters for `runtime-patch` post-upgrade/install hook | +| runtime.rbac | object | `{"create":true,"rules":[]}` | RBAC parameters | +| runtime.rbac.create | bool | `true` | Create RBAC resources | +| runtime.rbac.rules | list | `[]` | Add custom rule to the engine role | +| runtime.runtimeExtends | list | `["system/default/hybrid/k8s_low_limits"]` | Set parent runtime to inherit. Should not be changes. Parent runtime is controlled from Codefresh side. | +| runtime.serviceAccount | object | `{"annotations":{},"create":true}` | Set annotation on engine Service Account Ref: https://codefresh.io/docs/docs/administration/codefresh-runner/#injecting-aws-arn-roles-into-the-cluster | +| serviceMonitor | object | See below | Add serviceMonitor | +| serviceMonitor.main.enabled | bool | `false` | Enable service monitor for dind pods | +| storage.azuredisk.cachingMode | string | `"None"` | | +| storage.azuredisk.skuName | string | `"Premium_LRS"` | Set storage type (`Premium_LRS`) | +| storage.backend | string | `"local"` | Set backend volume type (`local`/`ebs`/`ebs-csi`/`gcedisk`/`azuredisk`) | +| storage.ebs.accessKeyId | string | `""` | Set AWS_ACCESS_KEY_ID for volume-provisioner (optional) Ref: https://codefresh.io/docs/docs/installation/codefresh-runner/#dind-volume-provisioner-permissions | +| storage.ebs.accessKeyIdSecretKeyRef | object | `{}` | Existing secret containing AWS_ACCESS_KEY_ID. | +| storage.ebs.availabilityZone | string | `"us-east-1a"` | Set EBS volumes availability zone (required) | +| storage.ebs.encrypted | string | `"false"` | Enable encryption (optional) | +| storage.ebs.kmsKeyId | string | `""` | Set KMS encryption key ID (optional) | +| storage.ebs.secretAccessKey | string | `""` | Set AWS_SECRET_ACCESS_KEY for volume-provisioner (optional) Ref: https://codefresh.io/docs/docs/installation/codefresh-runner/#dind-volume-provisioner-permissions | +| storage.ebs.secretAccessKeySecretKeyRef | object | `{}` | Existing secret containing AWS_SECRET_ACCESS_KEY | +| storage.ebs.volumeType | string | `"gp2"` | Set EBS volume type (`gp2`/`gp3`/`io1`) (required) | +| storage.fsType | string | `"ext4"` | Set filesystem type (`ext4`/`xfs`) | +| storage.gcedisk.availabilityZone | string | `"us-west1-a"` | Set GCP volume availability zone | +| storage.gcedisk.serviceAccountJson | string | `""` | Set Google SA JSON key for volume-provisioner (optional) | +| storage.gcedisk.serviceAccountJsonSecretKeyRef | object | `{}` | Existing secret containing containing Google SA JSON key for volume-provisioner (optional) | +| storage.gcedisk.volumeType | string | `"pd-ssd"` | Set GCP volume backend type (`pd-ssd`/`pd-standard`) | +| storage.local.volumeParentDir | string | `"/var/lib/codefresh/dind-volumes"` | Set volume path on the host filesystem | +| storage.mountAzureJson | bool | `false` | | +| volumeProvisioner | object | See below | Volume Provisioner parameters | +| volumeProvisioner.affinity | object | `{}` | Set affinity | +| volumeProvisioner.dind-lv-monitor | object | See below | `dind-lv-monitor` DaemonSet parameters (local volumes cleaner) | +| volumeProvisioner.enabled | bool | `true` | Enable volume-provisioner | +| volumeProvisioner.env | object | `{}` | Add additional env vars | +| volumeProvisioner.image | object | `{"digest":"sha256:ede6f663c912a08b7d335b5ec5518ccc266b27c431d0854d22971005992adc5d","registry":"quay.io","repository":"codefresh/dind-volume-provisioner","tag":"1.35.2"}` | Set image | +| volumeProvisioner.nodeSelector | object | `{}` | Set node selector | +| volumeProvisioner.podAnnotations | object | `{}` | Set pod annotations | +| volumeProvisioner.podSecurityContext | object | See below | Set security context for the pod | +| volumeProvisioner.rbac | object | `{"create":true,"rules":[]}` | RBAC parameters | +| volumeProvisioner.rbac.create | bool | `true` | Create RBAC resources | +| volumeProvisioner.rbac.rules | list | `[]` | Add custom rule to the role | +| volumeProvisioner.replicasCount | int | `1` | Set number of pods | +| volumeProvisioner.resources | object | `{}` | Set resources | +| volumeProvisioner.serviceAccount | object | `{"annotations":{},"create":true,"name":""}` | Service Account parameters | +| volumeProvisioner.serviceAccount.annotations | object | `{}` | Additional service account annotations | +| volumeProvisioner.serviceAccount.create | bool | `true` | Create service account | +| volumeProvisioner.serviceAccount.name | string | `""` | Override service account name | +| volumeProvisioner.tolerations | list | `[]` | Set tolerations | +| volumeProvisioner.updateStrategy | object | `{"type":"Recreate"}` | Upgrade strategy | diff --git a/charts/codefresh/cf-runtime/7.5.5/README.md.gotmpl b/charts/codefresh/cf-runtime/7.5.5/README.md.gotmpl new file mode 100644 index 0000000000..2238e5e0ef --- /dev/null +++ b/charts/codefresh/cf-runtime/7.5.5/README.md.gotmpl @@ -0,0 +1,1071 @@ +## Codefresh Runner + +{{ template "chart.versionBadge" . }}{{ template "chart.typeBadge" . }}{{ template "chart.appVersionBadge" . }} + +Helm chart for deploying [Codefresh Runner](https://codefresh.io/docs/docs/installation/codefresh-runner/) to Kubernetes. + +## Table of Content + +- [Prerequisites](#prerequisites) +- [Get Chart Info](#get-chart-info) +- [Install Chart](#install-chart) +- [Chart Configuration](#chart-configuration) +- [Upgrade Chart](#upgrade-chart) + - [⚠️ Known issues](#⚠️-known-issues) + - [To 2.x](#to-2-x) + - [To 3.x](#to-3-x) + - [To 4.x](#to-4-x) + - [To 5.x](#to-5-x) + - [To 6.x](#to-6-x) + - [To 7.x](#to-7-x) +- [Architecture](#architecture) +- [Configuration](#configuration) + - [EBS backend volume configuration in AWS](#ebs-backend-volume-configuration) + - [Azure Disks backend volume configuration in AKS](#azure-disks-backend-volume-configuration) + - [GCE Disks backend volume configuration in GKE](#gce-disks-backend-volume-configuration-in-gke) + - [Custom volume mounts](#custom-volume-mounts) + - [Custom global environment variables](#custom-global-environment-variables) + - [Volume reuse policy](#volume-reuse-policy) + - [Volume cleaners](#volume-cleaners) + - [Rootless DinD](#rootless-dind) + - [ARM](#arm) + - [Openshift](#openshift) + - [On-premise](#on-premise) + +## Prerequisites + +- Kubernetes **1.19+** +- Helm **3.8.0+** + +⚠️⚠️⚠️ +> Since version 6.2.x chart is pushed **only** to OCI registry at `oci://quay.io/codefresh/cf-runtime` + +> Versions prior to 6.2.x are still available in ChartMuseum at `http://chartmuseum.codefresh.io/cf-runtime` + +## Get Chart Info + +```console +helm show all oci://quay.io/codefresh/cf-runtime +``` +See [Use OCI-based registries](https://helm.sh/docs/topics/registries/) + +## Install Chart + +**Important:** only helm3 is supported + +- Specify the following mandatory values + +`values.yaml` +```yaml +# -- Global parameters +# @default -- See below +global: + # -- User token in plain text (required if `global.codefreshTokenSecretKeyRef` is omitted!) + # Ref: https://g.codefresh.io/user/settings (see API Keys) + # Minimal API key scopes: Runner-Installation(read+write), Agent(read+write), Agents(read+write) + codefreshToken: "" + # -- User token that references an existing secret containing API key (required if `global.codefreshToken` is omitted!) + codefreshTokenSecretKeyRef: {} + # E.g. + # codefreshTokenSecretKeyRef: + # name: my-codefresh-api-token + # key: codefresh-api-token + + # -- Account ID (required!) + # Can be obtained here https://g.codefresh.io/2.0/account-settings/account-information + accountId: "" + + # -- K8s context name (required!) + context: "" + # E.g. + # context: prod-ue1-runtime-1 + + # -- Agent Name (optional!) + # If omitted, the following format will be used '{{ `{{ .Values.global.context }}_{{ .Release.Namespace }}` }}' + agentName: "" + # E.g. + # agentName: prod-ue1-runtime-1 + + # -- Runtime name (optional!) + # If omitted, the following format will be used '{{ `{{ .Values.global.context }}/{{ .Release.Namespace }}` }}' + runtimeName: "" + # E.g. + # runtimeName: prod-ue1-runtime-1/namespace +``` + +- Install chart + +```console +helm upgrade --install cf-runtime oci://quay.io/codefresh/cf-runtime -f values.yaml --create-namespace --namespace codefresh +``` + +## Chart Configuration + +See [Customizing the Chart Before Installing](https://helm.sh/docs/intro/using_helm/#customizing-the-chart-before-installing). + +## Upgrade Chart + +### ⚠️ Known issues + +Please check the following known issues before upgrading the chart: + +- Charts **7.1.1–7.4.3** have a bug because of which the following feature does not work: [“Secret Store — Kubernetes-Runtime Secret”](https://codefresh.io/docs/docs/integrations/secret-storage/#secret-store-setup-for-codefresh-runner-installation). + +### To 2.x + +This major release renames and deprecated several values in the chart. Most of the workload templates have been refactored. + +Affected values: +- `dockerRegistry` is deprecated. Replaced with `global.imageRegistry` +- `re` is renamed to `runtime` +- `storage.localVolumeMonitor` is replaced with `volumeProvisioner.dind-lv-monitor` +- `volumeProvisioner.volume-cleanup` is replaced with `volumeProvisioner.dind-volume-cleanup` +- `image` values structure has been updated. Split to `image.registry` `image.repository` `image.tag` +- pod's `annotations` is renamed to `podAnnotations` + +### To 3.x + +⚠️⚠️⚠️ +### READ this before the upgrade! + +This major release adds [runtime-environment](https://codefresh.io/docs/docs/installation/codefresh-runner/#runtime-environment-specification) spec into chart templates. +That means it is possible to set parametes for `dind` and `engine` pods via [values.yaml](./values.yaml). + +**If you had any overrides (i.e. tolerations/nodeSelector/environment variables/etc) added in runtime spec via [codefresh CLI](https://codefresh-io.github.io/cli/) (for example, you did use [get](https://codefresh-io.github.io/cli/runtime-environments/get-runtime-environments/) and [patch](https://codefresh-io.github.io/cli/runtime-environments/apply-runtime-environments/) commands to modify the runtime-environment), you MUST add these into chart's [values.yaml](./values.yaml) for `.Values.runtime.dind` or(and) .`Values.runtime.engine`** + +**For backward compatibility, you can disable updating runtime-environment spec via** `.Values.runtime.patch.enabled=false` + +Affected values: +- added **mandatory** `global.codefreshToken`/`global.codefreshTokenSecretKeyRef` **You must specify it before the upgrade!** +- `runtime.engine` is added +- `runtime.dind` is added +- `global.existingAgentToken` is replaced with `global.agentTokenSecretKeyRef` +- `global.existingDindCertsSecret` is replaced with `global.dindCertsSecretRef` + +### To 4.x + +This major release adds **agentless inCluster** runtime mode (relevant only for [Codefresh On-Premises](#on-premise) users) + +Affected values: +- `runtime.agent` / `runtime.inCluster` / `runtime.accounts` / `runtime.description` are added + +### To 5.x + +This major release converts `.runtime.dind.pvcs` from **list** to **dict** + +> 4.x chart's values example: +```yaml +runtime: + dind: + pvcs: + - name: dind + storageClassName: my-storage-class-name + volumeSize: 32Gi + reuseVolumeSelector: 'codefresh-app,io.codefresh.accountName' + reuseVolumeSortOrder: pipeline_id +``` + +> 5.x chart's values example: +```yaml +runtime: + dind: + pvcs: + dind: + name: dind + storageClassName: my-storage-class-name + volumeSize: 32Gi + reuseVolumeSelector: 'codefresh-app,io.codefresh.accountName' + reuseVolumeSortOrder: pipeline_id +``` + +Affected values: +- `.runtime.dind.pvcs` converted from **list** to **dict** + +### To 6.x + +⚠️⚠️⚠️ +### READ this before the upgrade! + +This major release deprecates previously required `codefresh runner init --generate-helm-values-file`. + +Affected values: +- **Replaced** `.monitor.clusterId` with `.global.context` as **mandatory** value! +- **Deprecated** `.global.agentToken` / `.global.agentTokenSecretKeyRef` +- **Removed** `.global.agentId` +- **Removed** `.global.keys` / `.global.dindCertsSecretRef` +- **Removed** `.global.existingAgentToken` / `existingDindCertsSecret` +- **Removed** `.monitor.clusterId` / `.monitor.token` / `.monitor.existingMonitorToken` + +#### Migrate the Helm chart from version 5.x to 6.x + +Given this is the legacy `generated_values.yaml` values: + +> legacy `generated_values.yaml` +```yaml +{ + "appProxy": { + "enabled": false, + }, + "monitor": { + "enabled": false, + "clusterId": "my-cluster-name", + "token": "1234567890" + }, + "global": { + "namespace": "namespace", + "codefreshHost": "https://g.codefresh.io", + "agentToken": "0987654321", + "agentId": "agent-id-here", + "agentName": "my-cluster-name_my-namespace", + "accountId": "my-account-id", + "runtimeName": "my-cluster-name/my-namespace", + "codefreshToken": "1234567890", + "keys": { + "key": "-----BEGIN RSA PRIVATE KEY-----...", + "csr": "-----BEGIN CERTIFICATE REQUEST-----...", + "ca": "-----BEGIN CERTIFICATE-----...", + "serverCert": "-----BEGIN CERTIFICATE-----..." + } + } +} +``` + +Update `values.yaml` for new chart version: + +> For existing installation for backward compatibility `.Values.global.agentToken/agentTokenSecretKeyRef` **must be provided!** For installation from scratch this value is no longer required. + +> updated `values.yaml` +```yaml +global: + codefreshToken: "1234567890" + accountId: "my-account-id" + context: "my-cluster-name" + agentToken: "0987654321" # MANDATORY when migrating from < 6.x chart version ! + agentName: "my-cluster-name_my-namespace" # optional + runtimeName: "my-cluster-name/my-namespace" # optional +``` + +> **Note!** Though it's still possible to update runtime-environment via [get](https://codefresh-io.github.io/cli/runtime-environments/get-runtime-environments/) and [patch](https://codefresh-io.github.io/cli/runtime-environments/apply-runtime-environments/) commands, it's recommended to enable sidecar container to pull runtime spec from Codefresh API to detect any drift in configuration. + +```yaml +runner: + # -- Sidecar container + # Reconciles runtime spec from Codefresh API for drift detection + sidecar: + enabled: true +``` + +### To 7.x + +⚠️⚠️⚠️ **BREAKING CHANGE** ⚠️⚠️⚠️ + +7.0.0 release adds image digests to all images in default values, for example: + +```yaml +runtime: + engine: + image: + registry: quay.io + repository: codefresh/engine + tag: 1.174.15 + pullPolicy: IfNotPresent + digest: sha256:d547c2044c1488e911ff726462cc417adf2dda731cafd736493c4de4eb9e357b +``` + +Which means any overrides for tags won't be used and underlying Kubernetes runtime will pull the image by the digest. + +See [Pull an image by digest (immutable identifier)](https://docs.docker.com/reference/cli/docker/image/pull/#pull-an-image-by-digest-immutable-identifier) + + + +## Architecture + +[Codefresh Runner architecture](https://codefresh.io/docs/docs/installation/codefresh-runner/#codefresh-runner-architecture) + +## Configuration + +See [Customizing the Chart Before Installing](https://helm.sh/docs/intro/using_helm/#customizing-the-chart-before-installing). + +### EBS backend volume configuration + +`dind-volume-provisioner` should have permissions to create/attach/detach/delete/get EBS volumes + +Minimal IAM policy for `dind-volume-provisioner` + +```json +{ + "Version": "2012-10-17", + "Statement": [ + { + "Effect": "Allow", + "Action": [ + "ec2:AttachVolume", + "ec2:CreateSnapshot", + "ec2:CreateTags", + "ec2:CreateVolume", + "ec2:DeleteSnapshot", + "ec2:DeleteTags", + "ec2:DeleteVolume", + "ec2:DescribeInstances", + "ec2:DescribeSnapshots", + "ec2:DescribeTags", + "ec2:DescribeVolumes", + "ec2:DetachVolume" + ], + "Resource": "*" + } + ] +} +``` + +There are three options: + +1. Run `dind-volume-provisioner` pod on the node/node-group with IAM role + +```yaml +storage: + # -- Set backend volume type (`local`/`ebs`/`ebs-csi`/`gcedisk`/`azuredisk`) + backend: ebs-csi + + ebs: + availabilityZone: "us-east-1a" + +volumeProvisioner: + # -- Set node selector + nodeSelector: {} + # -- Set tolerations + tolerations: [] +``` + +2. Pass static credentials in `.Values.storage.ebs.accessKeyId/accessKeyIdSecretKeyRef` and `.Values.storage.ebs.secretAccessKey/secretAccessKeySecretKeyRef` + +```yaml +storage: + # -- Set backend volume type (`local`/`ebs`/`ebs-csi`/`gcedisk`/`azuredisk`) + backend: ebs-csi + + ebs: + availabilityZone: "us-east-1a" + + # -- Set AWS_ACCESS_KEY_ID for volume-provisioner (optional) + accessKeyId: "" + # -- Existing secret containing AWS_ACCESS_KEY_ID. + accessKeyIdSecretKeyRef: {} + # E.g. + # accessKeyIdSecretKeyRef: + # name: + # key: + + # -- Set AWS_SECRET_ACCESS_KEY for volume-provisioner (optional) + secretAccessKey: "" + # -- Existing secret containing AWS_SECRET_ACCESS_KEY + secretAccessKeySecretKeyRef: {} + # E.g. + # secretAccessKeySecretKeyRef: + # name: + # key: +``` + +3. Assign IAM role to `dind-volume-provisioner` service account + +```yaml +storage: + # -- Set backend volume type (`local`/`ebs`/`ebs-csi`/`gcedisk`/`azuredisk`) + backend: ebs-csi + + ebs: + availabilityZone: "us-east-1a" + +volumeProvisioner: + # -- Service Account parameters + serviceAccount: + # -- Create service account + create: true + # -- Additional service account annotations + annotations: + eks.amazonaws.com/role-arn: "arn:aws:iam:::role/" +``` + +### Custom volume mounts + +You can add your own volumes and volume mounts in the runtime environment, so that all pipeline steps will have access to the same set of external files. + +```yaml +runtime: + dind: + userVolumes: + regctl-docker-registry: + name: regctl-docker-registry + secret: + items: + - key: .dockerconfigjson + path: config.json + secretName: regctl-docker-registry + optional: true + userVolumeMounts: + regctl-docker-registry: + name: regctl-docker-registry + mountPath: /home/appuser/.docker/ + readOnly: true + +``` + +### Azure Disks backend volume configuration + +`dind-volume-provisioner` should have permissions to create/delete/get Azure Disks + +Role definition for `dind-volume-provisioner` + +`dind-volume-provisioner-role.json` +```json +{ + "Name": "CodefreshDindVolumeProvisioner", + "Description": "Perform create/delete/get disks", + "IsCustom": true, + "Actions": [ + "Microsoft.Compute/disks/read", + "Microsoft.Compute/disks/write", + "Microsoft.Compute/disks/delete" + + ], + "AssignableScopes": ["/subscriptions/"] +} +``` + +When creating an AKS cluster in Azure there is the option to use a [managed identity](https://learn.microsoft.com/en-us/azure/aks/use-managed-identity) that is assigned to the kubelet. This identity is assigned to the underlying node pool in the AKS cluster and can then be used by the dind-volume-provisioner. + +```console +export ROLE_DEFINITIN_FILE=dind-volume-provisioner-role.json +export SUBSCRIPTION_ID=$(az account show --query "id" | xargs echo ) +export RESOURCE_GROUP= +export AKS_NAME= +export LOCATION=$(az aks show -g $RESOURCE_GROUP -n $AKS_NAME --query location | xargs echo) +export NODES_RESOURCE_GROUP=MC_${RESOURCE_GROUP}_${AKS_NAME}_${LOCATION} +export NODE_SERVICE_PRINCIPAL=$(az aks show -g $RESOURCE_GROUP -n $AKS_NAME --query identityProfile.kubeletidentity.objectId | xargs echo) + +az role definition create --role-definition @${ROLE_DEFINITIN_FILE} +az role assignment create --assignee $NODE_SERVICE_PRINCIPAL --scope /subscriptions/$SUBSCRIPTION_ID/resourceGroups/$NODES_RESOURCE_GROUP --role CodefreshDindVolumeProvisioner +``` + +Deploy Helm chart with the following values: + +`values.yaml` +```yaml +volumeProvisioner: + podSecurityContext: + enabled: true + runAsUser: 0 + runAsGroup: 0 + fsGroup: 0 + +storage: + backend: azuredisk + azuredisk: + availabilityZone: northeurope-1 # replace with your zone + resourceGroup: my-resource-group-name + + mountAzureJson: true + +runtime: + dind: + nodeSelector: + topology.kubernetes.io/zone: northeurope-1 +``` + +### GCE Disks backend volume configuration in GKE + +`dind-volume-provisioner` should have `ComputeEngine.StorageAdmin` permissions + +There are three options: + +1. Run `dind-volume-provisioner` pod on the node/node-group with IAM Service Account + +```yaml +storage: + # -- Set backend volume type (`local`/`ebs`/`ebs-csi`/`gcedisk`/`azuredisk`) + backend: gcedisk + + gcedisk: + # -- Set GCP volume backend type (`pd-ssd`/`pd-standard`) + volumeType: "pd-standard" + # -- Set GCP volume availability zone + availabilityZone: "us-central1-c" + +volumeProvisioner: + # -- Set node selector + nodeSelector: {} + # -- Set tolerations + tolerations: [] + +# -- Set runtime parameters +runtime: + # -- Parameters for DinD (docker-in-docker) pod + dind: + # -- Set node selector. + nodeSelector: + topology.kubernetes.io/zone: us-central1-c +``` + +2. Pass static credentials in `.Values.storage.gcedisk.serviceAccountJson` (inline) or `.Values.storage.gcedisk.serviceAccountJsonSecretKeyRef` (from your own secret) + +```yaml +storage: + # -- Set backend volume type (`local`/`ebs`/`ebs-csi`/`gcedisk`/`azuredisk`) + backend: gcedisk + + gcedisk: + # -- Set GCP volume backend type (`pd-ssd`/`pd-standard`) + volumeType: "`pd-standard" + # -- Set GCP volume availability zone + availabilityZone: "us-central1-c" + # -- Set Google SA JSON key for volume-provisioner (optional) + serviceAccountJson: | + { + "type": "service_account", + "project_id": "...", + "private_key_id": "...", + "private_key": "...", + "client_email": "...", + "client_id": "...", + "auth_uri": "...", + "token_uri": "...", + "auth_provider_x509_cert_url": "...", + "client_x509_cert_url": "..." + } + # -- Existing secret containing containing Google SA JSON key for volume-provisioner (optional) + serviceAccountJsonSecretKeyRef: {} + # E.g.: + # serviceAccountJsonSecretKeyRef: + # name: gce-service-account + # key: service-account.json + +# -- Set runtime parameters +runtime: + # -- Parameters for DinD (docker-in-docker) pod + dind: + # -- Set node selector. + nodeSelector: + topology.kubernetes.io/zone: us-central1-c +``` + +3. Assign IAM role to `dind-volume-provisioner` service account + +```yaml +storage: + # -- Set backend volume type (`local`/`ebs`/`ebs-csi`/`gcedisk`/`azuredisk`) + backend: gcedisk + + gcedisk: + # -- Set GCP volume backend type (`pd-ssd`/`pd-standard`) + volumeType: "`pd-standard" + # -- Set GCP volume availability zone + availabilityZone: "us-central1-c" + +volumeProvisioner: + # -- Service Account parameters + serviceAccount: + # -- Create service account + create: true + # -- Additional service account annotations + annotations: + iam.gke.io/gcp-service-account: @.iam.gserviceaccount.com + +# -- Set runtime parameters +runtime: + # -- Parameters for DinD (docker-in-docker) pod + dind: + # -- Set node selector. + nodeSelector: + topology.kubernetes.io/zone: us-central1-c +``` + +### Custom global environment variables + +You can add your own environment variables to the runtime environment. All pipeline steps have access to the global variables. + +```yaml +runtime: + engine: + userEnvVars: + - name: GITHUB_TOKEN + valueFrom: + secretKeyRef: + name: github-token + key: token +``` + +### Volume reuse policy + +Volume reuse behavior depends on the configuration for `reuseVolumeSelector` in the runtime environment spec. + +```yaml +runtime: + dind: + pvcs: + - name: dind + ... + reuseVolumeSelector: 'codefresh-app,io.codefresh.accountName' + reuseVolumeSortOrder: pipeline_id +``` + +The following options are available: +- `reuseVolumeSelector: 'codefresh-app,io.codefresh.accountName'` - PV can be used by ANY pipeline in the specified account (default). +Benefit: Fewer PVs, resulting in lower costs. Since any PV can be used by any pipeline, the cluster needs to maintain/reserve fewer PVs in its PV pool for Codefresh. +Downside: Since the PV can be used by any pipeline, the PVs could have assets and info from different pipelines, reducing the probability of cache. + +- `reuseVolumeSelector: 'codefresh-app,io.codefresh.accountName,project_id'` - PV can be used by ALL pipelines in your account, assigned to the same project. + +- `reuseVolumeSelector: 'codefresh-app,io.codefresh.accountName,pipeline_id'` - PV can be used only by a single pipeline. +Benefit: More probability of cache without “spam” from other pipelines. +Downside: More PVs to maintain and therefore higher costs. + +- `reuseVolumeSelector: 'codefresh-app,io.codefresh.accountName,pipeline_id,io.codefresh.branch_name'` - PV can be used only by single pipeline AND single branch. + +- `reuseVolumeSelector: 'codefresh-app,io.codefresh.accountName,pipeline_id,trigger'` - PV can be used only by single pipeline AND single trigger. + +### Volume cleaners + +Codefresh pipelines require disk space for: + * [Pipeline Shared Volume](https://codefresh.io/docs/docs/pipelines/introduction-to-codefresh-pipelines/#sharing-the-workspace-between-build-steps) (`/codefresh/volume`, implemented as [docker volume](https://docs.docker.com/storage/volumes/)) + * Docker containers, both running and stopped + * Docker images and cached layers + +Codefresh offers two options to manage disk space and prevent out-of-space errors: +* Use runtime cleaners on Docker images and volumes +* [Set the minimum disk space per pipeline build volume](https://codefresh.io/docs/docs/pipelines/pipelines/#set-minimum-disk-space-for-a-pipeline-build) + +To improve performance by using Docker cache, Codefresh `volume-provisioner` can provision previously used disks with Docker images and pipeline volumes from previously run builds. + +### Types of runtime volume cleaners + +Docker images and volumes must be cleaned on a regular basis. + +* [IN-DIND cleaner](https://github.com/codefresh-io/dind/tree/master/cleaner): Deletes extra Docker containers, volumes, and images in **DIND pod**. +* [External volume cleaner](https://github.com/codefresh-io/dind-volume-cleanup): Deletes unused **external** PVs (EBS, GCE/Azure disks). +* [Local volume cleaner](https://github.com/codefresh-io/dind-volume-utils/blob/master/local-volumes/lv-cleaner.sh): Deletes **local** volumes if node disk space is close to the threshold. + +### IN-DIND cleaner + +**Purpose:** Removes unneeded *docker containers, images, volumes* inside Kubernetes volume mounted on the DIND pod + +**How it runs:** Inside each DIND pod as script + +**Triggered by:** SIGTERM and also during the run when disk usage > 90% (configurable) + +**Configured by:** Environment Variables which can be set in Runtime Environment spec + +**Configuration/Logic:** [README.md](https://github.com/codefresh-io/dind/tree/master/cleaner#readme) + +Override `.Values.runtime.dind.env` if necessary (the following are **defaults**): + +```yaml +runtime: + dind: + env: + CLEAN_PERIOD_SECONDS: '21600' # launch clean if last clean was more than CLEAN_PERIOD_SECONDS seconds ago + CLEAN_PERIOD_BUILDS: '5' # launch clean if last clean was more CLEAN_PERIOD_BUILDS builds since last build + IMAGE_RETAIN_PERIOD: '14400' # do not delete docker images if they have events since current_timestamp - IMAGE_RETAIN_PERIOD + VOLUMES_RETAIN_PERIOD: '14400' # do not delete docker volumes if they have events since current_timestamp - VOLUMES_RETAIN_PERIOD + DISK_USAGE_THRESHOLD: '0.8' # launch clean based on current disk usage DISK_USAGE_THRESHOLD + INODES_USAGE_THRESHOLD: '0.8' # launch clean based on current inodes usage INODES_USAGE_THRESHOLD +``` + +### External volumes cleaner + +**Purpose:** Removes unused *kubernetes volumes and related backend volumes* + +**How it runs:** Runs as `dind-volume-cleanup` CronJob. Installed in case the Runner uses non-local volumes `.Values.storage.backend != local` + +**Triggered by:** CronJob every 10min (configurable) + +**Configuration:** + +Set `codefresh.io/volume-retention` for dinds' PVCs: + +```yaml +runtime: + dind: + pvcs: + dind: + ... + annotations: + codefresh.io/volume-retention: 7d +``` + +Or override environment variables for `dind-volume-cleanup` cronjob: + +```yaml +volumeProvisioner: + dind-volume-cleanup: + env: + RETENTION_DAYS: 7 # clean volumes that were last used more than `RETENTION_DAYS` (default is 4) ago +``` + +### Local volumes cleaner + +**Purpose:** Deletes local volumes when node disk space is close to the threshold + +**How it runs:** Runs as `dind-lv-monitor` DaemonSet. Installed in case the Runner uses local volumes `.Values.storage.backend == local` + +**Triggered by:** Disk space usage or inode usage that exceeds thresholds (configurable) + +**Configuration:** + +Override environment variables for `dind-lv-monitor` daemonset: + +```yaml +volumeProvisioner: + dind-lv-monitor: + env: + KB_USAGE_THRESHOLD: 60 # default 80 (percentage) + INODE_USAGE_THRESHOLD: 60 # default 80 +``` + +### Rootless DinD + +DinD pod runs a `priviliged` container with **rootfull** docker. + +To run the docker daemon as non-root user (**rootless** mode), refer to `values-rootless.yaml`: + +```yaml +volumeProvisioner: + env: + IS_ROOTLESS: true + # -- Only if local volumes are used as backend storage (ignored for ebs/ebs-csi disks) + dind-lv-monitor: + image: + tag: 1.30.0-rootless + digest: sha256:712e549e6e843b04684647f17e0973f8047e0d60e6e8b38a693ea64dc75b0479 + containerSecurityContext: + runAsUser: 1000 + podSecurityContext: + fsGroup: 1000 + # Ref: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/#configure-volume-permission-and-ownership-change-policy-for-pods + fsGroupChangePolicy: "OnRootMismatch" + # -- Enable initContainer to run chmod for /var/lib/codefresh/dind-volumes on host nodes + volumePermissions: + enabled: false + +runtime: + dind: + image: + tag: 26.1.4-1.28.10-rootless + digest: sha256:59dfc004eb22a8f09c8a3d585271a055af9df4591ab815bca418c24a2077f5c8 + userVolumeMounts: + dind: + name: dind + mountPath: /home/rootless/.local/share/docker + containerSecurityContext: + privileged: true + runAsUser: 1000 + podSecurityContext: + fsGroup: 1000 + # Ref: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/#configure-volume-permission-and-ownership-change-policy-for-pods + fsGroupChangePolicy: "OnRootMismatch" + # -- Enable initContainer to run chmod for /home/rootless in DinD pod + # !!! Will slow down dind pod startup + volumePermissions: + enabled: true +``` + +### ARM + +With the Codefresh Runner, you can run native ARM64v8 builds. + +> **Note!** +> You cannot run both amd64 and arm64 images within the same pipeline. As one pipeline can map only to one runtime, you can run either amd64 or arm64 within the same pipeline. + +Provide `nodeSelector` and(or) `tolerations` for dind pods: + +`values.yaml` +```yaml +runtime: + dind: + nodeSelector: + arch: arm64 + tolerations: + - key: arch + operator: Equal + value: arm64 + effect: NoSchedule +``` + +### Openshift + +To install Codefresh Runner on OpenShift use the following `values.yaml` example + +```yaml +runner: + podSecurityContext: + enabled: false + +volumeProvisioner: + podSecurityContext: + enabled: false + env: + PRIVILEGED_CONTAINER: true + dind-lv-monitor: + containerSecurityContext: + enabled: true + privileged: true + volumePermissions: + enabled: true + securityContext: + privileged: true + runAsUser: auto +``` + +Grant `privileged` SCC to `cf-runtime-runner` and `cf-runtime-volume-provisioner` service accounts. + +```console +oc adm policy add-scc-to-user privileged system:serviceaccount:codefresh:cf-runtime-runner + +oc adm policy add-scc-to-user privileged system:serviceaccount:codefresh:cf-runtime-volume-provisioner +``` + +### On-premise + +If you have [Codefresh On-Premises](https://artifacthub.io/packages/helm/codefresh-onprem/codefresh) deployed, you can install Codefresh Runner in **agentless** mode. + +**What is agentless mode?** + +Agent (aka venona) is Runner component which responsible for calling Codefresh API to run builds and create dind/engine pods and pvc objects. Agent can only be assigned to a single account, thus you can't share one runtime across multiple accounts. However, with **agentless** mode it's possible to register the runtime as **system**-type runtime so it's registered on the platform level and can be assigned/shared across multiple accounts. + +**What are the prerequisites?** +- You have a running [Codefresh On-Premises](https://artifacthub.io/packages/helm/codefresh-onprem/codefresh) control-plane environment +- You have a Codefresh API token with platform **Admin** permissions scope + + +### How to deploy agentless runtime when it's on the SAME k8s cluster as On-Premises control-plane environment? + +- Enable cluster-level permissions for cf-api (On-Premises control-plane component) + +> `values.yaml` for [Codefresh On-Premises](https://artifacthub.io/packages/helm/codefresh-onprem/codefresh) Helm chart +```yaml +cfapi: + ... + # -- Enable ClusterRole/ClusterRoleBinding + rbac: + namespaced: false +``` + +- Set the following values for Runner Helm chart + +`.Values.global.codefreshHost=...` \ +`.Values.global.codefreshToken=...` \ +`.Values.global.runtimeName=system/...` \ +`.Values.runtime.agent=false` \ +`.Values.runtime.inCluster=true` + +> `values.yaml` for [Codefresh Runner](https://artifacthub.io/packages/helm/codefresh-runner/cf-runtime) helm chart +```yaml +global: + # -- URL of Codefresh On-Premises Platform + codefreshHost: "https://myonprem.somedomain.com" + # -- User token in plain text with Admin permission scope + codefreshToken: "" + # -- User token that references an existing secret containing API key. + codefreshTokenSecretKeyRef: {} + # E.g. + # codefreshTokenSecretKeyRef: + # name: my-codefresh-api-token + # key: codefresh-api-token + + # -- Distinguished runtime name + # (for On-Premise only; mandatory!) Must be prefixed with "system/..." + runtimeName: "system/prod-ue1-some-cluster-name" + +# -- Set runtime parameters +runtime: + # -- (for On-Premise only; mandatory!) Disable agent + agent: false + # -- (for On-Premise only; optional) Set inCluster runtime (default: `true`) + # `inCluster=true` flag is set when Runtime and On-Premises control-plane are run on the same cluster + # `inCluster=false` flag is set when Runtime and On-Premises control-plane are on different clusters + inCluster: true + # -- (for On-Premise only; optional) Assign accounts to runtime (list of account ids; default is empty) + # Accounts can be assigned to the runtime in Codefresh UI later so you can kepp it empty. + accounts: [] + # -- Set parent runtime to inherit. + runtimeExtends: [] +``` + +- Install the chart + +```console +helm upgrade --install cf-runtime oci://quay.io/codefresh/cf-runtime -f values.yaml --create-namespace --namespace cf-runtime +``` + +- Verify the runtime and run test pipeline + +Go to [https:///admin/runtime-environments/system](https:///admin/runtime-environments/system) to check the runtime. Assign it to the required account(s). Run test pipeline on it. + + +### How to deploy agentless runtime when it's on the DIFFERENT k8s cluster than On-Premises control-plane environment? + +In this case, it's required to mount runtime cluster's `KUBECONFIG` into On-Premises `cf-api` deployment + +- Create the neccessary RBAC resources + +> `values.yaml` for [Codefresh Runner](https://artifacthub.io/packages/helm/codefresh-runner/cf-runtime) helm chart +```yaml +extraResources: +- apiVersion: rbac.authorization.k8s.io/v1 + kind: Role + metadata: + name: codefresh-role + namespace: '{{ "{{ .Release.Namespace }}" }}' + rules: + - apiGroups: [""] + resources: ["pods", "persistentvolumeclaims", "persistentvolumes"] + verbs: ["list", "watch", "get", "create", "patch", "delete"] + - apiGroups: ["snapshot.storage.k8s.io"] + resources: ["volumesnapshots"] + verbs: ["list", "watch", "get", "create", "patch", "delete"] +- apiVersion: v1 + kind: ServiceAccount + metadata: + name: codefresh-runtime-user + namespace: '{{ "{{ .Release.Namespace }}" }}' +- apiVersion: rbac.authorization.k8s.io/v1 + kind: RoleBinding + metadata: + name: codefresh-runtime-user + namespace: '{{ "{{ .Release.Namespace }}" }}' + roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: codefresh-role + subjects: + - kind: ServiceAccount + name: codefresh-runtime-user + namespace: '{{ "{{ .Release.Namespace }}" }}' +- apiVersion: v1 + kind: Secret + metadata: + name: codefresh-runtime-user-token + namespace: '{{ "{{ .Release.Namespace }}" }}' + annotations: + kubernetes.io/service-account.name: codefresh-runtime-user + type: kubernetes.io/service-account-token +``` + +- Set up the following environment variables to create a `KUBECONFIG` file + +```shell +NAMESPACE=cf-runtime +CLUSTER_NAME=prod-ue1-some-cluster-name +CURRENT_CONTEXT=$(kubectl config current-context) + +USER_TOKEN_VALUE=$(kubectl -n cf-runtime get secret/codefresh-runtime-user-token -o=go-template='{{ `{{.data.token}}` }}' | base64 --decode) +CURRENT_CLUSTER=$(kubectl config view --raw -o=go-template='{{ `{{range .contexts}}{{if eq .name "'''${CURRENT_CONTEXT}'''"}}{{ index .context "cluster" }}{{end}}{{end}}` }}') +CLUSTER_CA=$(kubectl config view --raw -o=go-template='{{ `{{range .clusters}}{{if eq .name "'''${CURRENT_CLUSTER}'''"}}"{{with index .cluster "certificate-authority-data" }}{{.}}{{end}}"{{ end }}{{ end }}` }}') +CLUSTER_SERVER=$(kubectl config view --raw -o=go-template='{{ `{{range .clusters}}{{if eq .name "'''${CURRENT_CLUSTER}'''"}}{{ .cluster.server }}{{end}}{{ end }}` }}') + +export -p USER_TOKEN_VALUE CURRENT_CONTEXT CURRENT_CLUSTER CLUSTER_CA CLUSTER_SERVER CLUSTER_NAME +``` + +- Create a kubeconfig file + +```console +cat << EOF > $CLUSTER_NAME-kubeconfig +apiVersion: v1 +kind: Config +current-context: ${CLUSTER_NAME} +contexts: +- name: ${CLUSTER_NAME} + context: + cluster: ${CLUSTER_NAME} + user: codefresh-runtime-user + namespace: ${NAMESPACE} +clusters: +- name: ${CLUSTER_NAME} + cluster: + certificate-authority-data: ${CLUSTER_CA} + server: ${CLUSTER_SERVER} +users: +- name: ${CLUSTER_NAME} + user: + token: ${USER_TOKEN_VALUE} +EOF +``` + +- **Switch context to On-Premises control-plane cluster**. Create k8s secret (via any tool like [ESO](https://external-secrets.io/v0.4.4/), `kubectl`, etc ) containing runtime cluster's `KUBECONFG` created in previous step. + +```shell +NAMESPACE=codefresh +kubectl create secret generic dind-runtime-clusters --from-file=$CLUSTER_NAME=$CLUSTER_NAME-kubeconfig -n $NAMESPACE +``` + +- Mount secret containing runtime cluster's `KUBECONFG` into cf-api in On-Premises control-plane cluster + +> `values.yaml` for [Codefresh On-Premises](https://artifacthub.io/packages/helm/codefresh-onprem/codefresh) helm chart +```yaml +cf-api: + ... + volumes: + dind-clusters: + enabled: true + type: secret + nameOverride: dind-runtime-clusters + optional: true +``` +> volumeMount `/etc/kubeconfig` is already configured in cf-api Helm chart template. No need to specify it. + +- Set the following values for Runner helm chart + +> `values.yaml` for [Codefresh Runner](https://artifacthub.io/packages/helm/codefresh-runner/cf-runtime) helm chart + +`.Values.global.codefreshHost=...` \ +`.Values.global.codefreshToken=...` \ +`.Values.global.runtimeName=system/...` \ +`.Values.runtime.agent=false` \ +`.Values.runtime.inCluster=false` + +**Important!** +`.Values.global.name` ("system/" prefix is ignored!) should match the cluster name (key in `dind-runtime-clusters` secret created previously) +```yaml +global: + # -- URL of Codefresh On-Premises Platform + codefreshHost: "https://myonprem.somedomain.com" + # -- User token in plain text with Admin permission scope + codefreshToken: "" + # -- User token that references an existing secret containing API key. + codefreshTokenSecretKeyRef: {} + # E.g. + # codefreshTokenSecretKeyRef: + # name: my-codefresh-api-token + # key: codefresh-api-token + + # -- Distinguished runtime name + # (for On-Premise only; mandatory!) Must be prefixed with "system/..." + name: "system/prod-ue1-some-cluster-name" + +# -- Set runtime parameters +runtime: + # -- (for On-Premise only; mandatory!) Disable agent + agent: false + # -- (for On-Premise only; optional) Set inCluster runtime (default: `true`) + # `inCluster=true` flag is set when Runtime and On-Premises control-plane are run on the same cluster + # `inCluster=false` flag is set when Runtime and On-Premises control-plane are on different clusters + inCluster: false + # -- (for On-Premise only; optional) Assign accounts to runtime (list of account ids; default is empty) + # Accounts can be assigned to the runtime in Codefresh UI later so you can kepp it empty. + accounts: [] + # -- (optional) Set parent runtime to inherit. + runtimeExtends: [] +``` + +- Install the chart + +```console +helm upgrade --install cf-runtime oci://quay.io/codefresh/cf-runtime -f values.yaml --create-namespace --namespace cf-runtime +``` + +- Verify the runtime and run test pipeline + +Go to [https:///admin/runtime-environments/system](https:///admin/runtime-environments/system) to see the runtime. Assign it to the required account(s). + +{{ template "chart.requirementsSection" . }} + +{{ template "chart.valuesSection" . }} diff --git a/charts/codefresh/cf-runtime/7.5.5/files/cleanup-runtime.sh b/charts/codefresh/cf-runtime/7.5.5/files/cleanup-runtime.sh new file mode 100644 index 0000000000..c1fc5f3682 --- /dev/null +++ b/charts/codefresh/cf-runtime/7.5.5/files/cleanup-runtime.sh @@ -0,0 +1,37 @@ +#!/bin/bash + +echo "-----" +echo "API_HOST: ${API_HOST}" +echo "AGENT_NAME: ${AGENT_NAME}" +echo "RUNTIME_NAME: ${RUNTIME_NAME}" +echo "AGENT: ${AGENT}" +echo "AGENT_SECRET_NAME: ${AGENT_SECRET_NAME}" +echo "DIND_SECRET_NAME: ${DIND_SECRET_NAME}" +echo "-----" + +auth() { + codefresh auth create-context --api-key ${API_TOKEN} --url ${API_HOST} +} + +remove_runtime() { + if [ "$AGENT" == "true" ]; then + codefresh delete re ${RUNTIME_NAME} || true + else + codefresh delete sys-re ${RUNTIME_NAME} || true + fi +} + +remove_agent() { + codefresh delete agent ${AGENT_NAME} || true +} + +remove_secrets() { + kubectl patch secret $(kubectl get secret -l codefresh.io/internal=true | awk 'NR>1{print $1}' | xargs) -p '{"metadata":{"finalizers":null}}' --type=merge || true + kubectl delete secret $AGENT_SECRET_NAME || true + kubectl delete secret $DIND_SECRET_NAME || true +} + +auth +remove_runtime +remove_agent +remove_secrets \ No newline at end of file diff --git a/charts/codefresh/cf-runtime/7.5.5/files/configure-dind-certs.sh b/charts/codefresh/cf-runtime/7.5.5/files/configure-dind-certs.sh new file mode 100644 index 0000000000..a1092eb1e6 --- /dev/null +++ b/charts/codefresh/cf-runtime/7.5.5/files/configure-dind-certs.sh @@ -0,0 +1,132 @@ +#!/usr/bin/env bash +# + +#--- +fatal() { + echo "ERROR: $1" + exit 1 +} + +msg() { echo -e "\e[32mINFO ---> $1\e[0m"; } +err() { echo -e "\e[31mERR ---> $1\e[0m" ; return 1; } + +exit_trap () { + local lc="$BASH_COMMAND" rc=$? + if [ $rc != 0 ]; then + if [[ -n "$SLEEP_ON_ERROR" ]]; then + echo -e "\nSLEEP_ON_ERROR is set - Sleeping to fix error" + sleep $SLEEP_ON_ERROR + fi + fi +} +trap exit_trap EXIT + +usage() { + echo "Usage: + $0 [-n | --namespace] [--server-cert-cn] [--server-cert-extra-sans] codefresh-api-host codefresh-api-token + +Example: + $0 -n workflow https://g.codefresh.io 21341234.423141234.412431234 + +" +} + +# Args +while [[ $1 =~ ^(-(n|h)|--(namespace|server-cert-cn|server-cert-extra-sans|help)) ]] +do + key=$1 + value=$2 + + case $key in + -h|--help) + usage + exit + ;; + -n|--namespace) + NAMESPACE="$value" + shift + ;; + --server-cert-cn) + SERVER_CERT_CN="$value" + shift + ;; + --server-cert-extra-sans) + SERVER_CERT_EXTRA_SANS="$value" + shift + ;; + esac + shift # past argument or value +done + +API_HOST=${1:-"$CF_API_HOST"} +API_TOKEN=${2:-"$CF_API_TOKEN"} + +[[ -z "$API_HOST" ]] && usage && fatal "Missing API_HOST" +[[ -z "$API_TOKEN" ]] && usage && fatal "Missing token" + + +API_SIGN_PATH=${API_SIGN_PATH:-"api/custom_clusters/signServerCerts"} + +NAMESPACE=${NAMESPACE:-default} +RELEASE=${RELEASE:-cf-runtime} + +DIR=$(dirname $0) +TMPDIR=/tmp/codefresh/ + +TMP_CERTS_FILE_ZIP=$TMPDIR/cf-certs.zip +TMP_CERTS_HEADERS_FILE=$TMPDIR/cf-certs-response-headers.txt +CERTS_DIR=$TMPDIR/ssl +SRV_TLS_CA_CERT=${CERTS_DIR}/ca.pem +SRV_TLS_KEY=${CERTS_DIR}/server-key.pem +SRV_TLS_CSR=${CERTS_DIR}/server-cert.csr +SRV_TLS_CERT=${CERTS_DIR}/server-cert.pem +CF_SRV_TLS_CERT=${CERTS_DIR}/cf-server-cert.pem +CF_SRV_TLS_CA_CERT=${CERTS_DIR}/cf-ca.pem +mkdir -p $TMPDIR $CERTS_DIR + +K8S_CERT_SECRET_NAME=codefresh-certs-server +echo -e "\n------------------\nGenerating server tls certificates ... " + +SERVER_CERT_CN=${SERVER_CERT_CN:-"docker.codefresh.io"} +SERVER_CERT_EXTRA_SANS="${SERVER_CERT_EXTRA_SANS}" +### + + openssl genrsa -out $SRV_TLS_KEY 4096 || fatal "Failed to generate openssl key " + openssl req -subj "/CN=${SERVER_CERT_CN}" -new -key $SRV_TLS_KEY -out $SRV_TLS_CSR || fatal "Failed to generate openssl csr " + GENERATE_CERTS=true + CSR=$(sed ':a;N;$!ba;s/\n/\\n/g' ${SRV_TLS_CSR}) + + SERVER_CERT_SANS="IP:127.0.0.1,DNS:dind,DNS:*.dind.${NAMESPACE},DNS:*.dind.${NAMESPACE}.svc${KUBE_DOMAIN},DNS:*.cf-cd.com,DNS:*.codefresh.io" + if [[ -n "${SERVER_CERT_EXTRA_SANS}" ]]; then + SERVER_CERT_SANS=${SERVER_CERT_SANS},${SERVER_CERT_EXTRA_SANS} + fi + echo "{\"reqSubjectAltName\": \"${SERVER_CERT_SANS}\", \"csr\": \"${CSR}\" }" > ${TMPDIR}/sign_req.json + + rm -fv ${TMP_CERTS_HEADERS_FILE} ${TMP_CERTS_FILE_ZIP} + + SIGN_STATUS=$(curl -k -sSL -d @${TMPDIR}/sign_req.json -H "Content-Type: application/json" -H "Authorization: ${API_TOKEN}" -H "Expect: " \ + -o ${TMP_CERTS_FILE_ZIP} -D ${TMP_CERTS_HEADERS_FILE} -w '%{http_code}' ${API_HOST}/${API_SIGN_PATH} ) + + echo "Sign request completed with HTTP_STATUS_CODE=$SIGN_STATUS" + if [[ $SIGN_STATUS != 200 ]]; then + echo "ERROR: Cannot sign certificates" + if [[ -f ${TMP_CERTS_FILE_ZIP} ]]; then + mv ${TMP_CERTS_FILE_ZIP} ${TMP_CERTS_FILE_ZIP}.error + cat ${TMP_CERTS_FILE_ZIP}.error + fi + exit 1 + fi + unzip -o -d ${CERTS_DIR}/ ${TMP_CERTS_FILE_ZIP} || fatal "Failed to unzip certificates to ${CERTS_DIR} " + cp -v ${CF_SRV_TLS_CA_CERT} $SRV_TLS_CA_CERT || fatal "received ${TMP_CERTS_FILE_ZIP} does not contains ca.pem" + cp -v ${CF_SRV_TLS_CERT} $SRV_TLS_CERT || fatal "received ${TMP_CERTS_FILE_ZIP} does not contains cf-server-cert.pem" + + +echo -e "\n------------------\nCreating certificate secret " + +kubectl -n $NAMESPACE create secret generic $K8S_CERT_SECRET_NAME \ + --from-file=$SRV_TLS_CA_CERT \ + --from-file=$SRV_TLS_KEY \ + --from-file=$SRV_TLS_CERT \ + --dry-run=client -o yaml | kubectl apply --overwrite -f - +kubectl -n $NAMESPACE label --overwrite secret ${K8S_CERT_SECRET_NAME} codefresh.io/internal=true +kubectl -n $NAMESPACE patch secret $K8S_CERT_SECRET_NAME -p '{"metadata": {"finalizers": ["kubernetes"]}}' diff --git a/charts/codefresh/cf-runtime/7.5.5/files/init-runtime.sh b/charts/codefresh/cf-runtime/7.5.5/files/init-runtime.sh new file mode 100644 index 0000000000..eb3488af11 --- /dev/null +++ b/charts/codefresh/cf-runtime/7.5.5/files/init-runtime.sh @@ -0,0 +1,80 @@ +#!/bin/bash + +echo "-----" +echo "API_HOST: ${API_HOST}" +echo "AGENT_NAME: ${AGENT_NAME}" +echo "KUBE_CONTEXT: ${KUBE_CONTEXT}" +echo "KUBE_NAMESPACE: ${KUBE_NAMESPACE}" +echo "OWNER_NAME: ${OWNER_NAME}" +echo "RUNTIME_NAME: ${RUNTIME_NAME}" +echo "SECRET_NAME: ${SECRET_NAME}" +echo "-----" + +create_agent_secret() { + + kubectl apply -f - < $1\e[0m"; } +err() { echo -e "\e[31mERR ---> $1\e[0m" ; return 1; } + + +if [ -z "${USER_CODEFRESH_TOKEN}" ]; then + err "missing codefresh user token. must supply \".global.codefreshToken\" if agent-codefresh-token does not exist" + exit 1 +fi + +codefresh auth create-context --api-key ${USER_CODEFRESH_TOKEN} --url ${API_HOST} + +while true; do + msg "Reconciling ${RUNTIME_NAME} runtime" + + sleep $RECONCILE_INTERVAL + + codefresh get re \ + --name ${RUNTIME_NAME} \ + -o yaml \ + | yq 'del(.version, .metadata.changedBy, .metadata.creationTime)' > /tmp/runtime.yaml + + kubectl get cm ${CONFIGMAP_NAME} -n ${KUBE_NAMESPACE} -o yaml \ + | yq 'del(.metadata.resourceVersion, .metadata.uid)' \ + | yq eval '.data["runtime.yaml"] = load_str("/tmp/runtime.yaml")' \ + | kubectl apply -f - +done diff --git a/charts/codefresh/cf-runtime/7.5.5/templates/_components/app-proxy/_deployment.yaml b/charts/codefresh/cf-runtime/7.5.5/templates/_components/app-proxy/_deployment.yaml new file mode 100644 index 0000000000..26f3576b77 --- /dev/null +++ b/charts/codefresh/cf-runtime/7.5.5/templates/_components/app-proxy/_deployment.yaml @@ -0,0 +1,70 @@ +{{- define "app-proxy.resources.deployment" -}} +{{ $cfCommonTplSemver := printf "cf-common-%s" (index .Subcharts "cf-common").Chart.Version }} +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ include "app-proxy.fullname" . }} + labels: + {{- include "app-proxy.labels" . | nindent 4 }} +spec: + replicas: {{ .Values.replicasCount }} + strategy: + type: {{ .Values.updateStrategy.type }} + selector: + matchLabels: + {{- include "app-proxy.selectorLabels" . | nindent 6 }} + template: + metadata: + labels: + {{- include "app-proxy.selectorLabels" . | nindent 8 }} + {{- with .Values.podAnnotations }} + annotations: + {{- toYaml . | nindent 8 }} + {{- end }} + spec: + {{- include (printf "%s.image.pullSecrets" $cfCommonTplSemver ) . | nindent 8 }} + serviceAccountName: {{ include "app-proxy.serviceAccountName" . }} + {{- if .Values.podSecurityContext.enabled }} + securityContext: {{- omit .Values.podSecurityContext "enabled" | toYaml | nindent 8 }} + {{- end }} + containers: + - name: app-proxy + image: {{ include (printf "%s.image.name" $cfCommonTplSemver ) (dict "image" .Values.image "context" .) }} + imagePullPolicy: {{ .Values.image.pullPolicy | default "Always" }} + env: + {{- include "app-proxy.environment-variables" . | nindent 8 }} + ports: + - name: http + containerPort: 3000 + readinessProbe: + initialDelaySeconds: {{ .Values.readinessProbe.initialDelaySeconds }} + periodSeconds: {{ .Values.readinessProbe.periodSeconds }} + timeoutSeconds: {{ .Values.readinessProbe.timeoutSeconds }} + successThreshold: {{ .Values.readinessProbe.successThreshold }} + failureThreshold: {{ .Values.readinessProbe.failureThreshold }} + httpGet: + path: /health + port: http + resources: + {{- toYaml .Values.resources | nindent 12 }} + volumeMounts: + {{- with .Values.extraVolumeMounts }} + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.nodeSelector }} + nodeSelector: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.affinity }} + affinity: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.tolerations }} + tolerations: + {{- toYaml . | nindent 6 }} + {{- end }} + volumes: + {{- with .Values.extraVolumes }} + {{- toYaml . | nindent 6 }} + {{- end }} +{{- end -}} diff --git a/charts/codefresh/cf-runtime/7.5.5/templates/_components/app-proxy/_env-vars.yaml b/charts/codefresh/cf-runtime/7.5.5/templates/_components/app-proxy/_env-vars.yaml new file mode 100644 index 0000000000..c9b9a0e36a --- /dev/null +++ b/charts/codefresh/cf-runtime/7.5.5/templates/_components/app-proxy/_env-vars.yaml @@ -0,0 +1,19 @@ +{{- define "app-proxy.environment-variables.defaults" }} +PORT: 3000 +{{- end }} + +{{- define "app-proxy.environment-variables.calculated" }} +CODEFRESH_HOST: {{ include "runtime.runtime-environment-spec.codefresh-host" . }} +{{- with .Values.ingress.pathPrefix }} +API_PATH_PREFIX: {{ . | quote }} +{{- end }} +{{- end }} + +{{- define "app-proxy.environment-variables" }} +{{- $cfCommonTplSemver := printf "cf-common-%s" (index .Subcharts "cf-common").Chart.Version }} +{{- $defaults := (include "app-proxy.environment-variables.defaults" . | fromYaml) }} +{{- $calculated := (include "app-proxy.environment-variables.calculated" . | fromYaml) }} +{{- $overrides := .Values.env }} +{{- $mergedValues := mergeOverwrite (merge $defaults $calculated) $overrides }} +{{- include (printf "%s.env-vars" $cfCommonTplSemver) (dict "Values" $mergedValues "context" .) }} +{{- end }} \ No newline at end of file diff --git a/charts/codefresh/cf-runtime/7.5.5/templates/_components/app-proxy/_helpers.tpl b/charts/codefresh/cf-runtime/7.5.5/templates/_components/app-proxy/_helpers.tpl new file mode 100644 index 0000000000..2d4272ca92 --- /dev/null +++ b/charts/codefresh/cf-runtime/7.5.5/templates/_components/app-proxy/_helpers.tpl @@ -0,0 +1,43 @@ +{{/* +Expand the name of the chart. +*/}} +{{- define "app-proxy.name" -}} + {{- printf "%s-%s" (include "cf-runtime.name" .) "app-proxy" | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Create a default fully qualified app name. +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +If release name contains chart name it will be used as a full name. +*/}} +{{- define "app-proxy.fullname" -}} + {{- printf "%s-%s" (include "cf-runtime.fullname" .) "app-proxy" | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Common labels +*/}} +{{- define "app-proxy.labels" -}} +{{ include "cf-runtime.labels" . }} +codefresh.io/application: app-proxy +{{- end }} + +{{/* +Selector labels +*/}} +{{- define "app-proxy.selectorLabels" -}} +{{ include "cf-runtime.selectorLabels" . }} +codefresh.io/application: app-proxy +{{- end }} + + +{{/* +Create the name of the service account to use +*/}} +{{- define "app-proxy.serviceAccountName" -}} +{{- if .Values.serviceAccount.create }} +{{- default (include "app-proxy.fullname" .) .Values.serviceAccount.name }} +{{- else }} +{{- default "default" .Values.serviceAccount.name }} +{{- end }} +{{- end }} diff --git a/charts/codefresh/cf-runtime/7.5.5/templates/_components/app-proxy/_ingress.yaml b/charts/codefresh/cf-runtime/7.5.5/templates/_components/app-proxy/_ingress.yaml new file mode 100644 index 0000000000..4a4b5c159d --- /dev/null +++ b/charts/codefresh/cf-runtime/7.5.5/templates/_components/app-proxy/_ingress.yaml @@ -0,0 +1,32 @@ +{{- define "app-proxy.resources.ingress" -}} +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: {{ include "app-proxy.fullname" . }} + labels: {{- include "app-proxy.labels" . | nindent 4 }} + {{- with .Values.ingress.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + {{- if and .Values.ingress.class (semverCompare ">=1.18-0" .Capabilities.KubeVersion.GitVersion) }} + ingressClassName: {{ .Values.ingress.class }} + {{- end }} + {{- if .Values.ingress.tlsSecret }} + tls: + - hosts: + - {{ .Values.ingress.host }} + secretName: {{ .Values.ingress.tlsSecret }} + {{- end }} + rules: + - host: {{ .Values.ingress.host }} + http: + paths: + - path: {{ .Values.ingress.pathPrefix | default "/" }} + pathType: ImplementationSpecific + backend: + service: + name: {{ include "app-proxy.fullname" . }} + port: + number: 80 +{{- end -}} diff --git a/charts/codefresh/cf-runtime/7.5.5/templates/_components/app-proxy/_rbac.yaml b/charts/codefresh/cf-runtime/7.5.5/templates/_components/app-proxy/_rbac.yaml new file mode 100644 index 0000000000..87bd869ba0 --- /dev/null +++ b/charts/codefresh/cf-runtime/7.5.5/templates/_components/app-proxy/_rbac.yaml @@ -0,0 +1,47 @@ +{{- define "app-proxy.resources.rbac" -}} +{{- if .Values.serviceAccount.create }} +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ include "app-proxy.serviceAccountName" . }} + labels: + {{- include "app-proxy.labels" . | nindent 4 }} + {{- with .Values.serviceAccount.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +{{- end }} +--- +{{- if .Values.rbac.create }} +kind: {{ .Values.rbac.namespaced | ternary "Role" "ClusterRole" }} +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: {{ include "app-proxy.fullname" . }} + labels: + {{- include "app-proxy.labels" . | nindent 4 }} +rules: + - apiGroups: [ "" ] + resources: [ "secrets" ] + verbs: [ "get" ] +{{- with .Values.rbac.rules }} + {{ toYaml . | nindent 2 }} +{{- end }} +{{- end }} +--- +{{- if and .Values.serviceAccount.create .Values.rbac.create }} +kind: {{ .Values.rbac.namespaced | ternary "RoleBinding" "ClusterRoleBinding" }} +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: {{ include "app-proxy.fullname" . }} + labels: + {{- include "app-proxy.labels" . | nindent 4 }} +subjects: + - kind: ServiceAccount + name: {{ include "app-proxy.serviceAccountName" . }} + namespace: {{ .Release.Namespace }} +roleRef: + kind: Role + name: {{ include "app-proxy.fullname" . }} + apiGroup: rbac.authorization.k8s.io +{{- end }} +{{- end -}} diff --git a/charts/codefresh/cf-runtime/7.5.5/templates/_components/app-proxy/_service.yaml b/charts/codefresh/cf-runtime/7.5.5/templates/_components/app-proxy/_service.yaml new file mode 100644 index 0000000000..4c3a93bf27 --- /dev/null +++ b/charts/codefresh/cf-runtime/7.5.5/templates/_components/app-proxy/_service.yaml @@ -0,0 +1,17 @@ +{{- define "app-proxy.resources.service" -}} +apiVersion: v1 +kind: Service +metadata: + name: {{ include "app-proxy.fullname" . }} + labels: + {{- include "app-proxy.labels" . | nindent 4 }} +spec: + type: ClusterIP + ports: + - name: http + port: 80 + protocol: TCP + targetPort: 3000 + selector: + {{- include "app-proxy.selectorLabels" . | nindent 4 }} +{{- end -}} \ No newline at end of file diff --git a/charts/codefresh/cf-runtime/7.5.5/templates/_components/event-exporter/_deployment.yaml b/charts/codefresh/cf-runtime/7.5.5/templates/_components/event-exporter/_deployment.yaml new file mode 100644 index 0000000000..62588b4d3d --- /dev/null +++ b/charts/codefresh/cf-runtime/7.5.5/templates/_components/event-exporter/_deployment.yaml @@ -0,0 +1,62 @@ +{{- define "event-exporter.resources.deployment" -}} +{{ $cfCommonTplSemver := printf "cf-common-%s" (index .Subcharts "cf-common").Chart.Version }} +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ include "event-exporter.fullname" . }} + labels: + {{- include "event-exporter.labels" . | nindent 4 }} +spec: + replicas: {{ .Values.replicasCount }} + strategy: + type: {{ .Values.updateStrategy.type }} + selector: + matchLabels: + {{- include "event-exporter.selectorLabels" . | nindent 6 }} + template: + metadata: + labels: + {{- include "event-exporter.selectorLabels" . | nindent 8 }} + {{- with .Values.podAnnotations }} + annotations: + {{- toYaml . | nindent 8 }} + {{- end }} + spec: + {{- include (printf "%s.image.pullSecrets" $cfCommonTplSemver ) . | nindent 8 }} + serviceAccountName: {{ include "event-exporter.serviceAccountName" . }} + {{- if .Values.podSecurityContext.enabled }} + securityContext: {{- omit .Values.podSecurityContext "enabled" | toYaml | nindent 8 }} + {{- end }} + containers: + - name: event-exporter + image: {{ include (printf "%s.image.name" $cfCommonTplSemver ) (dict "image" .Values.image "context" .) }} + imagePullPolicy: {{ .Values.image.pullPolicy | default "Always" }} + args: [--running-in-cluster=true] + env: + {{- include "event-exporter.environment-variables" . | nindent 8 }} + ports: + - name: metrics + containerPort: 9102 + resources: + {{- toYaml .Values.resources | nindent 12 }} + volumeMounts: + {{- with .Values.extraVolumeMounts }} + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.nodeSelector }} + nodeSelector: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.affinity }} + affinity: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.tolerations }} + tolerations: + {{- toYaml . | nindent 6 }} + {{- end }} + volumes: + {{- with .Values.extraVolumes }} + {{- toYaml . | nindent 6 }} + {{- end }} +{{- end -}} \ No newline at end of file diff --git a/charts/codefresh/cf-runtime/7.5.5/templates/_components/event-exporter/_env-vars.yaml b/charts/codefresh/cf-runtime/7.5.5/templates/_components/event-exporter/_env-vars.yaml new file mode 100644 index 0000000000..d28d0776f3 --- /dev/null +++ b/charts/codefresh/cf-runtime/7.5.5/templates/_components/event-exporter/_env-vars.yaml @@ -0,0 +1,14 @@ +{{- define "event-exporter.environment-variables.defaults" }} +{{- end }} + +{{- define "event-exporter.environment-variables.calculated" }} +{{- end }} + +{{- define "event-exporter.environment-variables" }} +{{- $cfCommonTplSemver := printf "cf-common-%s" (index .Subcharts "cf-common").Chart.Version }} +{{- $defaults := (include "event-exporter.environment-variables.defaults" . | fromYaml) }} +{{- $calculated := (include "event-exporter.environment-variables.calculated" . | fromYaml) }} +{{- $overrides := .Values.env }} +{{- $mergedValues := mergeOverwrite (merge $defaults $calculated) $overrides }} +{{- include (printf "%s.env-vars" $cfCommonTplSemver) (dict "Values" $mergedValues "context" .) }} +{{- end }} \ No newline at end of file diff --git a/charts/codefresh/cf-runtime/7.5.5/templates/_components/event-exporter/_helpers.tpl b/charts/codefresh/cf-runtime/7.5.5/templates/_components/event-exporter/_helpers.tpl new file mode 100644 index 0000000000..5b8b5eff7f --- /dev/null +++ b/charts/codefresh/cf-runtime/7.5.5/templates/_components/event-exporter/_helpers.tpl @@ -0,0 +1,43 @@ +{{/* +Expand the name of the chart. +*/}} +{{- define "event-exporter.name" -}} + {{- printf "%s-%s" (include "cf-runtime.name" .) "event-exporter" | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Create a default fully qualified app name. +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +If release name contains chart name it will be used as a full name. +*/}} +{{- define "event-exporter.fullname" -}} + {{- printf "%s-%s" (include "cf-runtime.fullname" .) "event-exporter" | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Common labels +*/}} +{{- define "event-exporter.labels" -}} +{{ include "cf-runtime.labels" . }} +app: event-exporter +{{- end }} + +{{/* +Selector labels +*/}} +{{- define "event-exporter.selectorLabels" -}} +{{ include "cf-runtime.selectorLabels" . }} +app: event-exporter +{{- end }} + + +{{/* +Create the name of the service account to use +*/}} +{{- define "event-exporter.serviceAccountName" -}} +{{- if .Values.serviceAccount.create }} +{{- default (include "event-exporter.fullname" .) .Values.serviceAccount.name }} +{{- else }} +{{- default "default" .Values.serviceAccount.name }} +{{- end }} +{{- end }} diff --git a/charts/codefresh/cf-runtime/7.5.5/templates/_components/event-exporter/_rbac.yaml b/charts/codefresh/cf-runtime/7.5.5/templates/_components/event-exporter/_rbac.yaml new file mode 100644 index 0000000000..69d7b6b2fb --- /dev/null +++ b/charts/codefresh/cf-runtime/7.5.5/templates/_components/event-exporter/_rbac.yaml @@ -0,0 +1,47 @@ +{{- define "event-exporter.resources.rbac" -}} +{{- if .Values.serviceAccount.create }} +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ include "event-exporter.serviceAccountName" . }} + labels: + {{- include "event-exporter.labels" . | nindent 4 }} + {{- with .Values.serviceAccount.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +{{- end }} +--- +{{- if .Values.rbac.create }} +kind: ClusterRole +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: {{ include "event-exporter.fullname" . }} + labels: + {{- include "event-exporter.labels" . | nindent 4 }} +rules: + - apiGroups: [""] + resources: [events] + verbs: [get, list, watch] +{{- with .Values.rbac.rules }} + {{ toYaml . | nindent 2 }} +{{- end }} +{{- end }} +--- +{{- if and .Values.serviceAccount.create .Values.rbac.create }} +kind: ClusterRoleBinding +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: {{ include "event-exporter.fullname" . }} + labels: + {{- include "event-exporter.labels" . | nindent 4 }} +subjects: + - kind: ServiceAccount + name: {{ include "event-exporter.serviceAccountName" . }} + namespace: {{ .Release.Namespace }} +roleRef: + kind: ClusterRole + name: {{ include "event-exporter.fullname" . }} + apiGroup: rbac.authorization.k8s.io +{{- end }} +{{- end -}} diff --git a/charts/codefresh/cf-runtime/7.5.5/templates/_components/event-exporter/_service.yaml b/charts/codefresh/cf-runtime/7.5.5/templates/_components/event-exporter/_service.yaml new file mode 100644 index 0000000000..6fa29ec1a0 --- /dev/null +++ b/charts/codefresh/cf-runtime/7.5.5/templates/_components/event-exporter/_service.yaml @@ -0,0 +1,17 @@ +{{- define "event-exporter.resources.service" -}} +apiVersion: v1 +kind: Service +metadata: + name: {{ include "event-exporter.fullname" . }} + labels: + {{- include "event-exporter.labels" . | nindent 4 }} +spec: + type: ClusterIP + ports: + - name: metrics + port: 9102 + targetPort: metrics + protocol: TCP + selector: + {{- include "event-exporter.selectorLabels" . | nindent 4 }} +{{- end -}} \ No newline at end of file diff --git a/charts/codefresh/cf-runtime/7.5.5/templates/_components/event-exporter/_serviceMontor.yaml b/charts/codefresh/cf-runtime/7.5.5/templates/_components/event-exporter/_serviceMontor.yaml new file mode 100644 index 0000000000..6092443f0a --- /dev/null +++ b/charts/codefresh/cf-runtime/7.5.5/templates/_components/event-exporter/_serviceMontor.yaml @@ -0,0 +1,14 @@ +{{- define "event-exporter.resources.serviceMonitor" -}} +apiVersion: monitoring.coreos.com/v1 +kind: ServiceMonitor +metadata: + name: {{ include "event-exporter.fullname" . }} + labels: + {{- include "event-exporter.labels" . | nindent 4 }} +spec: + endpoints: + - port: metrics + selector: + matchLabels: + {{- include "event-exporter.selectorLabels" . | nindent 6 }} +{{- end -}} \ No newline at end of file diff --git a/charts/codefresh/cf-runtime/7.5.5/templates/_components/monitor/_deployment.yaml b/charts/codefresh/cf-runtime/7.5.5/templates/_components/monitor/_deployment.yaml new file mode 100644 index 0000000000..7efa6557b1 --- /dev/null +++ b/charts/codefresh/cf-runtime/7.5.5/templates/_components/monitor/_deployment.yaml @@ -0,0 +1,70 @@ +{{- define "monitor.resources.deployment" -}} +{{ $cfCommonTplSemver := printf "cf-common-%s" (index .Subcharts "cf-common").Chart.Version }} +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ include "monitor.fullname" . }} + labels: + {{- include "monitor.labels" . | nindent 4 }} +spec: + replicas: {{ .Values.replicasCount }} + strategy: + type: {{ .Values.updateStrategy.type }} + selector: + matchLabels: + {{- include "monitor.selectorLabels" . | nindent 6 }} + template: + metadata: + labels: + {{- include "monitor.selectorLabels" . | nindent 8 }} + {{- with .Values.podAnnotations }} + annotations: + {{- toYaml . | nindent 8 }} + {{- end }} + spec: + {{- include (printf "%s.image.pullSecrets" $cfCommonTplSemver ) . | nindent 8 }} + serviceAccountName: {{ include "monitor.serviceAccountName" . }} + {{- if .Values.podSecurityContext.enabled }} + securityContext: {{- omit .Values.podSecurityContext "enabled" | toYaml | nindent 8 }} + {{- end }} + containers: + - name: monitor + image: {{ include (printf "%s.image.name" $cfCommonTplSemver ) (dict "image" .Values.image "context" .) }} + imagePullPolicy: {{ .Values.image.pullPolicy | default "Always" }} + env: + {{- include "monitor.environment-variables" . | nindent 8 }} + ports: + - name: http + containerPort: 9020 + readinessProbe: + initialDelaySeconds: {{ .Values.readinessProbe.initialDelaySeconds }} + periodSeconds: {{ .Values.readinessProbe.periodSeconds }} + timeoutSeconds: {{ .Values.readinessProbe.timeoutSeconds }} + successThreshold: {{ .Values.readinessProbe.successThreshold }} + failureThreshold: {{ .Values.readinessProbe.failureThreshold }} + httpGet: + path: /api/ping + port: 9020 + resources: + {{- toYaml .Values.resources | nindent 12 }} + volumeMounts: + {{- with .Values.extraVolumeMounts }} + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.nodeSelector }} + nodeSelector: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.affinity }} + affinity: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.tolerations }} + tolerations: + {{- toYaml . | nindent 6 }} + {{- end }} + volumes: + {{- with .Values.extraVolumes }} + {{- toYaml . | nindent 6 }} + {{- end }} +{{- end -}} \ No newline at end of file diff --git a/charts/codefresh/cf-runtime/7.5.5/templates/_components/monitor/_env-vars.yaml b/charts/codefresh/cf-runtime/7.5.5/templates/_components/monitor/_env-vars.yaml new file mode 100644 index 0000000000..f58c7fa250 --- /dev/null +++ b/charts/codefresh/cf-runtime/7.5.5/templates/_components/monitor/_env-vars.yaml @@ -0,0 +1,26 @@ +{{- define "monitor.environment-variables.defaults" }} +SERVICE_NAME: {{ include "monitor.fullname" . }} +PORT: 9020 +HELM3: true +NODE_OPTIONS: "--max_old_space_size=4096" +{{- end }} + +{{- define "monitor.environment-variables.calculated" }} +API_TOKEN: {{ include "runtime.installation-token-env-var-value" . | nindent 2 }} +CLUSTER_ID: {{ include "runtime.runtime-environment-spec.context-name" . }} +API_URL: {{ include "runtime.runtime-environment-spec.codefresh-host" . }}/api/k8s-monitor/events +ACCOUNT_ID: {{ .Values.global.accountId }} +NAMESPACE: {{ .Release.Namespace }} +{{- if .Values.rbac.namespaced }} +ROLE_BINDING: true +{{- end }} +{{- end }} + +{{- define "monitor.environment-variables" }} +{{- $cfCommonTplSemver := printf "cf-common-%s" (index .Subcharts "cf-common").Chart.Version }} +{{- $defaults := (include "monitor.environment-variables.defaults" . | fromYaml) }} +{{- $calculated := (include "monitor.environment-variables.calculated" . | fromYaml) }} +{{- $overrides := .Values.env }} +{{- $mergedValues := mergeOverwrite (merge $defaults $calculated) $overrides }} +{{- include (printf "%s.env-vars" $cfCommonTplSemver) (dict "Values" $mergedValues "context" .) }} +{{- end }} \ No newline at end of file diff --git a/charts/codefresh/cf-runtime/7.5.5/templates/_components/monitor/_helpers.tpl b/charts/codefresh/cf-runtime/7.5.5/templates/_components/monitor/_helpers.tpl new file mode 100644 index 0000000000..71cc1c027d --- /dev/null +++ b/charts/codefresh/cf-runtime/7.5.5/templates/_components/monitor/_helpers.tpl @@ -0,0 +1,42 @@ +{{/* +Expand the name of the chart. +*/}} +{{- define "monitor.name" -}} + {{- printf "%s-%s" (include "cf-runtime.name" .) "monitor" | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Create a default fully qualified app name. +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +If release name contains chart name it will be used as a full name. +*/}} +{{- define "monitor.fullname" -}} + {{- printf "%s-%s" (include "cf-runtime.fullname" .) "monitor" | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Common labels +*/}} +{{- define "monitor.labels" -}} +{{ include "cf-runtime.labels" . }} +codefresh.io/application: monitor +{{- end }} + +{{/* +Selector labels +*/}} +{{- define "monitor.selectorLabels" -}} +{{ include "cf-runtime.selectorLabels" . }} +codefresh.io/application: monitor +{{- end }} + +{{/* +Create the name of the service account to use +*/}} +{{- define "monitor.serviceAccountName" -}} +{{- if .Values.serviceAccount.create }} +{{- default (include "monitor.fullname" .) .Values.serviceAccount.name }} +{{- else }} +{{- default "default" .Values.serviceAccount.name }} +{{- end }} +{{- end }} \ No newline at end of file diff --git a/charts/codefresh/cf-runtime/7.5.5/templates/_components/monitor/_rbac.yaml b/charts/codefresh/cf-runtime/7.5.5/templates/_components/monitor/_rbac.yaml new file mode 100644 index 0000000000..88204796ae --- /dev/null +++ b/charts/codefresh/cf-runtime/7.5.5/templates/_components/monitor/_rbac.yaml @@ -0,0 +1,56 @@ +{{- define "monitor.resources.rbac" -}} +{{- if .Values.serviceAccount.create }} +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ include "monitor.serviceAccountName" . }} + labels: + {{- include "monitor.labels" . | nindent 4 }} + {{- with .Values.serviceAccount.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +{{- end }} +--- +{{- if .Values.rbac.create }} +kind: {{ .Values.rbac.namespaced | ternary "Role" "ClusterRole" }} +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: {{ include "monitor.fullname" . }} + labels: + {{- include "monitor.labels" . | nindent 4 }} +rules: + - apiGroups: [ "" ] + resources: [ "*" ] + verbs: [ "get", "list", "watch", "create", "delete" ] + - apiGroups: [ "" ] + resources: [ "pods" ] + verbs: [ "get", "list", "watch", "create", "deletecollection" ] + - apiGroups: [ "extensions" ] + resources: [ "*" ] + verbs: [ "get", "list", "watch" ] + - apiGroups: [ "apps" ] + resources: [ "*" ] + verbs: [ "get", "list", "watch" ] +{{- with .Values.rbac.rules }} + {{ toYaml . | nindent 2 }} +{{- end }} +{{- end }} +--- +{{- if and .Values.serviceAccount.create .Values.rbac.create }} +kind: {{ .Values.rbac.namespaced | ternary "RoleBinding" "ClusterRoleBinding" }} +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: {{ include "monitor.fullname" . }} + labels: + {{- include "monitor.labels" . | nindent 4 }} +subjects: + - kind: ServiceAccount + name: {{ include "monitor.serviceAccountName" . }} + namespace: {{ .Release.Namespace }} +roleRef: + kind: {{ .Values.rbac.namespaced | ternary "Role" "ClusterRole" }} + name: {{ include "monitor.fullname" . }} + apiGroup: rbac.authorization.k8s.io +{{- end }} +{{- end -}} diff --git a/charts/codefresh/cf-runtime/7.5.5/templates/_components/monitor/_service.yaml b/charts/codefresh/cf-runtime/7.5.5/templates/_components/monitor/_service.yaml new file mode 100644 index 0000000000..f6ae9bb0f7 --- /dev/null +++ b/charts/codefresh/cf-runtime/7.5.5/templates/_components/monitor/_service.yaml @@ -0,0 +1,17 @@ +{{- define "monitor.resources.service" -}} +apiVersion: v1 +kind: Service +metadata: + name: {{ include "monitor.fullname" . }} + labels: + {{- include "monitor.labels" . | nindent 4 }} +spec: + type: ClusterIP + ports: + - name: http + port: 80 + protocol: TCP + targetPort: 9020 + selector: + {{- include "monitor.selectorLabels" . | nindent 4 }} +{{- end -}} diff --git a/charts/codefresh/cf-runtime/7.5.5/templates/_components/runner/_deployment.yaml b/charts/codefresh/cf-runtime/7.5.5/templates/_components/runner/_deployment.yaml new file mode 100644 index 0000000000..e1fb9439ab --- /dev/null +++ b/charts/codefresh/cf-runtime/7.5.5/templates/_components/runner/_deployment.yaml @@ -0,0 +1,103 @@ +{{- define "runner.resources.deployment" -}} +{{ $cfCommonTplSemver := printf "cf-common-%s" (index .Subcharts "cf-common").Chart.Version }} +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ include "runner.fullname" . }} + labels: + {{- include "runner.labels" . | nindent 4 }} +spec: + replicas: {{ .Values.replicasCount }} + strategy: + type: {{ .Values.updateStrategy.type }} + selector: + matchLabels: + {{- include "runner.selectorLabels" . | nindent 6 }} + template: + metadata: + labels: + {{- include "runner.selectorLabels" . | nindent 8 }} + {{- with .Values.podAnnotations }} + annotations: + {{- toYaml . | nindent 8 }} + {{- end }} + spec: + {{- include (printf "%s.image.pullSecrets" $cfCommonTplSemver ) . | nindent 8 }} + serviceAccountName: {{ include "runner.serviceAccountName" . }} + {{- if .Values.podSecurityContext.enabled }} + securityContext: {{- omit .Values.podSecurityContext "enabled" | toYaml | nindent 8 }} + {{- end }} + initContainers: + - name: init + image: {{ include (printf "%s.image.name" $cfCommonTplSemver ) (dict "image" .Values.init.image "context" .) }} + imagePullPolicy: {{ .Values.init.image.pullPolicy | default "IfNotPresent" }} + command: + - /bin/bash + args: + - -ec + - | {{ .Files.Get "files/init-runtime.sh" | nindent 10 }} + env: + {{- include "runner-init.environment-variables" . | nindent 8 }} + {{- with .Values.init.resources }} + resources: + {{- toYaml . | nindent 10 }} + {{- end }} + containers: + - name: runner + image: {{ include (printf "%s.image.name" $cfCommonTplSemver ) (dict "image" .Values.image "context" .) }} + imagePullPolicy: {{ .Values.image.pullPolicy | default "IfNotPresent" }} + env: + {{- include "runner.environment-variables" . | nindent 8 }} + ports: + - name: http + containerPort: 8080 + readinessProbe: + initialDelaySeconds: {{ .Values.readinessProbe.initialDelaySeconds }} + periodSeconds: {{ .Values.readinessProbe.periodSeconds }} + timeoutSeconds: {{ .Values.readinessProbe.timeoutSeconds }} + successThreshold: {{ .Values.readinessProbe.successThreshold }} + failureThreshold: {{ .Values.readinessProbe.failureThreshold }} + httpGet: + path: /health + port: http + {{- with .Values.resources }} + resources: + {{- toYaml . | nindent 10 }} + {{- end }} + {{- with .Values.extraVolumeMounts }} + volumeMounts: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- if .Values.sidecar.enabled }} + - name: reconcile-runtime + image: {{ include (printf "%s.image.name" $cfCommonTplSemver ) (dict "image" .Values.sidecar.image "context" .) }} + imagePullPolicy: {{ .Values.sidecar.image.pullPolicy | default "IfNotPresent" }} + command: + - /bin/bash + args: + - -ec + - | {{ .Files.Get "files/reconcile-runtime.sh" | nindent 10 }} + env: + {{- include "runner-sidecar.environment-variables" . | nindent 8 }} + {{- with .Values.sidecar.resources }} + resources: + {{- toYaml . | nindent 10 }} + {{- end }} + {{- end }} + {{- with .Values.nodeSelector }} + nodeSelector: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.affinity }} + affinity: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.tolerations }} + tolerations: + {{- toYaml . | nindent 6 }} + {{- end }} + {{- with .Values.extraVolumes }} + volumes: + {{- toYaml . | nindent 6 }} + {{- end }} +{{- end -}} diff --git a/charts/codefresh/cf-runtime/7.5.5/templates/_components/runner/_helpers.tpl b/charts/codefresh/cf-runtime/7.5.5/templates/_components/runner/_helpers.tpl new file mode 100644 index 0000000000..2608cb67ee --- /dev/null +++ b/charts/codefresh/cf-runtime/7.5.5/templates/_components/runner/_helpers.tpl @@ -0,0 +1,42 @@ +{{/* +Expand the name of the chart. +*/}} +{{- define "runner.name" -}} + {{- printf "%s-%s" (include "cf-runtime.name" .) "runner" | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Create a default fully qualified app name. +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +If release name contains chart name it will be used as a full name. +*/}} +{{- define "runner.fullname" -}} + {{- printf "%s-%s" (include "cf-runtime.fullname" .) "runner" | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Common labels +*/}} +{{- define "runner.labels" -}} +{{ include "cf-runtime.labels" . }} +codefresh.io/application: runner +{{- end }} + +{{/* +Selector labels +*/}} +{{- define "runner.selectorLabels" -}} +{{ include "cf-runtime.selectorLabels" . }} +codefresh.io/application: runner +{{- end }} + +{{/* +Create the name of the service account to use +*/}} +{{- define "runner.serviceAccountName" -}} + {{- if .Values.serviceAccount.create }} + {{- default (include "runner.fullname" .) .Values.serviceAccount.name }} + {{- else }} + {{- default "default" .Values.serviceAccount.name }} + {{- end }} +{{- end }} diff --git a/charts/codefresh/cf-runtime/7.5.5/templates/_components/runner/_rbac.yaml b/charts/codefresh/cf-runtime/7.5.5/templates/_components/runner/_rbac.yaml new file mode 100644 index 0000000000..d95b958d54 --- /dev/null +++ b/charts/codefresh/cf-runtime/7.5.5/templates/_components/runner/_rbac.yaml @@ -0,0 +1,53 @@ +{{- define "runner.resources.rbac" -}} +{{- if .Values.serviceAccount.create }} +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ include "runner.serviceAccountName" . }} + labels: + {{- include "runner.labels" . | nindent 4 }} + {{- with .Values.serviceAccount.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +{{- end }} +--- +{{- if .Values.rbac.create }} +kind: Role +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: {{ include "runner.fullname" . }} + labels: + {{- include "runner.labels" . | nindent 4 }} +rules: + - apiGroups: [ "" ] + resources: [ "pods", "persistentvolumeclaims" ] + verbs: [ "get", "create", "delete", patch ] + - apiGroups: [ "" ] + resources: [ "configmaps", "secrets" ] + verbs: [ "get", "create", "update", patch ] + - apiGroups: [ "apps" ] + resources: [ "deployments" ] + verbs: [ "get" ] +{{- with .Values.rbac.rules }} + {{ toYaml . | nindent 2 }} +{{- end }} +{{- end }} +--- +{{- if and .Values.serviceAccount.create .Values.rbac.create }} +kind: RoleBinding +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: {{ include "runner.fullname" . }} + labels: + {{- include "runner.labels" . | nindent 4 }} +subjects: + - kind: ServiceAccount + name: {{ include "runner.serviceAccountName" . }} + namespace: {{ .Release.Namespace }} +roleRef: + kind: Role + name: {{ include "runner.fullname" . }} + apiGroup: rbac.authorization.k8s.io +{{- end }} +{{- end -}} diff --git a/charts/codefresh/cf-runtime/7.5.5/templates/_components/runner/environment-variables/_init-container.yaml b/charts/codefresh/cf-runtime/7.5.5/templates/_components/runner/environment-variables/_init-container.yaml new file mode 100644 index 0000000000..6dda110f78 --- /dev/null +++ b/charts/codefresh/cf-runtime/7.5.5/templates/_components/runner/environment-variables/_init-container.yaml @@ -0,0 +1,30 @@ +{{- define "runner-init.environment-variables.defaults" }} +HOME: /tmp +{{- end }} + +{{- define "runner-init.environment-variables.calculated" }} +AGENT_NAME: {{ include "runtime.runtime-environment-spec.agent-name" . }} +API_HOST: {{ include "runtime.runtime-environment-spec.codefresh-host" . }} +AGENT_CODEFRESH_TOKEN: + valueFrom: + secretKeyRef: + name: {{ include "runner.fullname" . }} + key: agent-codefresh-token + optional: true +EXISTING_AGENT_CODEFRESH_TOKEN: {{ include "runtime.agent-token-env-var-value" . | nindent 2 }} +KUBE_CONTEXT: {{ include "runtime.runtime-environment-spec.context-name" . }} +KUBE_NAMESPACE: {{ .Release.Namespace }} +OWNER_NAME: {{ include "runner.fullname" . }} +RUNTIME_NAME: {{ include "runtime.runtime-environment-spec.runtime-name" . }} +SECRET_NAME: {{ include "runner.fullname" . }} +USER_CODEFRESH_TOKEN: {{ include "runtime.installation-token-env-var-value" . | nindent 2 }} +{{- end }} + +{{- define "runner-init.environment-variables" }} + {{- $cfCommonTplSemver := printf "cf-common-%s" (index .Subcharts "cf-common").Chart.Version }} + {{- $defaults := (include "runner-init.environment-variables.defaults" . | fromYaml) }} + {{- $calculated := (include "runner-init.environment-variables.calculated" . | fromYaml) }} + {{- $overrides := .Values.env }} + {{- $mergedValues := mergeOverwrite (merge $defaults $calculated) $overrides }} + {{- include (printf "%s.env-vars" $cfCommonTplSemver) (dict "Values" $mergedValues "context" .) }} +{{- end }} diff --git a/charts/codefresh/cf-runtime/7.5.5/templates/_components/runner/environment-variables/_main-container.yaml b/charts/codefresh/cf-runtime/7.5.5/templates/_components/runner/environment-variables/_main-container.yaml new file mode 100644 index 0000000000..a9e3b78de4 --- /dev/null +++ b/charts/codefresh/cf-runtime/7.5.5/templates/_components/runner/environment-variables/_main-container.yaml @@ -0,0 +1,29 @@ +{{- define "runner.environment-variables.defaults" }} +AGENT_MODE: InCluster +SELF_DEPLOYMENT_NAME: + valueFrom: + fieldRef: + fieldPath: metadata.name +{{- end }} + +{{- define "runner.environment-variables.calculated" }} +AGENT_ID: {{ include "runtime.runtime-environment-spec.agent-name" . }} +CODEFRESH_HOST: {{ include "runtime.runtime-environment-spec.codefresh-host" . }} +CODEFRESH_IN_CLUSTER_RUNTIME: {{ include "runtime.runtime-environment-spec.runtime-name" . }} +CODEFRESH_TOKEN: + valueFrom: + secretKeyRef: + name: {{ include "runner.fullname" . }} + key: agent-codefresh-token +DOCKER_REGISTRY: {{ .Values.global.imageRegistry }} +RUNTIME_CHART_VERSION: {{ .Chart.Version }} +{{- end }} + +{{- define "runner.environment-variables" }} +{{- $cfCommonTplSemver := printf "cf-common-%s" (index .Subcharts "cf-common").Chart.Version }} +{{- $defaults := (include "runner.environment-variables.defaults" . | fromYaml) }} +{{- $calculated := (include "runner.environment-variables.calculated" . | fromYaml) }} +{{- $overrides := .Values.env }} +{{- $mergedValues := mergeOverwrite (merge $defaults $calculated) $overrides }} +{{- include (printf "%s.env-vars" $cfCommonTplSemver) (dict "Values" $mergedValues "context" .) }} +{{- end }} diff --git a/charts/codefresh/cf-runtime/7.5.5/templates/_components/runner/environment-variables/_sidecar-container.yaml b/charts/codefresh/cf-runtime/7.5.5/templates/_components/runner/environment-variables/_sidecar-container.yaml new file mode 100644 index 0000000000..3adcbe5d49 --- /dev/null +++ b/charts/codefresh/cf-runtime/7.5.5/templates/_components/runner/environment-variables/_sidecar-container.yaml @@ -0,0 +1,22 @@ +{{- define "runner-sidecar.environment-variables.defaults" }} +HOME: /tmp +{{- end }} + +{{- define "runner-sidecar.environment-variables.calculated" }} +API_HOST: {{ include "runtime.runtime-environment-spec.codefresh-host" . }} +USER_CODEFRESH_TOKEN: {{ include "runtime.installation-token-env-var-value" . | nindent 2 }} +KUBE_CONTEXT: {{ include "runtime.runtime-environment-spec.context-name" . }} +KUBE_NAMESPACE: {{ .Release.Namespace }} +OWNER_NAME: {{ include "runner.fullname" . }} +RUNTIME_NAME: {{ include "runtime.runtime-environment-spec.runtime-name" . }} +CONFIGMAP_NAME: {{ printf "%s-%s" (include "runtime.fullname" .) "spec" }} +{{- end }} + +{{- define "runner-sidecar.environment-variables" }} + {{- $cfCommonTplSemver := printf "cf-common-%s" (index .Subcharts "cf-common").Chart.Version }} + {{- $defaults := (include "runner-sidecar.environment-variables.defaults" . | fromYaml) }} + {{- $calculated := (include "runner-sidecar.environment-variables.calculated" . | fromYaml) }} + {{- $overrides := .Values.sidecar.env }} + {{- $mergedValues := mergeOverwrite (merge $defaults $calculated) $overrides }} + {{- include (printf "%s.env-vars" $cfCommonTplSemver) (dict "Values" $mergedValues "context" .) }} +{{- end }} diff --git a/charts/codefresh/cf-runtime/7.5.5/templates/_components/volume-provisioner/_cronjob.yaml b/charts/codefresh/cf-runtime/7.5.5/templates/_components/volume-provisioner/_cronjob.yaml new file mode 100644 index 0000000000..20bd2d56e1 --- /dev/null +++ b/charts/codefresh/cf-runtime/7.5.5/templates/_components/volume-provisioner/_cronjob.yaml @@ -0,0 +1,58 @@ +{{- define "dind-volume-provisioner.resources.cronjob" -}} +{{ $cfCommonTplSemver := printf "cf-common-%s" (index .Subcharts "cf-common").Chart.Version }} +{{- if not (eq .Values.storage.backend "local") }} +apiVersion: batch/v1 +kind: CronJob +metadata: + name: {{ include "dind-volume-cleanup.fullname" . }} + labels: + {{- include "dind-volume-cleanup.labels" . | nindent 4 }} +spec: + concurrencyPolicy: {{ .Values.concurrencyPolicy }} + schedule: {{ .Values.schedule | quote }} + successfulJobsHistoryLimit: {{ .Values.successfulJobsHistory }} + failedJobsHistoryLimit: {{ .Values.failedJobsHistory }} + {{- with .Values.suspend }} + suspend: {{ . }} + {{- end }} + jobTemplate: + spec: + template: + metadata: + labels: + {{- include "dind-volume-cleanup.selectorLabels" . | nindent 12 }} + {{- with .Values.podAnnotations }} + annotations: + {{- toYaml . | nindent 12 }} + {{- end }} + spec: + {{- include (printf "%s.image.pullSecrets" $cfCommonTplSemver ) . | nindent 10 }} + serviceAccountName: {{ include "dind-volume-provisioner.serviceAccountName" . }} + {{- if .Values.podSecurityContext.enabled }} + securityContext: {{- omit .Values.podSecurityContext "enabled" | toYaml | nindent 12 }} + {{- end }} + restartPolicy: {{ .Values.restartPolicy | default "Never" }} + containers: + - name: dind-volume-cleanup + image: {{ include (printf "%s.image.name" $cfCommonTplSemver ) (dict "image" .Values.image "context" .) }} + imagePullPolicy: {{ .Values.image.pullPolicy | default "Always" }} + env: + {{- include (printf "%s.env-vars" $cfCommonTplSemver) (dict "Values" .Values.env "context" .) | nindent 12 }} + - name: PROVISIONED_BY + value: {{ include "dind-volume-provisioner.volumeProvisionerName" . }} + resources: + {{- toYaml .Values.resources | nindent 14 }} + {{- with .Values.nodeSelector }} + nodeSelector: + {{- toYaml . | nindent 12 }} + {{- end }} + {{- with .Values.affinity }} + affinity: + {{- toYaml . | nindent 12 }} + {{- end }} + {{- with .Values.tolerations }} + tolerations: + {{- toYaml . | nindent 10 }} + {{- end }} + {{- end }} +{{- end -}} diff --git a/charts/codefresh/cf-runtime/7.5.5/templates/_components/volume-provisioner/_daemonset.yaml b/charts/codefresh/cf-runtime/7.5.5/templates/_components/volume-provisioner/_daemonset.yaml new file mode 100644 index 0000000000..b24814702a --- /dev/null +++ b/charts/codefresh/cf-runtime/7.5.5/templates/_components/volume-provisioner/_daemonset.yaml @@ -0,0 +1,98 @@ +{{- define "dind-volume-provisioner.resources.daemonset" -}} +{{ $cfCommonTplSemver := printf "cf-common-%s" (index .Subcharts "cf-common").Chart.Version }} +{{ $localVolumeParentDir := .Values.storage.local.volumeParentDir }} +{{- if eq .Values.storage.backend "local" }} +--- +apiVersion: apps/v1 +kind: DaemonSet +metadata: + name: {{ include "dind-lv-monitor.fullname" . }} + labels: + {{- include "dind-lv-monitor.labels" . | nindent 4 }} +spec: + selector: + matchLabels: + {{- include "dind-lv-monitor.selectorLabels" . | nindent 6 }} + template: + metadata: + labels: + {{- include "dind-lv-monitor.selectorLabels" . | nindent 8 }} + {{- with .Values.podAnnotations }} + annotations: + {{- toYaml . | nindent 8 }} + {{- end }} + spec: + {{- include (printf "%s.image.pullSecrets" $cfCommonTplSemver ) . | nindent 8 }} + serviceAccountName: {{ include "dind-volume-provisioner.serviceAccountName" . }} + {{- if .Values.podSecurityContext.enabled }} + securityContext: {{- omit .Values.podSecurityContext "enabled" | toYaml | nindent 8 }} + {{- end }} + {{- if .Values.volumePermissions.enabled }} + initContainers: + - name: volume-permissions + image: {{ include (printf "%s.image.name" $cfCommonTplSemver ) (dict "image" .Values.volumePermissions.image "context" .) }} + imagePullPolicy: {{ .Values.volumePermissions.image.pullPolicy | default "Always" }} + command: + - /bin/sh + args: + - -ec + - | + chown -R {{ .Values.containerSecurityContext.runAsUser }}:{{ .Values.podSecurityContext.fsGroup }} {{ $localVolumeParentDir }} + volumeMounts: + - mountPath: {{ $localVolumeParentDir }} + name: dind-volume-dir + {{- if eq ( toString ( .Values.volumePermissions.securityContext.runAsUser )) "auto" }} + securityContext: {{- omit .Values.volumePermissions.securityContext "runAsUser" | toYaml | nindent 10 }} + {{- else }} + securityContext: {{- .Values.volumePermissions.securityContext | toYaml | nindent 10 }} + {{- end }} + resources: + {{- toYaml .Values.volumePermissions.resources | nindent 10 }} + {{- end }} + containers: + - name: dind-lv-monitor + image: {{ include (printf "%s.image.name" $cfCommonTplSemver ) (dict "image" .Values.image "context" .) }} + imagePullPolicy: {{ .Values.image.pullPolicy | default "Always" }} + {{- if .Values.containerSecurityContext.enabled }} + securityContext: {{- omit .Values.containerSecurityContext "enabled" | toYaml | nindent 10 }} + {{- end }} + command: + - /home/dind-volume-utils/bin/local-volumes-agent + env: + {{- include (printf "%s.env-vars" $cfCommonTplSemver) (dict "Values" .Values.env "context" .) | nindent 10 }} + - name: NODE_NAME + valueFrom: + fieldRef: + fieldPath: spec.nodeName + - name: VOLUME_PARENT_DIR + value: {{ $localVolumeParentDir }} + resources: + {{- toYaml .Values.resources | nindent 10 }} + volumeMounts: + - mountPath: {{ $localVolumeParentDir }} + readOnly: false + name: dind-volume-dir + {{- with .Values.extraVolumeMounts }} + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.nodeSelector }} + nodeSelector: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.affinity }} + affinity: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.tolerations }} + tolerations: + {{- toYaml . | nindent 6 }} + {{- end }} + volumes: + - name: dind-volume-dir + hostPath: + path: {{ $localVolumeParentDir }} + {{- with .Values.extraVolumes }} + {{- toYaml . | nindent 6 }} + {{- end }} +{{- end }} +{{- end -}} diff --git a/charts/codefresh/cf-runtime/7.5.5/templates/_components/volume-provisioner/_deployment.yaml b/charts/codefresh/cf-runtime/7.5.5/templates/_components/volume-provisioner/_deployment.yaml new file mode 100644 index 0000000000..9252b45200 --- /dev/null +++ b/charts/codefresh/cf-runtime/7.5.5/templates/_components/volume-provisioner/_deployment.yaml @@ -0,0 +1,67 @@ +{{- define "dind-volume-provisioner.resources.deployment" -}} +{{ $cfCommonTplSemver := printf "cf-common-%s" (index .Subcharts "cf-common").Chart.Version }} +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ include "dind-volume-provisioner.fullname" . }} + labels: + {{- include "dind-volume-provisioner.labels" . | nindent 4 }} +spec: + replicas: {{ .Values.replicasCount }} + strategy: + type: {{ .Values.updateStrategy.type }} + selector: + matchLabels: + {{- include "dind-volume-provisioner.selectorLabels" . | nindent 6 }} + template: + metadata: + labels: + {{- include "dind-volume-provisioner.selectorLabels" . | nindent 8 }} + {{- with .Values.podAnnotations }} + annotations: + {{- toYaml . | nindent 8 }} + {{- end }} + spec: + {{- include (printf "%s.image.pullSecrets" $cfCommonTplSemver ) . | nindent 8 }} + serviceAccountName: {{ include "dind-volume-provisioner.serviceAccountName" . }} + {{- if .Values.podSecurityContext.enabled }} + securityContext: {{- omit .Values.podSecurityContext "enabled" | toYaml | nindent 8 }} + {{- end }} + containers: + - name: dind-volume-provisioner + image: {{ include (printf "%s.image.name" $cfCommonTplSemver ) (dict "image" .Values.image "context" .) }} + imagePullPolicy: {{ .Values.image.pullPolicy | default "Always" }} + command: + - /usr/local/bin/dind-volume-provisioner + - -v=4 + - --resync-period=50s + env: + {{- include "dind-volume-provisioner.environment-variables" . | nindent 8 }} + ports: + - name: http + containerPort: 8080 + resources: + {{- toYaml .Values.resources | nindent 12 }} + volumeMounts: + {{- include "dind-volume-provisioner.volumeMounts.calculated" . | nindent 8 }} + {{- with .Values.extraVolumeMounts }} + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.nodeSelector }} + nodeSelector: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.affinity }} + affinity: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.tolerations }} + tolerations: + {{- toYaml . | nindent 6 }} + {{- end }} + volumes: + {{- include "dind-volume-provisioner.volumes.calculated" . | nindent 6 }} + {{- with .Values.extraVolumes }} + {{- toYaml . | nindent 6 }} + {{- end }} +{{- end -}} \ No newline at end of file diff --git a/charts/codefresh/cf-runtime/7.5.5/templates/_components/volume-provisioner/_env-vars.yaml b/charts/codefresh/cf-runtime/7.5.5/templates/_components/volume-provisioner/_env-vars.yaml new file mode 100644 index 0000000000..e1f5dfe603 --- /dev/null +++ b/charts/codefresh/cf-runtime/7.5.5/templates/_components/volume-provisioner/_env-vars.yaml @@ -0,0 +1,88 @@ +{{- define "dind-volume-provisioner.environment-variables.defaults" }} +{{- end }} + +{{- define "dind-volume-provisioner.environment-variables.calculated" }} +DOCKER_REGISTRY: {{ .Values.global.imageRegistry }} +PROVISIONER_NAME: {{ include "dind-volume-provisioner.volumeProvisionerName" . }} + +{{- if or .Values.storage.ebs.accessKeyId .Values.storage.ebs.accessKeyIdSecretKeyRef }} +AWS_ACCESS_KEY_ID: + {{- if .Values.storage.ebs.accessKeyId }} + valueFrom: + secretKeyRef: + name: {{ include "dind-volume-provisioner.fullname" . }} + key: aws_access_key_id + {{- else if .Values.storage.ebs.accessKeyIdSecretKeyRef }} + valueFrom: + secretKeyRef: + {{- .Values.storage.ebs.accessKeyIdSecretKeyRef | toYaml | nindent 6 }} + {{- end }} +{{- end }} + +{{- if or .Values.storage.ebs.secretAccessKey .Values.storage.ebs.secretAccessKeySecretKeyRef }} +AWS_SECRET_ACCESS_KEY: + {{- if .Values.storage.ebs.secretAccessKey }} + valueFrom: + secretKeyRef: + name: {{ include "dind-volume-provisioner.fullname" . }} + key: aws_secret_access_key + {{- else if .Values.storage.ebs.secretAccessKeySecretKeyRef }} + valueFrom: + secretKeyRef: + {{- .Values.storage.ebs.secretAccessKeySecretKeyRef | toYaml | nindent 6 }} + {{- end }} +{{- end }} + +{{- if or .Values.storage.gcedisk.serviceAccountJson .Values.storage.gcedisk.serviceAccountJsonSecretKeyRef }} +GOOGLE_APPLICATION_CREDENTIALS: {{ printf "/etc/dind-volume-provisioner/credentials/%s" (.Values.storage.gcedisk.serviceAccountJsonSecretKeyRef.key | default "google-service-account.json") }} +{{- end }} + +{{- if and .Values.storage.mountAzureJson }} +AZURE_CREDENTIAL_FILE: /etc/kubernetes/azure.json +CLOUDCONFIG_AZURE: /etc/kubernetes/azure.json +{{- end }} + +{{- end }} + +{{- define "dind-volume-provisioner.environment-variables" }} +{{- $cfCommonTplSemver := printf "cf-common-%s" (index .Subcharts "cf-common").Chart.Version }} +{{- $defaults := (include "dind-volume-provisioner.environment-variables.defaults" . | fromYaml) }} +{{- $calculated := (include "dind-volume-provisioner.environment-variables.calculated" . | fromYaml) }} +{{- $overrides := .Values.env }} +{{- $mergedValues := mergeOverwrite (merge $defaults $calculated) $overrides }} +{{- include (printf "%s.env-vars" $cfCommonTplSemver) (dict "Values" $mergedValues "context" .) }} +{{- end }} + + +{{- define "dind-volume-provisioner.volumes.calculated" }} + {{- if .Values.storage.gcedisk.serviceAccountJson }} +- name: credentials + secret: + secretName: {{ include "dind-volume-provisioner.fullname" . }} + optional: true + {{- else if .Values.storage.gcedisk.serviceAccountJsonSecretKeyRef }} +- name: credentials + secret: + secretName: {{ .Values.storage.gcedisk.serviceAccountJsonSecretKeyRef.name }} + optional: true + {{- end }} + {{- if .Values.storage.mountAzureJson }} +- name: azure-json + hostPath: + path: /etc/kubernetes/azure.json + type: File + {{- end }} +{{- end }} + +{{- define "dind-volume-provisioner.volumeMounts.calculated" }} + {{- if or .Values.storage.gcedisk.serviceAccountJson .Values.storage.gcedisk.serviceAccountJsonSecretKeyRef }} +- name: credentials + readOnly: true + mountPath: "/etc/dind-volume-provisioner/credentials" + {{- end }} + {{- if .Values.storage.mountAzureJson }} +- name: azure-json + readOnly: true + mountPath: "/etc/kubernetes/azure.json" + {{- end }} +{{- end }} diff --git a/charts/codefresh/cf-runtime/7.5.5/templates/_components/volume-provisioner/_helpers.tpl b/charts/codefresh/cf-runtime/7.5.5/templates/_components/volume-provisioner/_helpers.tpl new file mode 100644 index 0000000000..e3d3a0d3f7 --- /dev/null +++ b/charts/codefresh/cf-runtime/7.5.5/templates/_components/volume-provisioner/_helpers.tpl @@ -0,0 +1,93 @@ +{{/* +Expand the name of the chart. +*/}} +{{- define "dind-volume-provisioner.name" -}} + {{- printf "%s-%s" (include "cf-runtime.name" .) "volume-provisioner" | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Create a default fully qualified app name. +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +If release name contains chart name it will be used as a full name. +*/}} +{{- define "dind-volume-provisioner.fullname" -}} + {{- printf "%s-%s" (include "cf-runtime.fullname" .) "volume-provisioner" | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{- define "dind-volume-cleanup.fullname" -}} + {{- printf "%s-%s" (include "cf-runtime.fullname" .) "volume-cleanup" | trunc 52 | trimSuffix "-" }} +{{- end }} + +{{- define "dind-lv-monitor.fullname" -}} + {{- printf "%s-%s" (include "cf-runtime.fullname" .) "lv-monitor" | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Provisioner name for storage class +*/}} +{{- define "dind-volume-provisioner.volumeProvisionerName" }} + {{- printf "codefresh.io/dind-volume-provisioner-runner-%s" .Release.Namespace }} +{{- end }} + +{{/* +Common labels for dind-lv-monitor +*/}} +{{- define "dind-lv-monitor.labels" -}} +{{ include "cf-runtime.labels" . }} +codefresh.io/application: lv-monitor +{{- end }} + +{{/* +Selector labels for dind-lv-monitor +*/}} +{{- define "dind-lv-monitor.selectorLabels" -}} +{{ include "cf-runtime.selectorLabels" . }} +codefresh.io/application: lv-monitor +{{- end }} + +{{/* +Common labels for dind-volume-provisioner +*/}} +{{- define "dind-volume-provisioner.labels" -}} +{{ include "cf-runtime.labels" . }} +codefresh.io/application: volume-provisioner +{{- end }} + +{{/* +Selector labels for dind-volume-provisioner +*/}} +{{- define "dind-volume-provisioner.selectorLabels" -}} +{{ include "cf-runtime.selectorLabels" . }} +codefresh.io/application: volume-provisioner +{{- end }} + +{{/* +Common labels for dind-volume-cleanup +*/}} +{{- define "dind-volume-cleanup.labels" -}} +{{ include "cf-runtime.labels" . }} +codefresh.io/application: pv-cleanup +{{- end }} + +{{/* +Common labels for dind-volume-cleanup +*/}} +{{- define "dind-volume-cleanup.selectorLabels" -}} +{{ include "cf-runtime.selectorLabels" . }} +codefresh.io/application: pv-cleanup +{{- end }} + +{{/* +Create the name of the service account to use +*/}} +{{- define "dind-volume-provisioner.serviceAccountName" -}} +{{- if .Values.serviceAccount.create }} +{{- default (include "dind-volume-provisioner.fullname" .) .Values.serviceAccount.name }} +{{- else }} +{{- default "default" .Values.serviceAccount.name }} +{{- end }} +{{- end }} + +{{- define "dind-volume-provisioner.storageClassName" }} +{{- printf "dind-local-volumes-runner-%s" .Release.Namespace }} +{{- end }} \ No newline at end of file diff --git a/charts/codefresh/cf-runtime/7.5.5/templates/_components/volume-provisioner/_rbac.yaml b/charts/codefresh/cf-runtime/7.5.5/templates/_components/volume-provisioner/_rbac.yaml new file mode 100644 index 0000000000..fbcbc684fc --- /dev/null +++ b/charts/codefresh/cf-runtime/7.5.5/templates/_components/volume-provisioner/_rbac.yaml @@ -0,0 +1,71 @@ +{{- define "dind-volume-provisioner.resources.rbac" -}} +{{- if .Values.serviceAccount.create }} +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ include "dind-volume-provisioner.serviceAccountName" . }} + labels: + {{- include "dind-volume-provisioner.labels" . | nindent 4 }} + {{- with .Values.serviceAccount.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +{{- end }} +--- +{{- if .Values.rbac.create }} +kind: ClusterRole +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: {{ include "dind-volume-provisioner.fullname" . }} + labels: + {{- include "dind-volume-provisioner.labels" . | nindent 4 }} +rules: + - apiGroups: [ "" ] + resources: [ "persistentvolumes" ] + verbs: [ "get", "list", "watch", "create", "delete", "patch" ] + - apiGroups: [ "" ] + resources: [ "persistentvolumeclaims" ] + verbs: [ "get", "list", "watch", "update", "delete" ] + - apiGroups: [ "storage.k8s.io" ] + resources: [ "storageclasses" ] + verbs: [ "get", "list", "watch" ] + - apiGroups: [ "" ] + resources: [ "events" ] + verbs: [ "list", "watch", "create", "update", "patch" ] + - apiGroups: [ "" ] + resources: [ "secrets" ] + verbs: [ "get", "list" ] + - apiGroups: [ "" ] + resources: [ "nodes" ] + verbs: [ "get", "list", "watch" ] + - apiGroups: [ "" ] + resources: [ "pods" ] + verbs: [ "get", "list", "watch", "create", "delete", "patch" ] + - apiGroups: [ "" ] + resources: [ "endpoints" ] + verbs: [ "get", "list", "watch", "create", "update", "delete" ] + - apiGroups: [ "coordination.k8s.io" ] + resources: [ "leases" ] + verbs: [ "get", "create", "update" ] +{{- with .Values.rbac.rules }} + {{ toYaml . | nindent 2 }} +{{- end }} +{{- end }} +--- +{{- if and .Values.serviceAccount.create .Values.rbac.create }} +kind: ClusterRoleBinding +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: {{ include "dind-volume-provisioner.fullname" . }} + labels: + {{- include "dind-volume-provisioner.labels" . | nindent 4 }} +subjects: + - kind: ServiceAccount + name: {{ include "dind-volume-provisioner.serviceAccountName" . }} + namespace: {{ .Release.Namespace }} +roleRef: + kind: ClusterRole + name: {{ include "dind-volume-provisioner.fullname" . }} + apiGroup: rbac.authorization.k8s.io +{{- end }} +{{- end -}} diff --git a/charts/codefresh/cf-runtime/7.5.5/templates/_components/volume-provisioner/_secret.yaml b/charts/codefresh/cf-runtime/7.5.5/templates/_components/volume-provisioner/_secret.yaml new file mode 100644 index 0000000000..f361a79910 --- /dev/null +++ b/charts/codefresh/cf-runtime/7.5.5/templates/_components/volume-provisioner/_secret.yaml @@ -0,0 +1,22 @@ +{{- define "dind-volume-provisioner.resources.secret" -}} +{{- if or .Values.storage.ebs.accessKeyId .Values.storage.ebs.secretAccessKey .Values.storage.gcedisk.serviceAccountJson }} +apiVersion: v1 +kind: Secret +type: Opaque +metadata: + name: {{ include "dind-volume-provisioner.fullname" . }} + labels: + {{- include "dind-volume-provisioner.labels" . | nindent 4 }} +stringData: + {{- with .Values.storage.gcedisk.serviceAccountJson }} + google-service-account.json: | +{{- . | nindent 4 }} + {{- end }} + {{- with .Values.storage.ebs.accessKeyId }} + aws_access_key_id: {{ . }} + {{- end }} + {{- with .Values.storage.ebs.secretAccessKey }} + aws_secret_access_key: {{ . }} + {{- end }} +{{- end }} +{{- end -}} diff --git a/charts/codefresh/cf-runtime/7.5.5/templates/_components/volume-provisioner/_storageclass.yaml b/charts/codefresh/cf-runtime/7.5.5/templates/_components/volume-provisioner/_storageclass.yaml new file mode 100644 index 0000000000..62e910c87e --- /dev/null +++ b/charts/codefresh/cf-runtime/7.5.5/templates/_components/volume-provisioner/_storageclass.yaml @@ -0,0 +1,47 @@ +{{- define "dind-volume-provisioner.resources.storageclass" -}} +kind: StorageClass +apiVersion: storage.k8s.io/v1 +metadata: + {{/* has to be exactly that */}} + name: {{ include "dind-volume-provisioner.storageClassName" . }} + labels: + {{- include "dind-volume-provisioner.labels" . | nindent 4 }} +provisioner: {{ include "dind-volume-provisioner.volumeProvisionerName" . }} +parameters: +{{- if eq .Values.storage.backend "local" }} + volumeBackend: local + volumeParentDir: {{ .Values.storage.local.volumeParentDir }} +{{- else if eq .Values.storage.backend "gcedisk" }} + volumeBackend: {{ .Values.storage.backend }} + type: {{ .Values.storage.gcedisk.volumeType | default "pd-ssd" }} + zone: {{ required ".Values.storage.gcedisk.availabilityZone is required" .Values.storage.gcedisk.availabilityZone }} + fsType: {{ .Values.storage.fsType | default "ext4" }} +{{- else if or (eq .Values.storage.backend "ebs") (eq .Values.storage.backend "ebs-csi")}} + volumeBackend: {{ .Values.storage.backend }} + VolumeType: {{ .Values.storage.ebs.volumeType | default "gp3" }} + AvailabilityZone: {{ required ".Values.storage.ebs.availabilityZone is required" .Values.storage.ebs.availabilityZone }} + fsType: {{ .Values.storage.fsType | default "ext4" }} + encrypted: {{ .Values.storage.ebs.encrypted | default "false" | quote }} + {{- with .Values.storage.ebs.kmsKeyId }} + kmsKeyId: {{ . | quote }} + {{- end }} + {{- with .Values.storage.ebs.iops }} + iops: {{ . | quote }} + {{- end }} + {{- with .Values.storage.ebs.throughput }} + throughput: {{ . | quote }} + {{- end }} +{{- else if or (eq .Values.storage.backend "azuredisk") (eq .Values.storage.backend "azuredisk-csi")}} + volumeBackend: {{ .Values.storage.backend }} + kind: managed + skuName: {{ .Values.storage.azuredisk.skuName | default "Premium_LRS" }} + fsType: {{ .Values.storage.fsType | default "ext4" }} + cachingMode: {{ .Values.storage.azuredisk.cachingMode | default "None" }} + {{- with .Values.storage.azuredisk.availabilityZone }} + availabilityZone: {{ . | quote }} + {{- end }} + {{- with .Values.storage.azuredisk.resourceGroup }} + resourceGroup: {{ . | quote }} + {{- end }} +{{- end }} +{{- end -}} \ No newline at end of file diff --git a/charts/codefresh/cf-runtime/7.5.5/templates/_helpers.tpl b/charts/codefresh/cf-runtime/7.5.5/templates/_helpers.tpl new file mode 100644 index 0000000000..72f44e36af --- /dev/null +++ b/charts/codefresh/cf-runtime/7.5.5/templates/_helpers.tpl @@ -0,0 +1,51 @@ +{{/* +Expand the name of the chart. +*/}} +{{- define "cf-runtime.name" -}} +{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Create a default fully qualified app name. +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +If release name contains chart name it will be used as a full name. +*/}} +{{- define "cf-runtime.fullname" -}} +{{- if .Values.fullnameOverride }} +{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- $name := default .Chart.Name .Values.nameOverride }} +{{- if contains $name .Release.Name }} +{{- .Release.Name | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }} +{{- end }} +{{- end }} +{{- end }} + +{{/* +Create chart name and version as used by the chart label. +*/}} +{{- define "cf-runtime.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Common labels +*/}} +{{- define "cf-runtime.labels" -}} +helm.sh/chart: {{ include "cf-runtime.chart" . }} +{{ include "cf-runtime.selectorLabels" . }} +{{- if .Chart.AppVersion }} +app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} +{{- end }} +app.kubernetes.io/managed-by: {{ .Release.Service }} +{{- end }} + +{{/* +Selector labels +*/}} +{{- define "cf-runtime.selectorLabels" -}} +app.kubernetes.io/name: {{ include "cf-runtime.name" . }} +app.kubernetes.io/instance: {{ .Release.Name }} +{{- end }} diff --git a/charts/codefresh/cf-runtime/7.5.5/templates/app-proxy/deployment.yaml b/charts/codefresh/cf-runtime/7.5.5/templates/app-proxy/deployment.yaml new file mode 100644 index 0000000000..90341b3059 --- /dev/null +++ b/charts/codefresh/cf-runtime/7.5.5/templates/app-proxy/deployment.yaml @@ -0,0 +1,9 @@ +{{- $appProxyContext := deepCopy . }} +{{- $_ := set $appProxyContext "Values" (get .Values "appProxy") }} +{{- $_ := set $appProxyContext.Values "global" (get .Values "global") }} +{{- $_ := set $appProxyContext.Values "nameOverride" (get .Values "nameOverride") }} +{{- $_ := set $appProxyContext.Values "fullnameOverride" (get .Values "fullnameOverride") }} + +{{- if $appProxyContext.Values.enabled }} +{{- include "app-proxy.resources.deployment" $appProxyContext }} +{{- end }} diff --git a/charts/codefresh/cf-runtime/7.5.5/templates/app-proxy/ingress.yaml b/charts/codefresh/cf-runtime/7.5.5/templates/app-proxy/ingress.yaml new file mode 100644 index 0000000000..56ab5e95ea --- /dev/null +++ b/charts/codefresh/cf-runtime/7.5.5/templates/app-proxy/ingress.yaml @@ -0,0 +1,9 @@ +{{- $appProxyContext := deepCopy . }} +{{- $_ := set $appProxyContext "Values" (get .Values "appProxy") }} +{{- $_ := set $appProxyContext.Values "global" (get .Values "global") }} +{{- $_ := set $appProxyContext.Values "nameOverride" (get .Values "nameOverride") }} +{{- $_ := set $appProxyContext.Values "fullnameOverride" (get .Values "fullnameOverride") }} + +{{- if $appProxyContext.Values.enabled }} +{{- include "app-proxy.resources.ingress" $appProxyContext }} +{{- end }} diff --git a/charts/codefresh/cf-runtime/7.5.5/templates/app-proxy/rbac.yaml b/charts/codefresh/cf-runtime/7.5.5/templates/app-proxy/rbac.yaml new file mode 100644 index 0000000000..4db87dcb45 --- /dev/null +++ b/charts/codefresh/cf-runtime/7.5.5/templates/app-proxy/rbac.yaml @@ -0,0 +1,9 @@ +{{- $appProxyContext := deepCopy . }} +{{- $_ := set $appProxyContext "Values" (get .Values "appProxy") }} +{{- $_ := set $appProxyContext.Values "global" (get .Values "global") }} +{{- $_ := set $appProxyContext.Values "nameOverride" (get .Values "nameOverride") }} +{{- $_ := set $appProxyContext.Values "fullnameOverride" (get .Values "fullnameOverride") }} + +{{- if $appProxyContext.Values.enabled }} +{{- include "app-proxy.resources.rbac" $appProxyContext }} +{{- end }} diff --git a/charts/codefresh/cf-runtime/7.5.5/templates/app-proxy/service.yaml b/charts/codefresh/cf-runtime/7.5.5/templates/app-proxy/service.yaml new file mode 100644 index 0000000000..0b9d85ec0d --- /dev/null +++ b/charts/codefresh/cf-runtime/7.5.5/templates/app-proxy/service.yaml @@ -0,0 +1,9 @@ +{{- $appProxyContext := deepCopy . }} +{{- $_ := set $appProxyContext "Values" (get .Values "appProxy") }} +{{- $_ := set $appProxyContext.Values "global" (get .Values "global") }} +{{- $_ := set $appProxyContext.Values "nameOverride" (get .Values "nameOverride") }} +{{- $_ := set $appProxyContext.Values "fullnameOverride" (get .Values "fullnameOverride") }} + +{{- if $appProxyContext.Values.enabled }} +{{- include "app-proxy.resources.service" $appProxyContext }} +{{- end }} diff --git a/charts/codefresh/cf-runtime/7.5.5/templates/event-exporter/deployment.yaml b/charts/codefresh/cf-runtime/7.5.5/templates/event-exporter/deployment.yaml new file mode 100644 index 0000000000..4942882407 --- /dev/null +++ b/charts/codefresh/cf-runtime/7.5.5/templates/event-exporter/deployment.yaml @@ -0,0 +1,9 @@ +{{- $eventExporterContext := deepCopy . }} +{{- $_ := set $eventExporterContext "Values" (get .Values "event-exporter") }} +{{- $_ := set $eventExporterContext.Values "global" (get .Values "global") }} +{{- $_ := set $eventExporterContext.Values "nameOverride" (get .Values "nameOverride") }} +{{- $_ := set $eventExporterContext.Values "fullnameOverride" (get .Values "fullnameOverride") }} + +{{- if and $eventExporterContext.Values.enabled }} +{{- include "event-exporter.resources.deployment" $eventExporterContext }} +{{- end }} diff --git a/charts/codefresh/cf-runtime/7.5.5/templates/event-exporter/rbac.yaml b/charts/codefresh/cf-runtime/7.5.5/templates/event-exporter/rbac.yaml new file mode 100644 index 0000000000..6a9bf5c65a --- /dev/null +++ b/charts/codefresh/cf-runtime/7.5.5/templates/event-exporter/rbac.yaml @@ -0,0 +1,9 @@ +{{- $eventExporterContext := deepCopy . }} +{{- $_ := set $eventExporterContext "Values" (get .Values "event-exporter") }} +{{- $_ := set $eventExporterContext.Values "global" (get .Values "global") }} +{{- $_ := set $eventExporterContext.Values "nameOverride" (get .Values "nameOverride") }} +{{- $_ := set $eventExporterContext.Values "fullnameOverride" (get .Values "fullnameOverride") }} + +{{- if and $eventExporterContext.Values.enabled }} +{{- include "event-exporter.resources.rbac" $eventExporterContext }} +{{- end }} diff --git a/charts/codefresh/cf-runtime/7.5.5/templates/event-exporter/service.yaml b/charts/codefresh/cf-runtime/7.5.5/templates/event-exporter/service.yaml new file mode 100644 index 0000000000..c5d856dfe3 --- /dev/null +++ b/charts/codefresh/cf-runtime/7.5.5/templates/event-exporter/service.yaml @@ -0,0 +1,11 @@ +{{- $eventExporterContext := deepCopy . }} +{{- $_ := set $eventExporterContext "Values" (get .Values "event-exporter") }} +{{- $_ := set $eventExporterContext.Values "global" (get .Values "global") }} +{{- $_ := set $eventExporterContext.Values "nameOverride" (get .Values "nameOverride") }} +{{- $_ := set $eventExporterContext.Values "fullnameOverride" (get .Values "fullnameOverride") }} + +{{- if $eventExporterContext.Values.enabled }} +{{- include "event-exporter.resources.service" $eventExporterContext }} +--- +{{- include "event-exporter.resources.serviceMonitor" $eventExporterContext }} +{{- end }} diff --git a/charts/codefresh/cf-runtime/7.5.5/templates/extra/extra-resources.yaml b/charts/codefresh/cf-runtime/7.5.5/templates/extra/extra-resources.yaml new file mode 100644 index 0000000000..1a9777c649 --- /dev/null +++ b/charts/codefresh/cf-runtime/7.5.5/templates/extra/extra-resources.yaml @@ -0,0 +1,6 @@ +{{ $cfCommonTplSemver := printf "cf-common-%s" (index .Subcharts "cf-common").Chart.Version }} + +{{- range .Values.extraResources }} +--- +{{ include (printf "%s.tplrender" $cfCommonTplSemver) (dict "Values" . "context" $) }} +{{- end }} \ No newline at end of file diff --git a/charts/codefresh/cf-runtime/7.5.5/templates/extra/runtime-images-cm.yaml b/charts/codefresh/cf-runtime/7.5.5/templates/extra/runtime-images-cm.yaml new file mode 100644 index 0000000000..f269c84b2b --- /dev/null +++ b/charts/codefresh/cf-runtime/7.5.5/templates/extra/runtime-images-cm.yaml @@ -0,0 +1,19 @@ +{{ $cfCommonTplSemver := printf "cf-common-%s" (index .Subcharts "cf-common").Chart.Version }} +{{ $values := .Values.runtime.engine.runtimeImages }} +--- +kind: ConfigMap +apiVersion: v1 +metadata: + {{- /* dummy template just to list runtime images */}} + name: {{ include "runtime.fullname" . }}-images + labels: + {{- include "runtime.labels" . | nindent 4 }} + annotations: + {{- with $values.annotations }} + {{- toYaml . | nindent 4 }} + {{- end }} +data: + images: | + {{- range $key, $val := $values }} + image: {{ $val }} + {{- end }} diff --git a/charts/codefresh/cf-runtime/7.5.5/templates/hooks/post-install/cm-update-runtime.yaml b/charts/codefresh/cf-runtime/7.5.5/templates/hooks/post-install/cm-update-runtime.yaml new file mode 100644 index 0000000000..46a306c560 --- /dev/null +++ b/charts/codefresh/cf-runtime/7.5.5/templates/hooks/post-install/cm-update-runtime.yaml @@ -0,0 +1,18 @@ +{{ $cfCommonTplSemver := printf "cf-common-%s" (index .Subcharts "cf-common").Chart.Version }} +{{ $values := .Values.runtime.patch }} +{{- if $values.enabled }} +--- +kind: ConfigMap +apiVersion: v1 +metadata: + name: {{ include "runtime.fullname" . }}-spec + labels: + {{- include "runtime.labels" . | nindent 4 }} + annotations: + {{- with $values.annotations }} + {{- toYaml . | nindent 4 }} + {{- end }} +data: + runtime.yaml: | + {{ include "runtime.runtime-environment-spec.template" . | nindent 4 | trim }} +{{- end }} diff --git a/charts/codefresh/cf-runtime/7.5.5/templates/hooks/post-install/job-gencerts-dind.yaml b/charts/codefresh/cf-runtime/7.5.5/templates/hooks/post-install/job-gencerts-dind.yaml new file mode 100644 index 0000000000..4a08a229c8 --- /dev/null +++ b/charts/codefresh/cf-runtime/7.5.5/templates/hooks/post-install/job-gencerts-dind.yaml @@ -0,0 +1,68 @@ +{{ $cfCommonTplSemver := printf "cf-common-%s" (index .Subcharts "cf-common").Chart.Version }} +{{ $values := .Values.runtime.gencerts }} +{{- if and $values.enabled }} +--- +apiVersion: batch/v1 +kind: Job +metadata: + name: {{ include "runtime.fullname" . }}-gencerts-dind + labels: + {{- include "runtime.labels" . | nindent 4 }} + annotations: + helm.sh/hook: post-install,post-upgrade + helm.sh/hook-weight: "3" + helm.sh/hook-delete-policy: before-hook-creation,hook-succeeded + {{- with $values.annotations }} + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + {{- with $values.ttlSecondsAfterFinished }} + ttlSecondsAfterFinished: {{ . }} + {{- end }} + {{- with $values.backoffLimit }} + backoffLimit: {{ . | int }} + {{- end }} + template: + metadata: + name: {{ include "runtime.fullname" . }}-gencerts-dind + labels: + {{- include "runtime.labels" . | nindent 8 }} + spec: + {{- if $values.rbac.enabled }} + serviceAccountName: {{ template "runtime.fullname" . }}-gencerts-dind + {{- end }} + securityContext: + {{- toYaml $values.podSecurityContext | nindent 8 }} + containers: + - name: gencerts-dind + image: {{ include (printf "%s.image.name" $cfCommonTplSemver ) (dict "image" $values.image "context" .) }} + imagePullPolicy: {{ $values.image.pullPolicy | default "Always" }} + command: + - "/bin/bash" + args: + - -ec + - | {{ .Files.Get "files/configure-dind-certs.sh" | nindent 10 }} + env: + - name: NAMESPACE + value: {{ .Release.Namespace }} + - name: RELEASE + value: {{ .Release.Name }} + - name: CF_API_HOST + value: {{ include "runtime.runtime-environment-spec.codefresh-host" . }} + - name: CF_API_TOKEN + {{- include "runtime.installation-token-env-var-value" . | indent 10}} + {{- include (printf "%s.env-vars" $cfCommonTplSemver) (dict "Values" $values.env "context" .) | nindent 8 }} + {{- with $values.nodeSelector }} + nodeSelector: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with $values.affinity }} + affinity: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with $values.tolerations }} + tolerations: + {{- toYaml . | nindent 6 }} + {{- end }} + restartPolicy: OnFailure +{{- end }} diff --git a/charts/codefresh/cf-runtime/7.5.5/templates/hooks/post-install/job-update-runtime.yaml b/charts/codefresh/cf-runtime/7.5.5/templates/hooks/post-install/job-update-runtime.yaml new file mode 100644 index 0000000000..955e882d77 --- /dev/null +++ b/charts/codefresh/cf-runtime/7.5.5/templates/hooks/post-install/job-update-runtime.yaml @@ -0,0 +1,77 @@ +{{ $cfCommonTplSemver := printf "cf-common-%s" (index .Subcharts "cf-common").Chart.Version }} +{{ $values := .Values.runtime.patch }} +{{- if $values.enabled }} +--- +apiVersion: batch/v1 +kind: Job +metadata: + name: {{ include "runtime.fullname" . }}-patch + labels: + {{- include "runtime.labels" . | nindent 4 }} + annotations: + helm.sh/hook: post-install,post-upgrade + helm.sh/hook-weight: "5" + helm.sh/hook-delete-policy: before-hook-creation,hook-succeeded + {{- with $values.annotations }} + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + {{- with $values.ttlSecondsAfterFinished }} + ttlSecondsAfterFinished: {{ . }} + {{- end }} + {{- with $values.backoffLimit }} + backoffLimit: {{ . | int }} + {{- end }} + template: + metadata: + name: {{ include "runtime.fullname" . }}-patch + labels: + {{- include "runtime.labels" . | nindent 8 }} + spec: + securityContext: + {{- toYaml $values.podSecurityContext | nindent 8 }} + containers: + - name: patch-runtime + image: {{ include (printf "%s.image.name" $cfCommonTplSemver ) (dict "image" $values.image "context" .) }} + imagePullPolicy: {{ $values.image.pullPolicy | default "Always" }} + command: + - "/bin/bash" + args: + - -ec + - | + codefresh auth create-context --api-key $API_KEY --url $API_HOST + cat /usr/share/extras/runtime.yaml + codefresh get re +{{- if .Values.runtime.agent }} + codefresh patch re -f /usr/share/extras/runtime.yaml +{{- else }} + codefresh patch sys-re -f /usr/share/extras/runtime.yaml +{{- end }} + env: + - name: API_KEY + {{- include "runtime.installation-token-env-var-value" . | indent 10}} + - name: API_HOST + value: {{ include "runtime.runtime-environment-spec.codefresh-host" . }} + {{- include (printf "%s.env-vars" $cfCommonTplSemver) (dict "Values" $values.env "context" .) | nindent 8 }} + volumeMounts: + - name: config + mountPath: /usr/share/extras/runtime.yaml + subPath: runtime.yaml + {{- with $values.nodeSelector }} + nodeSelector: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with $values.affinity }} + affinity: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with $values.tolerations }} + tolerations: + {{- toYaml . | nindent 6 }} + {{- end }} + restartPolicy: OnFailure + volumes: + - name: config + configMap: + name: {{ include "runtime.fullname" . }}-spec +{{- end }} diff --git a/charts/codefresh/cf-runtime/7.5.5/templates/hooks/post-install/rbac-gencerts-dind.yaml b/charts/codefresh/cf-runtime/7.5.5/templates/hooks/post-install/rbac-gencerts-dind.yaml new file mode 100644 index 0000000000..4907dac380 --- /dev/null +++ b/charts/codefresh/cf-runtime/7.5.5/templates/hooks/post-install/rbac-gencerts-dind.yaml @@ -0,0 +1,37 @@ +{{ $cfCommonTplSemver := printf "cf-common-%s" (index .Subcharts "cf-common").Chart.Version }} +{{ $values := .Values.runtime.gencerts }} +{{- if and $values.enabled }} +--- +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ include "runtime.fullname" . }}-gencerts-dind + namespace: {{ .Release.Namespace }} +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: {{ include "runtime.fullname" . }}-gencerts-dind + namespace: {{ .Release.Namespace }} +rules: + - apiGroups: + - "" + resources: + - secrets + - configmaps + verbs: ["get", "list", "watch", "create", "update", "patch", "delete"] +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: {{ include "runtime.fullname" . }}-gencerts-dind + namespace: {{ .Release.Namespace }} +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: {{ include "runtime.fullname" . }}-gencerts-dind +subjects: + - kind: ServiceAccount + name: {{ include "runtime.fullname" . }}-gencerts-dind + namespace: {{ .Release.Namespace }} +{{ end }} \ No newline at end of file diff --git a/charts/codefresh/cf-runtime/7.5.5/templates/hooks/pre-delete/job-cleanup-resources.yaml b/charts/codefresh/cf-runtime/7.5.5/templates/hooks/pre-delete/job-cleanup-resources.yaml new file mode 100644 index 0000000000..0e3c7659f1 --- /dev/null +++ b/charts/codefresh/cf-runtime/7.5.5/templates/hooks/pre-delete/job-cleanup-resources.yaml @@ -0,0 +1,73 @@ +{{ $cfCommonTplSemver := printf "cf-common-%s" (index .Subcharts "cf-common").Chart.Version }} +{{ $values := .Values.runtime.patch }} +{{- if and $values.enabled }} +--- +apiVersion: batch/v1 +kind: Job +metadata: + name: {{ include "runtime.fullname" . }}-cleanup + labels: + {{- include "runtime.labels" . | nindent 4 }} + annotations: + helm.sh/hook: pre-delete + helm.sh/hook-delete-policy: hook-succeeded,before-hook-creation + {{- with $values.annotations }} + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + {{- with $values.ttlSecondsAfterFinished }} + ttlSecondsAfterFinished: {{ . }} + {{- end }} + {{- with $values.backoffLimit }} + backoffLimit: {{ . | int }} + {{- end }} + template: + metadata: + name: {{ include "runtime.fullname" . }}-cleanup + labels: + {{- include "runtime.labels" . | nindent 8 }} + spec: + {{- if $values.rbac.enabled }} + serviceAccountName: {{ template "runtime.fullname" . }}-cleanup + {{- end }} + securityContext: + {{- toYaml $values.podSecurityContext | nindent 8 }} + containers: + - name: cleanup + image: {{ include (printf "%s.image.name" $cfCommonTplSemver ) (dict "image" $values.image "context" .) }} + imagePullPolicy: {{ $values.image.pullPolicy | default "Always" }} + command: + - "/bin/bash" + args: + - -ec + - | {{ .Files.Get "files/cleanup-runtime.sh" | nindent 10 }} + env: + - name: AGENT_NAME + value: {{ include "runtime.runtime-environment-spec.agent-name" . }} + - name: RUNTIME_NAME + value: {{ include "runtime.runtime-environment-spec.runtime-name" . }} + - name: API_HOST + value: {{ include "runtime.runtime-environment-spec.codefresh-host" . }} + - name: API_TOKEN + {{- include "runtime.installation-token-env-var-value" . | indent 10}} + - name: AGENT + value: {{ .Values.runtime.agent | quote }} + - name: AGENT_SECRET_NAME + value: {{ include "runner.fullname" . }} + - name: DIND_SECRET_NAME + value: codefresh-certs-server + {{- include (printf "%s.env-vars" $cfCommonTplSemver) (dict "Values" $values.env "context" .) | nindent 8 }} + {{- with $values.nodeSelector }} + nodeSelector: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with $values.affinity }} + affinity: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with $values.tolerations }} + tolerations: + {{- toYaml . | nindent 6 }} + {{- end }} + restartPolicy: OnFailure +{{- end }} diff --git a/charts/codefresh/cf-runtime/7.5.5/templates/hooks/pre-delete/rbac-cleanup-resources.yaml b/charts/codefresh/cf-runtime/7.5.5/templates/hooks/pre-delete/rbac-cleanup-resources.yaml new file mode 100644 index 0000000000..468ec2212d --- /dev/null +++ b/charts/codefresh/cf-runtime/7.5.5/templates/hooks/pre-delete/rbac-cleanup-resources.yaml @@ -0,0 +1,46 @@ +{{ $cfCommonTplSemver := printf "cf-common-%s" (index .Subcharts "cf-common").Chart.Version }} +{{ $values := .Values.runtime.patch }} +{{- if and $values.enabled }} +--- +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ include "runtime.fullname" . }}-cleanup + namespace: {{ .Release.Namespace }} + annotations: + "helm.sh/hook": pre-delete + "helm.sh/hook-delete-policy": hook-succeeded,before-hook-creation,hook-failed +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: {{ include "runtime.fullname" . }}-cleanup + namespace: {{ .Release.Namespace }} + annotations: + "helm.sh/hook": pre-delete + "helm.sh/hook-delete-policy": hook-succeeded,before-hook-creation,hook-failed +rules: + - apiGroups: + - "*" + resources: + - "*" + verbs: + - "*" +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: {{ include "runtime.fullname" . }}-cleanup + namespace: {{ .Release.Namespace }} + annotations: + "helm.sh/hook": pre-delete + "helm.sh/hook-delete-policy": hook-succeeded,before-hook-creation,hook-failed +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: {{ include "runtime.fullname" . }}-cleanup +subjects: + - kind: ServiceAccount + name: {{ include "runtime.fullname" . }}-cleanup + namespace: {{ .Release.Namespace }} +{{ end }} \ No newline at end of file diff --git a/charts/codefresh/cf-runtime/7.5.5/templates/monitor/deployment.yaml b/charts/codefresh/cf-runtime/7.5.5/templates/monitor/deployment.yaml new file mode 100644 index 0000000000..00c9fb2f91 --- /dev/null +++ b/charts/codefresh/cf-runtime/7.5.5/templates/monitor/deployment.yaml @@ -0,0 +1,9 @@ +{{- $monitorContext := deepCopy . }} +{{- $_ := set $monitorContext "Values" (get .Values "monitor") }} +{{- $_ := set $monitorContext.Values "global" (get .Values "global") }} +{{- $_ := set $monitorContext.Values "nameOverride" (get .Values "nameOverride") }} +{{- $_ := set $monitorContext.Values "fullnameOverride" (get .Values "fullnameOverride") }} + +{{- if $monitorContext.Values.enabled }} +{{- include "monitor.resources.deployment" $monitorContext }} +{{- end }} diff --git a/charts/codefresh/cf-runtime/7.5.5/templates/monitor/rbac.yaml b/charts/codefresh/cf-runtime/7.5.5/templates/monitor/rbac.yaml new file mode 100644 index 0000000000..f9812d565d --- /dev/null +++ b/charts/codefresh/cf-runtime/7.5.5/templates/monitor/rbac.yaml @@ -0,0 +1,9 @@ +{{- $monitorContext := deepCopy . }} +{{- $_ := set $monitorContext "Values" (get .Values "monitor") }} +{{- $_ := set $monitorContext.Values "global" (get .Values "global") }} +{{- $_ := set $monitorContext.Values "nameOverride" (get .Values "nameOverride") }} +{{- $_ := set $monitorContext.Values "fullnameOverride" (get .Values "fullnameOverride") }} + +{{- if $monitorContext.Values.enabled }} +{{- include "monitor.resources.rbac" $monitorContext }} +{{- end }} diff --git a/charts/codefresh/cf-runtime/7.5.5/templates/monitor/service.yaml b/charts/codefresh/cf-runtime/7.5.5/templates/monitor/service.yaml new file mode 100644 index 0000000000..f99706614a --- /dev/null +++ b/charts/codefresh/cf-runtime/7.5.5/templates/monitor/service.yaml @@ -0,0 +1,9 @@ +{{- $monitorContext := deepCopy . }} +{{- $_ := set $monitorContext "Values" (get .Values "monitor") }} +{{- $_ := set $monitorContext.Values "global" (get .Values "global") }} +{{- $_ := set $monitorContext.Values "nameOverride" (get .Values "nameOverride") }} +{{- $_ := set $monitorContext.Values "fullnameOverride" (get .Values "fullnameOverride") }} + +{{- if $monitorContext.Values.enabled }} +{{- include "monitor.resources.service" $monitorContext }} +{{- end }} diff --git a/charts/codefresh/cf-runtime/7.5.5/templates/other/external-secrets.yaml b/charts/codefresh/cf-runtime/7.5.5/templates/other/external-secrets.yaml new file mode 100644 index 0000000000..dc24e24e51 --- /dev/null +++ b/charts/codefresh/cf-runtime/7.5.5/templates/other/external-secrets.yaml @@ -0,0 +1,2 @@ +{{ $templateName := printf "cf-common-%s.external-secrets" (index .Subcharts "cf-common").Chart.Version }} +{{- include $templateName . -}} diff --git a/charts/codefresh/cf-runtime/7.5.5/templates/other/podMonitor.yaml b/charts/codefresh/cf-runtime/7.5.5/templates/other/podMonitor.yaml new file mode 100644 index 0000000000..4319b722b9 --- /dev/null +++ b/charts/codefresh/cf-runtime/7.5.5/templates/other/podMonitor.yaml @@ -0,0 +1,2 @@ +{{ $templateName := printf "cf-common-%s.podMonitor" (index .Subcharts "cf-common").Chart.Version }} +{{- include $templateName . -}} diff --git a/charts/codefresh/cf-runtime/7.5.5/templates/other/serviceMonitor.yaml b/charts/codefresh/cf-runtime/7.5.5/templates/other/serviceMonitor.yaml new file mode 100644 index 0000000000..29f890fe2b --- /dev/null +++ b/charts/codefresh/cf-runtime/7.5.5/templates/other/serviceMonitor.yaml @@ -0,0 +1,2 @@ +{{ $templateName := printf "cf-common-%s.serviceMonitor" (index .Subcharts "cf-common").Chart.Version }} +{{- include $templateName . -}} diff --git a/charts/codefresh/cf-runtime/7.5.5/templates/runner/deployment.yaml b/charts/codefresh/cf-runtime/7.5.5/templates/runner/deployment.yaml new file mode 100644 index 0000000000..85777c487f --- /dev/null +++ b/charts/codefresh/cf-runtime/7.5.5/templates/runner/deployment.yaml @@ -0,0 +1,9 @@ +{{- $runnerContext := deepCopy . }} +{{- $_ := set $runnerContext "Values" (get .Values "runner") }} +{{- $_ := set $runnerContext.Values "global" (get .Values "global") }} +{{- $_ := set $runnerContext.Values "nameOverride" (get .Values "nameOverride") }} +{{- $_ := set $runnerContext.Values "fullnameOverride" (get .Values "fullnameOverride") }} + +{{- if and $runnerContext.Values.enabled .Values.runtime.agent }} +{{- include "runner.resources.deployment" $runnerContext }} +{{- end }} diff --git a/charts/codefresh/cf-runtime/7.5.5/templates/runner/rbac.yaml b/charts/codefresh/cf-runtime/7.5.5/templates/runner/rbac.yaml new file mode 100644 index 0000000000..d5f8c13233 --- /dev/null +++ b/charts/codefresh/cf-runtime/7.5.5/templates/runner/rbac.yaml @@ -0,0 +1,9 @@ +{{- $runnerContext := deepCopy . }} +{{- $_ := set $runnerContext "Values" (get .Values "runner") }} +{{- $_ := set $runnerContext.Values "global" (get .Values "global") }} +{{- $_ := set $runnerContext.Values "nameOverride" (get .Values "nameOverride") }} +{{- $_ := set $runnerContext.Values "fullnameOverride" (get .Values "fullnameOverride") }} + +{{- if and $runnerContext.Values.enabled .Values.runtime.agent }} +{{- include "runner.resources.rbac" $runnerContext }} +{{- end }} diff --git a/charts/codefresh/cf-runtime/7.5.5/templates/runtime/_helpers.tpl b/charts/codefresh/cf-runtime/7.5.5/templates/runtime/_helpers.tpl new file mode 100644 index 0000000000..6ba04fcc3e --- /dev/null +++ b/charts/codefresh/cf-runtime/7.5.5/templates/runtime/_helpers.tpl @@ -0,0 +1,123 @@ +{{/* +Expand the name of the chart. +*/}} +{{- define "runtime.name" -}} + {{- printf "%s" (include "cf-runtime.name" .) | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Create a default fully qualified app name. +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +If release name contains chart name it will be used as a full name. +*/}} +{{- define "runtime.fullname" -}} + {{- printf "%s" (include "cf-runtime.fullname" .) | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Common labels +*/}} +{{- define "runtime.labels" -}} +{{ include "cf-runtime.labels" . }} +codefresh.io/application: runtime +{{- end }} + +{{/* +Selector labels +*/}} +{{- define "runtime.selectorLabels" -}} +{{ include "cf-runtime.selectorLabels" . }} +codefresh.io/application: runtime +{{- end }} + +{{/* +Return runtime image (classic runtime) with private registry prefix +*/}} +{{- define "runtime.runtimeImageName" -}} + {{- if .registry -}} + {{- $imageName := (trimPrefix "quay.io/" .imageFullName) -}} + {{- printf "%s/%s" .registry $imageName -}} + {{- else -}} + {{- printf "%s" .imageFullName -}} + {{- end -}} +{{- end -}} + +{{/* +Environment variable value of Codefresh installation token +*/}} +{{- define "runtime.installation-token-env-var-value" -}} + {{- if .Values.global.codefreshToken }} +valueFrom: + secretKeyRef: + name: {{ include "runtime.installation-token-secret-name" . }} + key: codefresh-api-token + {{- else if .Values.global.codefreshTokenSecretKeyRef }} +valueFrom: + secretKeyRef: + {{- .Values.global.codefreshTokenSecretKeyRef | toYaml | nindent 4 }} + {{- end }} +{{- end }} + +{{/* +Environment variable value of Codefresh agent token +*/}} +{{- define "runtime.agent-token-env-var-value" -}} + {{- if .Values.global.agentToken }} +{{- printf "%s" .Values.global.agentToken | toYaml }} + {{- else if .Values.global.agentTokenSecretKeyRef }} +valueFrom: + secretKeyRef: + {{- .Values.global.agentTokenSecretKeyRef | toYaml | nindent 4 }} + {{- end }} +{{- end }} + +{{/* +Print Codefresh API token secret name +*/}} +{{- define "runtime.installation-token-secret-name" }} +{{- print "codefresh-user-token" }} +{{- end }} + +{{/* +Print Codefresh host +*/}} +{{- define "runtime.runtime-environment-spec.codefresh-host" }} +{{- if and (not .Values.global.codefreshHost) }} + {{- fail "ERROR: .global.codefreshHost is required" }} +{{- else }} + {{- printf "%s" (trimSuffix "/" .Values.global.codefreshHost) }} +{{- end }} +{{- end }} + +{{/* +Print runtime-environment name +*/}} +{{- define "runtime.runtime-environment-spec.runtime-name" }} +{{- if and (not .Values.global.runtimeName) }} + {{- printf "%s/%s" .Values.global.context .Release.Namespace }} +{{- else }} + {{- printf "%s" .Values.global.runtimeName }} +{{- end }} +{{- end }} + +{{/* +Print agent name +*/}} +{{- define "runtime.runtime-environment-spec.agent-name" }} +{{- if and (not .Values.global.agentName) }} + {{- printf "%s_%s" .Values.global.context .Release.Namespace }} +{{- else }} + {{- printf "%s" .Values.global.agentName }} +{{- end }} +{{- end }} + +{{/* +Print context +*/}} +{{- define "runtime.runtime-environment-spec.context-name" }} +{{- if and (not .Values.global.context) }} + {{- fail "ERROR: .global.context is required" }} +{{- else }} + {{- printf "%s" .Values.global.context }} +{{- end }} +{{- end }} diff --git a/charts/codefresh/cf-runtime/7.5.5/templates/runtime/cm-dind-daemon.yaml b/charts/codefresh/cf-runtime/7.5.5/templates/runtime/cm-dind-daemon.yaml new file mode 100644 index 0000000000..fc7f92905b --- /dev/null +++ b/charts/codefresh/cf-runtime/7.5.5/templates/runtime/cm-dind-daemon.yaml @@ -0,0 +1,10 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + {{- /* has to be a constant */}} + name: codefresh-dind-config + labels: + {{- include "runtime.labels" . | nindent 4 }} +data: + daemon.json: | +{{ coalesce .Values.re.dindDaemon .Values.runtime.dindDaemon | toPrettyJson | indent 4 }} diff --git a/charts/codefresh/cf-runtime/7.5.5/templates/runtime/rbac.yaml b/charts/codefresh/cf-runtime/7.5.5/templates/runtime/rbac.yaml new file mode 100644 index 0000000000..a51b125262 --- /dev/null +++ b/charts/codefresh/cf-runtime/7.5.5/templates/runtime/rbac.yaml @@ -0,0 +1,48 @@ +{{ $values := .Values.runtime }} +--- +{{- if or $values.serviceAccount.create }} +apiVersion: v1 +kind: ServiceAccount +metadata: + {{- /* has to be a constant */}} + name: codefresh-engine + labels: + {{- include "runtime.labels" . | nindent 4 }} + {{- with $values.serviceAccount.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +{{- end }} +--- +{{- if $values.rbac.create }} +kind: Role +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: codefresh-engine + labels: + {{- include "runner.labels" . | nindent 4 }} +rules: + - apiGroups: [ "" ] + resources: [ "secrets" ] + verbs: [ "get" ] +{{- with $values.rbac.rules }} + {{ toYaml . | nindent 2 }} +{{- end }} +{{- end }} +--- +{{- if and $values.serviceAccount.create $values.rbac.create }} +kind: RoleBinding +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: codefresh-engine + labels: + {{- include "runner.labels" . | nindent 4 }} +subjects: + - kind: ServiceAccount + name: codefresh-engine +roleRef: + kind: Role + name: codefresh-engine + apiGroup: rbac.authorization.k8s.io +{{- end }} + diff --git a/charts/codefresh/cf-runtime/7.5.5/templates/runtime/runtime-env-spec-tmpl.yaml b/charts/codefresh/cf-runtime/7.5.5/templates/runtime/runtime-env-spec-tmpl.yaml new file mode 100644 index 0000000000..b0d08664ef --- /dev/null +++ b/charts/codefresh/cf-runtime/7.5.5/templates/runtime/runtime-env-spec-tmpl.yaml @@ -0,0 +1,235 @@ +{{- define "runtime.runtime-environment-spec.template" }} +{{- $cfCommonTplSemver := printf "cf-common-%s" (index .Subcharts "cf-common").Chart.Version -}} +{{- $kubeconfigFilePath := (include "runtime.runtime-environment-spec.runtime-name" .) -}} +{{- $name := (include "runtime.runtime-environment-spec.runtime-name" .) -}} +{{- $engineContext := .Values.runtime.engine -}} +{{- $dindContext := .Values.runtime.dind -}} +{{- $imageRegistry := .Values.global.imageRegistry -}} +metadata: + name: {{ include "runtime.runtime-environment-spec.runtime-name" . }} + agent: {{ .Values.runtime.agent }} +runtimeScheduler: + type: KubernetesPod + {{- if $engineContext.image }} + image: {{ include (printf "%s.image.name" $cfCommonTplSemver ) (dict "image" $engineContext.image "context" .) | squote }} + {{- end }} + imagePullPolicy: {{ $engineContext.image.pullPolicy }} + {{- with $engineContext.command }} + command: {{- toYaml . | nindent 4 }} + {{- end }} + envVars: + {{- with $engineContext.env }} + {{- range $key, $val := . }} + {{ $key }}: {{ $val | squote }} + {{- end }} + {{- end }} + COMPOSE_IMAGE: {{ include "runtime.runtimeImageName" (dict "registry" $imageRegistry "imageFullName" $engineContext.runtimeImages.COMPOSE_IMAGE) | squote }} + CONTAINER_LOGGER_IMAGE: {{ include "runtime.runtimeImageName" (dict "registry" $imageRegistry "imageFullName" $engineContext.runtimeImages.CONTAINER_LOGGER_IMAGE) | squote }} + DOCKER_BUILDER_IMAGE: {{ include "runtime.runtimeImageName" (dict "registry" $imageRegistry "imageFullName" $engineContext.runtimeImages.DOCKER_BUILDER_IMAGE) | squote }} + DOCKER_PULLER_IMAGE: {{ include "runtime.runtimeImageName" (dict "registry" $imageRegistry "imageFullName" $engineContext.runtimeImages.DOCKER_PULLER_IMAGE) | squote }} + DOCKER_PUSHER_IMAGE: {{ include "runtime.runtimeImageName" (dict "registry" $imageRegistry "imageFullName" $engineContext.runtimeImages.DOCKER_PUSHER_IMAGE) | squote }} + DOCKER_TAG_PUSHER_IMAGE: {{ include "runtime.runtimeImageName" (dict "registry" $imageRegistry "imageFullName" $engineContext.runtimeImages.DOCKER_TAG_PUSHER_IMAGE) | squote }} + FS_OPS_IMAGE: {{ include "runtime.runtimeImageName" (dict "registry" $imageRegistry "imageFullName" $engineContext.runtimeImages.FS_OPS_IMAGE) | squote }} + GIT_CLONE_IMAGE: {{ include "runtime.runtimeImageName" (dict "registry" $imageRegistry "imageFullName" $engineContext.runtimeImages.GIT_CLONE_IMAGE) | squote }} + KUBE_DEPLOY: {{ include "runtime.runtimeImageName" (dict "registry" $imageRegistry "imageFullName" $engineContext.runtimeImages.KUBE_DEPLOY) | squote }} + PIPELINE_DEBUGGER_IMAGE: {{ include "runtime.runtimeImageName" (dict "registry" $imageRegistry "imageFullName" $engineContext.runtimeImages.PIPELINE_DEBUGGER_IMAGE) | squote }} + TEMPLATE_ENGINE: {{ include "runtime.runtimeImageName" (dict "registry" $imageRegistry "imageFullName" $engineContext.runtimeImages.TEMPLATE_ENGINE) | squote }} + CR_6177_FIXER: {{ include "runtime.runtimeImageName" (dict "registry" $imageRegistry "imageFullName" $engineContext.runtimeImages.CR_6177_FIXER) | squote }} + GC_BUILDER_IMAGE: {{ include "runtime.runtimeImageName" (dict "registry" $imageRegistry "imageFullName" $engineContext.runtimeImages.GC_BUILDER_IMAGE) | squote }} + COSIGN_IMAGE_SIGNER_IMAGE: {{ include "runtime.runtimeImageName" (dict "registry" $imageRegistry "imageFullName" $engineContext.runtimeImages.COSIGN_IMAGE_SIGNER_IMAGE) | squote }} + RUNTIME_CHART_VERSION: {{ .Chart.Version }} + {{- with $engineContext.userEnvVars }} + userEnvVars: {{- toYaml . | nindent 4 }} + {{- end }} + {{- with $engineContext.workflowLimits }} + workflowLimits: {{- toYaml . | nindent 4 }} + {{- end }} + cluster: + namespace: {{ .Release.Namespace }} + serviceAccount: {{ $engineContext.serviceAccount }} + {{- if .Values.runtime.agent }} + clusterProvider: + accountId: {{ .Values.global.accountId }} + selector: {{ include "runtime.runtime-environment-spec.context-name" . }} + {{- else }} + {{- if .Values.runtime.inCluster }} + inCluster: true + kubeconfigFilePath: null + {{- else }} + name: {{ $name }} + kubeconfigFilePath: {{ printf "/etc/kubeconfig/%s" $kubeconfigFilePath }} + {{- end }} + {{- end }} + {{- with $engineContext.nodeSelector }} + nodeSelector: {{- toYaml . | nindent 6 }} + {{- end }} + {{- with $engineContext.affinity }} + affinity: {{- toYaml . | nindent 4 }} + {{- end }} + {{- with $engineContext.tolerations }} + tolerations: {{- toYaml . | nindent 4 }} + {{- end }} + {{- with $engineContext.podAnnotations }} + annotations: + {{- range $key, $val := . }} + {{ $key }}: {{ $val | squote }} + {{- end }} + {{- end }} + {{- with $engineContext.podLabels }} + labels: {{- toYaml . | nindent 4 }} + {{- end }} + {{- if $engineContext.schedulerName }} + schedulerName: {{ $engineContext.schedulerName }} + {{- end }} + resources: + {{- if $engineContext.resources}} + {{- toYaml $engineContext.resources | nindent 4 }} + {{- end }} + {{- with $engineContext.terminationGracePeriodSeconds }} + terminationGracePeriodSeconds: {{ . }} + {{- end }} +dockerDaemonScheduler: + type: DindKubernetesPod + {{- if $dindContext.image }} + dindImage: {{ include (printf "%s.image.name" $cfCommonTplSemver ) (dict "image" $dindContext.image "context" .) | squote }} + {{- end }} + imagePullPolicy: {{ $dindContext.image.pullPolicy }} + {{- with $dindContext.userAccess }} + userAccess: {{ . }} + {{- end }} + {{- with $dindContext.env }} + envVars: + {{- range $key, $val := . }} + {{ $key }}: {{ $val | squote }} + {{- end }} + {{- end }} + cluster: + namespace: {{ .Release.Namespace }} + serviceAccount: {{ $dindContext.serviceAccount }} + {{- if .Values.runtime.agent }} + clusterProvider: + accountId: {{ .Values.global.accountId }} + selector: {{ include "runtime.runtime-environment-spec.context-name" . }} + {{- else }} + {{- if .Values.runtime.inCluster }} + inCluster: true + kubeconfigFilePath: null + {{- else }} + name: {{ $name }} + kubeconfigFilePath: {{ printf "/etc/kubeconfig/%s" $kubeconfigFilePath }} + {{- end }} + {{- end }} + {{- with $dindContext.nodeSelector }} + nodeSelector: {{- toYaml . | nindent 6 }} + {{- end }} + {{- with $dindContext.affinity }} + affinity: {{- toYaml . | nindent 4 }} + {{- end }} + {{- with $dindContext.tolerations }} + tolerations: {{- toYaml . | nindent 4 }} + {{- end }} + {{- with $dindContext.podAnnotations }} + annotations: + {{- range $key, $val := . }} + {{ $key }}: {{ $val | squote }} + {{- end }} + {{- end }} + {{- with $dindContext.podLabels }} + labels: {{- toYaml . | nindent 4 }} + {{- end }} + {{- if $dindContext.schedulerName }} + schedulerName: {{ $dindContext.schedulerName }} + {{- end }} + {{- if $dindContext.pvcs }} + pvcs: + {{- range $index, $pvc := $dindContext.pvcs }} + - name: {{ $pvc.name }} + reuseVolumeSelector: {{ $pvc.reuseVolumeSelector | squote }} + reuseVolumeSortOrder: {{ $pvc.reuseVolumeSortOrder }} + storageClassName: {{ include (printf "%v.tplrender" $cfCommonTplSemver) (dict "Values" $pvc.storageClassName "context" $) }} + volumeSize: {{ $pvc.volumeSize }} + {{- with $pvc.annotations }} + annotations: {{ . | toYaml | nindent 8 }} + {{- end }} + {{- end }} + {{- end }} + defaultDindResources: + {{- with $dindContext.resources }} + {{- if not .requests }} + limits: {{- toYaml .limits | nindent 6 }} + requests: null + {{- else }} + {{- toYaml . | nindent 4 }} + {{- end }} + {{- end }} + {{- with $dindContext.terminationGracePeriodSeconds }} + terminationGracePeriodSeconds: {{ . }} + {{- end }} + {{- with $dindContext.userVolumeMounts }} + userVolumeMounts: {{- toYaml . | nindent 4 }} + {{- end }} + {{- with $dindContext.userVolumes }} + userVolumes: {{- toYaml . | nindent 4 }} + {{- end }} + {{- if and (not .Values.runtime.agent) }} + clientCertPath: /etc/ssl/cf/ + volumeMounts: + codefresh-certs-server: + name: codefresh-certs-server + mountPath: /etc/ssl/cf + readOnly: false + volumes: + codefresh-certs-server: + name: codefresh-certs-server + secret: + secretName: codefresh-certs-server + {{- end }} + {{- with $dindContext.podSecurityContext }} + podSecurityContext: {{- toYaml . | nindent 4 }} + {{- end }} + {{- with $dindContext.containerSecurityContext }} + containerSecurityContext: {{- toYaml . | nindent 4 }} + {{- end }} + {{- if $dindContext.volumePermissions.enabled }} + initContainers: + - name: volume-permissions + image: {{ include (printf "%s.image.name" $cfCommonTplSemver ) (dict "image" $dindContext.volumePermissions.image "context" .) }} + imagePullPolicy: {{ $dindContext.volumePermissions.image.pullPolicy | default "Always" }} + command: + - /bin/sh + args: + - -ec + - | + chown -R {{ $dindContext.containerSecurityContext.runAsUser }}:{{ $dindContext.podSecurityContext.fsGroup }} /home/rootless/.local/share/docker + volumeMounts: + - mountPath: /home/rootless/.local/share/docker + name: dind + {{- if eq ( toString ( $dindContext.volumePermissions.securityContext.runAsUser )) "auto" }} + securityContext: {{- omit $dindContext.volumePermissions.securityContext "runAsUser" | toYaml | nindent 6 }} + {{- else }} + securityContext: {{- $dindContext.volumePermissions.securityContext | toYaml | nindent 6 }} + {{- end }} + resources: + {{- toYaml $dindContext.volumePermissions.resources | nindent 6 }} + {{- end }} +extends: {{- toYaml .Values.runtime.runtimeExtends | nindent 2 }} + {{- if .Values.runtime.description }} +description: {{ .Values.runtime.description }} + {{- else }} +description: null + {{- end }} +{{- if .Values.global.accountId }} +accountId: {{ .Values.global.accountId }} +{{- end }} +{{- if not .Values.runtime.agent }} +accounts: {{- toYaml .Values.runtime.accounts | nindent 2 }} +{{- end }} +{{- if .Values.appProxy.enabled }} +appProxy: + externalIP: >- + {{ printf "https://%s%s" .Values.appProxy.ingress.host (.Values.appProxy.ingress.pathPrefix | default "/") }} +{{- end }} +{{- if not .Values.runtime.agent }} +systemHybrid: true +{{- end }} +{{- end }} diff --git a/charts/codefresh/cf-runtime/7.5.5/templates/runtime/secret.yaml b/charts/codefresh/cf-runtime/7.5.5/templates/runtime/secret.yaml new file mode 100644 index 0000000000..2366d3ccf6 --- /dev/null +++ b/charts/codefresh/cf-runtime/7.5.5/templates/runtime/secret.yaml @@ -0,0 +1,11 @@ +{{- if and .Values.global.codefreshToken }} +apiVersion: v1 +kind: Secret +type: Opaque +metadata: + name: {{ include "runtime.installation-token-secret-name" . }} + labels: + {{- include "runtime.labels" . | nindent 4 }} +stringData: + codefresh-api-token: {{ .Values.global.codefreshToken }} +{{- end }} \ No newline at end of file diff --git a/charts/codefresh/cf-runtime/7.5.5/templates/runtime/svc-dind.yaml b/charts/codefresh/cf-runtime/7.5.5/templates/runtime/svc-dind.yaml new file mode 100644 index 0000000000..098edb4e87 --- /dev/null +++ b/charts/codefresh/cf-runtime/7.5.5/templates/runtime/svc-dind.yaml @@ -0,0 +1,16 @@ +apiVersion: v1 +kind: Service +metadata: + labels: + {{- include "runtime.labels" . | nindent 4 }} + app: dind + {{/* has to be a constant */}} + name: dind +spec: + ports: + - name: "dind-port" + port: 1300 + protocol: TCP + clusterIP: None + selector: + app: dind diff --git a/charts/codefresh/cf-runtime/7.5.5/templates/volume-provisioner/cronjob.yaml b/charts/codefresh/cf-runtime/7.5.5/templates/volume-provisioner/cronjob.yaml new file mode 100644 index 0000000000..db955bc771 --- /dev/null +++ b/charts/codefresh/cf-runtime/7.5.5/templates/volume-provisioner/cronjob.yaml @@ -0,0 +1,11 @@ +{{- $volumeProvisionerContext := deepCopy . }} +{{- $_ := set $volumeProvisionerContext "Values" (get .Values.volumeProvisioner "dind-volume-cleanup") }} +{{- $_ := set $volumeProvisionerContext.Values "serviceAccount" (get .Values.volumeProvisioner "serviceAccount") }} +{{- $_ := set $volumeProvisionerContext.Values "global" (get .Values "global") }} +{{- $_ := set $volumeProvisionerContext.Values "storage" (get .Values "storage") }} +{{- $_ := set $volumeProvisionerContext.Values "nameOverride" (get .Values "nameOverride") }} +{{- $_ := set $volumeProvisionerContext.Values "fullnameOverride" (get .Values "fullnameOverride") }} + +{{- if and $volumeProvisionerContext.Values.enabled .Values.volumeProvisioner.enabled }} +{{- include "dind-volume-provisioner.resources.cronjob" $volumeProvisionerContext }} +{{- end }} diff --git a/charts/codefresh/cf-runtime/7.5.5/templates/volume-provisioner/daemonset.yaml b/charts/codefresh/cf-runtime/7.5.5/templates/volume-provisioner/daemonset.yaml new file mode 100644 index 0000000000..39927149e8 --- /dev/null +++ b/charts/codefresh/cf-runtime/7.5.5/templates/volume-provisioner/daemonset.yaml @@ -0,0 +1,11 @@ +{{- $volumeProvisionerContext := deepCopy . }} +{{- $_ := set $volumeProvisionerContext "Values" (get .Values.volumeProvisioner "dind-lv-monitor") }} +{{- $_ := set $volumeProvisionerContext.Values "serviceAccount" (get .Values.volumeProvisioner "serviceAccount") }} +{{- $_ := set $volumeProvisionerContext.Values "global" (get .Values "global") }} +{{- $_ := set $volumeProvisionerContext.Values "storage" (get .Values "storage") }} +{{- $_ := set $volumeProvisionerContext.Values "nameOverride" (get .Values "nameOverride") }} +{{- $_ := set $volumeProvisionerContext.Values "fullnameOverride" (get .Values "fullnameOverride") }} + +{{- if and $volumeProvisionerContext.Values.enabled .Values.volumeProvisioner.enabled }} +{{- include "dind-volume-provisioner.resources.daemonset" $volumeProvisionerContext }} +{{- end }} diff --git a/charts/codefresh/cf-runtime/7.5.5/templates/volume-provisioner/deployment.yaml b/charts/codefresh/cf-runtime/7.5.5/templates/volume-provisioner/deployment.yaml new file mode 100644 index 0000000000..522fa8791f --- /dev/null +++ b/charts/codefresh/cf-runtime/7.5.5/templates/volume-provisioner/deployment.yaml @@ -0,0 +1,10 @@ +{{- $volumeProvisionerContext := deepCopy . }} +{{- $_ := set $volumeProvisionerContext "Values" (get .Values "volumeProvisioner") }} +{{- $_ := set $volumeProvisionerContext.Values "global" (get .Values "global") }} +{{- $_ := set $volumeProvisionerContext.Values "storage" (get .Values "storage") }} +{{- $_ := set $volumeProvisionerContext.Values "nameOverride" (get .Values "nameOverride") }} +{{- $_ := set $volumeProvisionerContext.Values "fullnameOverride" (get .Values "fullnameOverride") }} + +{{- if $volumeProvisionerContext.Values.enabled }} +{{- include "dind-volume-provisioner.resources.deployment" $volumeProvisionerContext }} +{{- end }} diff --git a/charts/codefresh/cf-runtime/7.5.5/templates/volume-provisioner/rbac.yaml b/charts/codefresh/cf-runtime/7.5.5/templates/volume-provisioner/rbac.yaml new file mode 100644 index 0000000000..f3ae9609f9 --- /dev/null +++ b/charts/codefresh/cf-runtime/7.5.5/templates/volume-provisioner/rbac.yaml @@ -0,0 +1,9 @@ +{{- $volumeProvisionerContext := deepCopy . }} +{{- $_ := set $volumeProvisionerContext "Values" (get .Values "volumeProvisioner") }} +{{- $_ := set $volumeProvisionerContext.Values "global" (get .Values "global") }} +{{- $_ := set $volumeProvisionerContext.Values "nameOverride" (get .Values "nameOverride") }} +{{- $_ := set $volumeProvisionerContext.Values "fullnameOverride" (get .Values "fullnameOverride") }} + +{{- if $volumeProvisionerContext.Values.enabled }} +{{- include "dind-volume-provisioner.resources.rbac" $volumeProvisionerContext }} +{{- end }} diff --git a/charts/codefresh/cf-runtime/7.5.5/templates/volume-provisioner/secret.yaml b/charts/codefresh/cf-runtime/7.5.5/templates/volume-provisioner/secret.yaml new file mode 100644 index 0000000000..accf601d13 --- /dev/null +++ b/charts/codefresh/cf-runtime/7.5.5/templates/volume-provisioner/secret.yaml @@ -0,0 +1,10 @@ +{{- $volumeProvisionerContext := deepCopy . }} +{{- $_ := set $volumeProvisionerContext "Values" (get .Values "volumeProvisioner") }} +{{- $_ := set $volumeProvisionerContext.Values "global" (get .Values "global") }} +{{- $_ := set $volumeProvisionerContext.Values "storage" (get .Values "storage") }} +{{- $_ := set $volumeProvisionerContext.Values "nameOverride" (get .Values "nameOverride") }} +{{- $_ := set $volumeProvisionerContext.Values "fullnameOverride" (get .Values "fullnameOverride") }} + +{{- if $volumeProvisionerContext.Values.enabled }} +{{- include "dind-volume-provisioner.resources.secret" $volumeProvisionerContext }} +{{- end }} diff --git a/charts/codefresh/cf-runtime/7.5.5/templates/volume-provisioner/storageclass.yaml b/charts/codefresh/cf-runtime/7.5.5/templates/volume-provisioner/storageclass.yaml new file mode 100644 index 0000000000..77a7602da1 --- /dev/null +++ b/charts/codefresh/cf-runtime/7.5.5/templates/volume-provisioner/storageclass.yaml @@ -0,0 +1,10 @@ +{{- $volumeProvisionerContext := deepCopy . }} +{{- $_ := set $volumeProvisionerContext "Values" (get .Values "volumeProvisioner") }} +{{- $_ := set $volumeProvisionerContext.Values "global" (get .Values "global") }} +{{- $_ := set $volumeProvisionerContext.Values "storage" (get .Values "storage") }} +{{- $_ := set $volumeProvisionerContext.Values "nameOverride" (get .Values "nameOverride") }} +{{- $_ := set $volumeProvisionerContext.Values "fullnameOverride" (get .Values "fullnameOverride") }} + +{{- if $volumeProvisionerContext.Values.enabled }} +{{- include "dind-volume-provisioner.resources.storageclass" $volumeProvisionerContext }} +{{- end }} diff --git a/charts/codefresh/cf-runtime/7.5.5/values-rootless.yaml b/charts/codefresh/cf-runtime/7.5.5/values-rootless.yaml new file mode 100644 index 0000000000..cb35a74fe9 --- /dev/null +++ b/charts/codefresh/cf-runtime/7.5.5/values-rootless.yaml @@ -0,0 +1,38 @@ +volumeProvisioner: + env: + IS_ROOTLESS: true + # -- Only if local volumes are used as backend storage (ignored for ebs/ebs-csi disks) + dind-lv-monitor: + image: + tag: 1.30.0-rootless + digest: sha256:712e549e6e843b04684647f17e0973f8047e0d60e6e8b38a693ea64dc75b0479 + containerSecurityContext: + runAsUser: 1000 + podSecurityContext: + fsGroup: 1000 + # Ref: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/#configure-volume-permission-and-ownership-change-policy-for-pods + fsGroupChangePolicy: "OnRootMismatch" + # -- Enable initContainer to run chmod for /var/lib/codefresh/dind-volumes on host nodes + volumePermissions: + enabled: false + +runtime: + dind: + image: + tag: 26.1.4-1.28.10-rootless + digest: sha256:59dfc004eb22a8f09c8a3d585271a055af9df4591ab815bca418c24a2077f5c8 + userVolumeMounts: + dind: + name: dind + mountPath: /home/rootless/.local/share/docker + containerSecurityContext: + privileged: true + runAsUser: 1000 + podSecurityContext: + fsGroup: 1000 + # Ref: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/#configure-volume-permission-and-ownership-change-policy-for-pods + fsGroupChangePolicy: "OnRootMismatch" + # -- Enable initContainer to run chmod for /home/rootless in DinD pod + # !!! Will slow down dind pod startup + volumePermissions: + enabled: true diff --git a/charts/codefresh/cf-runtime/7.5.5/values.yaml b/charts/codefresh/cf-runtime/7.5.5/values.yaml new file mode 100644 index 0000000000..e604919916 --- /dev/null +++ b/charts/codefresh/cf-runtime/7.5.5/values.yaml @@ -0,0 +1,916 @@ +# -- String to partially override cf-runtime.fullname template (will maintain the release name) +nameOverride: "" +# -- String to fully override cf-runtime.fullname template +fullnameOverride: "" +# -- Global parameters +# @default -- See below +global: + # -- Global Docker image registry + imageRegistry: "" + # -- Global Docker registry secret names as array + imagePullSecrets: [] + # -- URL of Codefresh Platform (required!) + codefreshHost: "https://g.codefresh.io" + # -- User token in plain text (required if `global.codefreshTokenSecretKeyRef` is omitted!) + # Ref: https://g.codefresh.io/user/settings (see API Keys) + # Minimal API key scopes: Runner-Installation(read+write), Agent(read+write), Agents(read+write) + codefreshToken: "" + # -- User token that references an existing secret containing API key (required if `global.codefreshToken` is omitted!) + codefreshTokenSecretKeyRef: {} + # E.g. + # codefreshTokenSecretKeyRef: + # name: my-codefresh-api-token + # key: codefresh-api-token + + # -- Account ID (required!) + # Can be obtained here https://g.codefresh.io/2.0/account-settings/account-information + accountId: "" + # -- K8s context name (required!) + context: "" + # E.g. + # context: prod-ue1-runtime-1 + + # -- Agent Name (optional!) + # If omitted, the following format will be used `{{ .Values.global.context }}_{{ .Release.Namespace }}` + agentName: "" + # E.g. + # agentName: prod-ue1-runtime-1 + + # -- Runtime name (optional!) + # If omitted, the following format will be used `{{ .Values.global.context }}/{{ .Release.Namespace }}` + runtimeName: "" + # E.g. + # runtimeName: prod-ue1-runtime-1/namespace + + # -- DEPRECATED Agent token in plain text. + # !!! MUST BE provided if migrating from < 6.x chart version + agentToken: "" + # -- DEPRECATED Agent token that references an existing secret containing API key. + # !!! MUST BE provided if migrating from < 6.x chart version + agentTokenSecretKeyRef: {} + # E.g. + # agentTokenSecretKeyRef: + # name: my-codefresh-agent-secret + # key: codefresh-agent-token +# DEPRECATED -- Use `.Values.global.imageRegistry` instead +dockerRegistry: "" +# DEPRECATED -- Use `.Values.runtime` instead +re: {} +# -- Runner parameters +# @default -- See below +runner: + # -- Enable the runner + enabled: true + # -- Set number of pods + replicasCount: 1 + # -- Upgrade strategy + updateStrategy: + type: RollingUpdate + # -- Set pod annotations + podAnnotations: {} + # -- Set image + image: + registry: quay.io + repository: codefresh/venona + tag: 2.0.0 + digest: sha256:bcc6e7495186f1f9c3e885afa891a3bda11b5374a577f069f34ddc75142342ef + # -- Init container + init: + image: + registry: quay.io + repository: codefresh/cli + tag: 0.88.4-rootless + digest: sha256:b256d150ff8a636851ddc1d5fb0490114d5036cc5bff357eac6a9899fea87562 + resources: + limits: + memory: 512Mi + cpu: '1' + requests: + memory: 256Mi + cpu: '0.2' + # -- Sidecar container + # Reconciles runtime spec from Codefresh API for drift detection + sidecar: + enabled: false + image: + registry: quay.io + repository: codefresh/kubectl + tag: 1.31.2 + digest: sha256:a30a8810dde249d0198f67792ed9696363f15c8cecbac955ee9bd267b5454ee7 + env: + RECONCILE_INTERVAL: 300 + resources: {} + # -- Add additional env vars + env: {} + # E.g. + # env: + # WORKFLOW_CONCURRENCY: 50 # The number of workflow creation and termination tasks the Runner can handle in parallel. Defaults to 50 + + # -- Service Account parameters + serviceAccount: + # -- Create service account + create: true + # -- Override service account name + name: "" + # -- Additional service account annotations + annotations: {} + # -- RBAC parameters + rbac: + # -- Create RBAC resources + create: true + # -- Add custom rule to the role + rules: [] + # -- Set security context for the pod + # @default -- See below + podSecurityContext: + enabled: true + runAsUser: 10001 + runAsGroup: 10001 + fsGroup: 10001 + # -- Readiness probe configuration + # @default -- See below + readinessProbe: + failureThreshold: 5 + initialDelaySeconds: 5 + periodSeconds: 5 + successThreshold: 1 + timeoutSeconds: 5 + # -- Set requests and limits + resources: {} + # -- Set node selector + nodeSelector: {} + # -- Set tolerations + tolerations: [] + # -- Set affinity + affinity: {} +# -- Volume Provisioner parameters +# @default -- See below +volumeProvisioner: + # -- Enable volume-provisioner + enabled: true + # -- Set number of pods + replicasCount: 1 + # -- Upgrade strategy + updateStrategy: + type: Recreate + # -- Set pod annotations + podAnnotations: {} + # -- Set image + image: + registry: quay.io + repository: codefresh/dind-volume-provisioner + tag: 1.35.2 + digest: sha256:ede6f663c912a08b7d335b5ec5518ccc266b27c431d0854d22971005992adc5d + # -- Add additional env vars + env: {} + # E.g. + # env: + # THREADINESS: 4 # The number of PVC requests the dind-volume-provisioner can process in parallel. Defaults to 4 + + # -- Service Account parameters + serviceAccount: + # -- Create service account + create: true + # -- Override service account name + name: "" + # -- Additional service account annotations + annotations: {} + # E.g. + # serviceAccount: + # annotations: + # eks.amazonaws.com/role-arn: "arn:aws:iam:::role/" + # -- RBAC parameters + rbac: + # -- Create RBAC resources + create: true + # -- Add custom rule to the role + rules: [] + # -- Set security context for the pod + # @default -- See below + podSecurityContext: + enabled: true + runAsUser: 3000 + runAsGroup: 3000 + fsGroup: 3000 + # -- Set node selector + nodeSelector: {} + # -- Set resources + resources: {} + # -- Set tolerations + tolerations: [] + # -- Set affinity + affinity: {} + # -- `dind-lv-monitor` DaemonSet parameters + # (local volumes cleaner) + # @default -- See below + dind-lv-monitor: + enabled: true + image: + registry: quay.io + repository: codefresh/dind-volume-utils + tag: 1.30.0 + digest: sha256:506915ccb63481cd6b249e9068235100ea2ae39d4c811c3e49851c20cbe5ee6f + podAnnotations: {} + podSecurityContext: + enabled: false + runAsUser: 1000 + fsGroup: 1000 + containerSecurityContext: {} + env: {} + resources: {} + nodeSelector: {} + tolerations: + - key: 'codefresh/dind' + operator: 'Exists' + effect: 'NoSchedule' + volumePermissions: + enabled: false + image: + registry: docker.io + repository: alpine + tag: 3.18 + digest: sha256:de0eb0b3f2a47ba1eb89389859a9bd88b28e82f5826b6969ad604979713c2d4f + resources: {} + securityContext: + runAsUser: 0 # auto + # `dind-volume-cleanup` CronJob parameters + # (external volumes cleaner) + # @default -- See below + dind-volume-cleanup: + enabled: true + image: + registry: quay.io + repository: codefresh/dind-volume-cleanup + tag: 1.2.0 + digest: sha256:1af3e3ecc87bf2e26ba07ecef68f54ad100d7e3b5fcf074099f627fd5d917369 + env: {} + concurrencyPolicy: Forbid + schedule: "*/10 * * * *" + successfulJobsHistory: 3 + failedJobsHistory: 1 + suspend: false + podAnnotations: {} + podSecurityContext: + enabled: true + fsGroup: 3000 + runAsGroup: 3000 + runAsUser: 3000 + nodeSelector: {} + affinity: {} + tolerations: [] +# Storage parameters for volume-provisioner +# @default -- See below +storage: + # -- Set backend volume type (`local`/`ebs`/`ebs-csi`/`gcedisk`/`azuredisk`) + backend: local + # -- Set filesystem type (`ext4`/`xfs`) + fsType: "ext4" + # Storage parametrs example for local volumes on the K8S nodes filesystem (i.e. `storage.backend=local`) + # https://kubernetes.io/docs/concepts/storage/volumes/#local + # @default -- See below + local: + # -- Set volume path on the host filesystem + volumeParentDir: /var/lib/codefresh/dind-volumes + # Storage parameters example for aws ebs disks (i.e. `storage.backend=ebs`/`storage.backend=ebs-csi`) + # https://aws.amazon.com/ebs/ + # https://codefresh.io/docs/docs/installation/codefresh-runner/#aws-backend-volume-configuration + # @default -- See below + ebs: + # -- Set EBS volume type (`gp2`/`gp3`/`io1`) (required) + volumeType: "gp2" + # -- Set EBS volumes availability zone (required) + availabilityZone: "us-east-1a" + # -- Enable encryption (optional) + encrypted: "false" + # -- Set KMS encryption key ID (optional) + kmsKeyId: "" + # -- Set AWS_ACCESS_KEY_ID for volume-provisioner (optional) + # Ref: https://codefresh.io/docs/docs/installation/codefresh-runner/#dind-volume-provisioner-permissions + accessKeyId: "" + # -- Existing secret containing AWS_ACCESS_KEY_ID. + accessKeyIdSecretKeyRef: {} + # E.g. + # accessKeyIdSecretKeyRef: + # name: + # key: + + # -- Set AWS_SECRET_ACCESS_KEY for volume-provisioner (optional) + # Ref: https://codefresh.io/docs/docs/installation/codefresh-runner/#dind-volume-provisioner-permissions + secretAccessKey: "" + # -- Existing secret containing AWS_SECRET_ACCESS_KEY + secretAccessKeySecretKeyRef: {} + # E.g. + # secretAccessKeySecretKeyRef: + # name: + # key: + # E.g. + # ebs: + # volumeType: gp3 + # availabilityZone: us-east-1c + # encrypted: false + # iops: "5000" + # # I/O operations per second. Only effetive when gp3 volume type is specified. + # # Default value - 3000. + # # Max - 16,000 + # throughput: "500" + # # Throughput in MiB/s. Only effective when gp3 volume type is specified. + # # Default value - 125. + # # Max - 1000. + # ebs: + # volumeType: gp2 + # availabilityZone: us-east-1c + # encrypted: true + # kmsKeyId: "1234abcd-12ab-34cd-56ef-1234567890ab" + # accessKeyId: "MYKEYID" + # secretAccessKey: "MYACCESSKEY" + + # Storage parameters example for gce disks + # https://cloud.google.com/compute/docs/disks#pdspecs + # https://codefresh.io/docs/docs/installation/codefresh-runner/#gke-google-kubernetes-engine-backend-volume-configuration + # @default -- See below + gcedisk: + # -- Set GCP volume backend type (`pd-ssd`/`pd-standard`) + volumeType: "pd-ssd" + # -- Set GCP volume availability zone + availabilityZone: "us-west1-a" + # -- Set Google SA JSON key for volume-provisioner (optional) + serviceAccountJson: "" + # -- Existing secret containing containing Google SA JSON key for volume-provisioner (optional) + serviceAccountJsonSecretKeyRef: {} + # E.g. + # gcedisk: + # volumeType: pd-ssd + # availabilityZone: us-central1-c + # serviceAccountJson: |- + # { + # "type": "service_account", + # "project_id": "...", + # "private_key_id": "...", + # "private_key": "...", + # "client_email": "...", + # "client_id": "...", + # "auth_uri": "...", + # "token_uri": "...", + # "auth_provider_x509_cert_url": "...", + # "client_x509_cert_url": "..." + # } + # Storage parameters example for Azure Disks + # https://codefresh.io/docs/docs/installation/codefresh-runner/#install-codefresh-runner-on-azure-kubernetes-service-aks + # @default -- See below + azuredisk: + # -- Set storage type (`Premium_LRS`) + skuName: Premium_LRS + cachingMode: None + # availabilityZone: northeurope-1 + # resourceGroup: + # DiskIOPSReadWrite: 500 + # DiskMBpsReadWrite: 100 + mountAzureJson: false +# -- Set runtime parameters +# @default -- See below +runtime: + # -- Set annotation on engine Service Account + # Ref: https://codefresh.io/docs/docs/administration/codefresh-runner/#injecting-aws-arn-roles-into-the-cluster + serviceAccount: + create: true + annotations: {} + # E.g. + # serviceAccount: + # annotations: + # eks.amazonaws.com/role-arn: "arn:aws:iam:::role/" + # -- Set parent runtime to inherit. + # Should not be changes. Parent runtime is controlled from Codefresh side. + runtimeExtends: + - system/default/hybrid/k8s_low_limits + # -- Runtime description + description: "" + # -- RBAC parameters + rbac: + # -- Create RBAC resources + create: true + # -- Add custom rule to the engine role + rules: [] + # -- (for On-Premise only) Enable agent + agent: true + # -- (for On-Premise only) Set inCluster runtime + inCluster: true + # -- (for On-Premise only) Assign accounts to runtime (list of account ids) + accounts: [] + # -- Parameters for DinD (docker-in-docker) pod (aka "runtime" pod). + dind: + # -- Set dind image. + image: + registry: quay.io + repository: codefresh/dind + tag: 26.1.4-1.28.8 # use `latest-rootless/rootless/26.1.4-1.28.8-rootless` tags for rootless-dind + pullPolicy: IfNotPresent + digest: sha256:33c343dd01e8a24f0b4a872bbe62884320719f9d9dc27b7a8fed9f7e9fc7e80e + # -- Set dind resources. + resources: + requests: null + limits: + cpu: 400m + memory: 800Mi + # -- Set termination grace period. + terminationGracePeriodSeconds: 30 + # -- PV claim spec parametes. + pvcs: + # -- Default dind PVC parameters + dind: + # -- PVC name prefix. + # Keep `dind` as default! Don't change! + name: dind + # -- PVC storage class name. + # Change ONLY if you need to use storage class NOT from Codefresh volume-provisioner + storageClassName: '{{ include "dind-volume-provisioner.storageClassName" . }}' + # -- PVC size. + volumeSize: 16Gi + # -- PV reuse selector. + # Ref: https://codefresh.io/docs/docs/installation/codefresh-runner/#volume-reuse-policy + reuseVolumeSelector: codefresh-app,io.codefresh.accountName + reuseVolumeSortOrder: pipeline_id + # -- PV annotations. + annotations: {} + # E.g.: + # annotations: + # codefresh.io/volume-retention: 7d + # -- Set additional env vars. + env: + DOCKER_ENABLE_DEPRECATED_PULL_SCHEMA_1_IMAGE: true + # -- Set pod annotations. + podAnnotations: {} + # -- Set pod labels. + podLabels: {} + # -- Set node selector. + nodeSelector: {} + # -- Set affinity + affinity: {} + # -- Set tolerations. + tolerations: [] + # -- Set security context for the pod. + podSecurityContext: {} + # -- Set container security context. + containerSecurityContext: {} + # -- Set scheduler name. + schedulerName: "" + # -- Set service account for pod. + serviceAccount: codefresh-engine + # -- Keep `true` as default! + userAccess: true + # -- Add extra volumes + userVolumes: {} + # E.g.: + # userVolumes: + # regctl-docker-registry: + # name: regctl-docker-registry + # secret: + # items: + # - key: .dockerconfigjson + # path: config.json + # secretName: regctl-docker-registry + # optional: true + # -- Add extra volume mounts + userVolumeMounts: {} + # E.g.: + # userVolumeMounts: + # regctl-docker-registry: + # name: regctl-docker-registry + # mountPath: /home/appuser/.docker/ + # readOnly: true + volumePermissions: + enabled: false + image: + registry: docker.io + repository: alpine + tag: 3.18 + digest: sha256:de0eb0b3f2a47ba1eb89389859a9bd88b28e82f5826b6969ad604979713c2d4f + resources: {} + securityContext: + runAsUser: 0 # auto + # -- Parameters for Engine pod (aka "pipeline" orchestrator). + engine: + # -- Set image. + image: + registry: quay.io + repository: codefresh/engine + tag: 1.177.4 + pullPolicy: IfNotPresent + digest: sha256:cfb0ece760033f95f37a40ca17614d91c928b4018705ad46fd39392daf578bc6 + # -- Set container command. + command: + - npm + - run + - start + # -- Set resources. + resources: + requests: + cpu: 100m + memory: 128Mi + limits: + cpu: 1000m + memory: 2048Mi + # -- Set termination grace period. + terminationGracePeriodSeconds: 180 + # -- Set system(base) runtime images. + # @default -- See below. + runtimeImages: + COMPOSE_IMAGE: quay.io/codefresh/compose:v2.32.2-1.5.2@sha256:9177054614f6db006a3500d2b9b8d2cafac4073ce891929d93e117714fccbd4b + CONTAINER_LOGGER_IMAGE: quay.io/codefresh/cf-container-logger:1.12.2@sha256:b3cbe2088f8fd0c48a0fa6df6c9ab8ad9d1d3c840a57f2c89520a655e2a8c116 + DOCKER_BUILDER_IMAGE: quay.io/codefresh/cf-docker-builder:1.4.2@sha256:8df63d741e7997902fa2ee7a0c68b3c3fd9003429bb093a3a9563b2cd2ec219c + DOCKER_PULLER_IMAGE: quay.io/codefresh/cf-docker-puller:8.0.18@sha256:1a15c3ae0952d3986de7866a3def8ac7e3e39f668fe87fd46c63d886ca06c6d7 + DOCKER_PUSHER_IMAGE: quay.io/codefresh/cf-docker-pusher:6.0.16@sha256:05efc1af8b1196f1b9b3f0781b4dcc1aa2cdd0ffc1347ee5fa81b16d029ec5c2 + DOCKER_TAG_PUSHER_IMAGE: quay.io/codefresh/cf-docker-tag-pusher:1.3.15@sha256:3a3e90cd10801c7ec0d3cf3816d0dcc90894d5d1771448c43f67215d90da5eca + FS_OPS_IMAGE: quay.io/codefresh/fs-ops:1.2.8@sha256:dc05888d84a959787a738caef914f83aa7392ff49c16767e612a29e180826f35 + GIT_CLONE_IMAGE: quay.io/codefresh/cf-git-cloner:10.2.0@sha256:a3ec854823f17d0fd817d978219122e644b1abd6db778fd835688fcb6d88c515 + KUBE_DEPLOY: quay.io/codefresh/cf-deploy-kubernetes:16.1.11@sha256:b6b3fc6cc5fad3ba9e36055278ce99a74a86876be116574503c6fbb4c1b4aa76 + PIPELINE_DEBUGGER_IMAGE: quay.io/codefresh/cf-debugger:1.3.7@sha256:3391822b7ad9835cc2a3a0ce5aaa55774ca110a8682d9512205dea24f438718a + TEMPLATE_ENGINE: quay.io/codefresh/pikolo:0.14.2@sha256:97874aefc46b58caf5b9d0edcfd2d6742db247e671424433363a1367020a8a65 + CR_6177_FIXER: alpine:edge@sha256:115729ec5cb049ba6359c3ab005ac742012d92bbaa5b8bc1a878f1e8f62c0cb8 + GC_BUILDER_IMAGE: quay.io/codefresh/cf-gc-builder:0.5.3@sha256:33ac914e6b844909f188a208cf90e569358cafa5aaa60f49848f49d99bcaf875 + COSIGN_IMAGE_SIGNER_IMAGE: quay.io/codefresh/cf-cosign-image-signer:2.4.0-cf.2@sha256:5e0993207aa809c25ed70cf89af444d9720892fb4a29deb82db45618b0cae4a9 + # -- Set additional env vars. + env: + # -- Interval to check the exec status in the container-logger + CONTAINER_LOGGER_EXEC_CHECK_INTERVAL_MS: 1000 + # -- Timeout while doing requests to the Docker daemon + DOCKER_REQUEST_TIMEOUT_MS: 30000 + # -- If "true", composition images will be pulled sequentially + FORCE_COMPOSE_SERIAL_PULL: false + # -- Level of logging for engine + LOGGER_LEVEL: debug + # -- Enable debug-level logging of outgoing HTTP/HTTPS requests + LOG_OUTGOING_HTTP_REQUESTS: false + # -- Enable emitting metrics from engine + METRICS_PROMETHEUS_ENABLED: true + # -- Enable legacy metrics + METRICS_PROMETHEUS_ENABLE_LEGACY_METRICS: false + # -- Enable collecting process metrics + METRICS_PROMETHEUS_COLLECT_PROCESS_METRICS: false + # -- Host for Prometheus metrics server + METRICS_PROMETHEUS_HOST: '0.0.0.0' + # -- Port for Prometheus metrics server + METRICS_PROMETHEUS_PORT: 9100 + # -- The timeout till the engine waits for Prometheus to pull the latest metrics before engine shuts down (in milliseconds) + METRICS_PROMETHEUS_SCRAPE_TIMEOUT: '15000' + # -- Trusted QEMU images used for docker builds - when left blank only 'tonistiigi/binfmt' is trusted. + TRUSTED_QEMU_IMAGES: '' + # -- Set workflow limits. + workflowLimits: + # -- Maximum time allowed to the engine to wait for the pre-steps (aka "Initializing Process") to succeed; seconds. + MAXIMUM_ALLOWED_TIME_BEFORE_PRE_STEPS_SUCCESS: 600 + # -- Maximum time for workflow execution; seconds. + MAXIMUM_ALLOWED_WORKFLOW_AGE_BEFORE_TERMINATION: 86400 + # -- Maximum time allowed to workflow to spend in "elected" state; seconds. + MAXIMUM_ELECTED_STATE_AGE_ALLOWED: 900 + # -- Maximum retry attempts allowed for workflow. + MAXIMUM_RETRY_ATTEMPTS_ALLOWED: 20 + # -- Maximum time allowed to workflow to spend in "terminating" state until force terminated; seconds. + MAXIMUM_TERMINATING_STATE_AGE_ALLOWED: 900 + # -- Maximum time allowed to workflow to spend in "terminating" state without logs activity until force terminated; seconds. + MAXIMUM_TERMINATING_STATE_AGE_ALLOWED_WITHOUT_UPDATE: 300 + # -- Time since the last health check report after which workflow is terminated; seconds. + TIME_ENGINE_INACTIVE_UNTIL_TERMINATION: 300 + # -- Time since the last health check report after which the engine is considered unhealthy; seconds. + TIME_ENGINE_INACTIVE_UNTIL_UNHEALTHY: 60 + # -- Time since the last workflow logs activity after which workflow is terminated; seconds. + TIME_INACTIVE_UNTIL_TERMINATION: 2700 + # -- Set pod annotations. + podAnnotations: {} + # -- Set pod labels. + podLabels: {} + # -- Set node selector. + nodeSelector: {} + # -- Set affinity + affinity: {} + # -- Set tolerations. + tolerations: [] + # -- Set scheduler name. + schedulerName: "" + # -- Set service account for pod. + serviceAccount: codefresh-engine + # -- Set extra env vars + userEnvVars: [] + # E.g. + # userEnvVars: + # - name: GITHUB_TOKEN + # valueFrom: + # secretKeyRef: + # name: github-token + # key: token + # -- Parameters for `runtime-patch` post-upgrade/install hook + # @default -- See below + patch: + enabled: true + image: + registry: quay.io + repository: codefresh/cli + tag: 0.88.4-rootless + digest: sha256:b256d150ff8a636851ddc1d5fb0490114d5036cc5bff357eac6a9899fea87562 + rbac: + enabled: true + annotations: {} + affinity: {} + nodeSelector: {} + podSecurityContext: {} + resources: {} + tolerations: [] + ttlSecondsAfterFinished: 180 + env: + HOME: /tmp + # -- Parameters for `gencerts-dind` post-upgrade/install hook + # @default -- See below + gencerts: + enabled: true + image: + registry: quay.io + repository: codefresh/kubectl + tag: 1.31.2 + digest: sha256:a30a8810dde249d0198f67792ed9696363f15c8cecbac955ee9bd267b5454ee7 + rbac: + enabled: true + annotations: {} + affinity: {} + nodeSelector: {} + podSecurityContext: {} + resources: {} + tolerations: [] + ttlSecondsAfterFinished: 180 + # -- DinD pod daemon config + # @default -- See below + dindDaemon: + hosts: + - unix:///var/run/docker.sock + - tcp://0.0.0.0:1300 + tlsverify: true + tls: true + tlscacert: /etc/ssl/cf-client/ca.pem + tlscert: /etc/ssl/cf/server-cert.pem + tlskey: /etc/ssl/cf/server-key.pem + insecure-registries: + - 192.168.99.100:5000 + metrics-addr: 0.0.0.0:9323 + experimental: true +# App-Proxy parameters +# Ref: https://codefresh.io/docs/docs/installation/codefresh-runner/#app-proxy-installation +# @default -- See below +appProxy: + # -- Enable app-proxy + enabled: false + # -- Set number of pods + replicasCount: 1 + # -- Upgrade strategy + updateStrategy: + type: RollingUpdate + # -- Set pod annotations + podAnnotations: {} + # -- Set image + image: + registry: quay.io + repository: codefresh/cf-app-proxy + tag: 0.0.50 + digest: sha256:cc5054ec572a33bb3545cb065c2082ecf988113efffe8235e57ed93d27b861a6 + # -- Add additional env vars + env: {} + # Set app-proxy ingress parameters + # @default -- See below + ingress: + # -- Set path prefix for ingress (keep empty for default `/` path) + pathPrefix: "" + # -- Set ingress class + class: "" + # -- Set DNS hostname the ingress will use + host: "" + # -- Set k8s tls secret for the ingress object + tlsSecret: "" + # -- Set extra annotations for ingress object + annotations: {} + # E.g. + # ingress: + # pathPrefix: "/cf-app-proxy" + # class: "nginx" + # host: "mydomain.com" + # tlsSecret: "tls-cert-app-proxy" + # annotations: + # nginx.ingress.kubernetes.io/whitelist-source-range: 123.123.123.123/130 + # -- Service Account parameters + serviceAccount: + # -- Create service account + create: true + # -- Override service account name + name: "" + # -- Use Role(true)/ClusterRole(true) + namespaced: true + # -- Additional service account annotations + annotations: {} + # -- RBAC parameters + rbac: + # -- Create RBAC resources + create: true + # -- Use Role(true)/ClusterRole(true) + namespaced: true + # -- Add custom rule to the role + rules: [] + # -- Set security context for the pod + podSecurityContext: {} + # -- Readiness probe configuration + # @default -- See below + readinessProbe: + failureThreshold: 5 + initialDelaySeconds: 5 + periodSeconds: 5 + successThreshold: 1 + timeoutSeconds: 5 + # -- Set requests and limits + resources: {} + # -- Set node selector + nodeSelector: {} + # -- Set tolerations + tolerations: [] + # -- Set affinity + affinity: {} +# Monitor parameters +# @default -- See below +monitor: + # -- Enable monitor + # Ref: https://codefresh.io/docs/docs/installation/codefresh-runner/#install-monitoring-component + enabled: false + # -- Set number of pods + replicasCount: 1 + # -- Upgrade strategy + updateStrategy: + type: RollingUpdate + # -- Set pod annotations + podAnnotations: {} + # -- Set image + image: + registry: quay.io + repository: codefresh/cf-k8s-agent + tag: 1.3.21 + digest: sha256:3cc7b3d41f841604133197a44f016db499f3e91e26448da36ce739a0b1171d05 + # -- Add additional env vars + env: {} + # -- Service Account parameters + serviceAccount: + # -- Create service account + create: true + # -- Override service account name + name: "" + # -- Additional service account annotations + annotations: {} + # -- RBAC parameters + rbac: + # -- Create RBAC resources + create: true + # -- Use Role(true)/ClusterRole(true) + namespaced: true + # -- Add custom rule to the role + rules: [] + # -- Readiness probe configuration + # @default -- See below + readinessProbe: + failureThreshold: 5 + initialDelaySeconds: 5 + periodSeconds: 5 + successThreshold: 1 + timeoutSeconds: 5 + podSecurityContext: {} + # -- Set node selector + nodeSelector: {} + # -- Set resources + resources: {} + # -- Set tolerations + tolerations: [] + # -- Set affinity + affinity: {} +# -- Add serviceMonitor +# @default -- See below +serviceMonitor: + main: + # -- Enable service monitor for dind pods + enabled: false + nameOverride: dind + selector: + matchLabels: + app: dind + endpoints: + - path: /metrics + targetPort: 9100 + relabelings: + - action: labelmap + regex: __meta_kubernetes_pod_label_(.+) +# -- Add podMonitor (for engine pods) +# @default -- See below +podMonitor: + main: + # -- Enable pod monitor for engine pods + enabled: false + nameOverride: engine + selector: + matchLabels: + app: runtime + podMetricsEndpoints: + - path: /metrics + targetPort: 9100 + runner: + # -- Enable pod monitor for runner pod + enabled: false + nameOverride: runner + selector: + matchLabels: + codefresh.io/application: runner + podMetricsEndpoints: + - path: /metrics + targetPort: 8080 + volume-provisioner: + # -- Enable pod monitor for volumeProvisioner pod + enabled: false + nameOverride: volume-provisioner + selector: + matchLabels: + codefresh.io/application: volume-provisioner + podMetricsEndpoints: + - path: /metrics + targetPort: 8080 +# -- Event exporter parameters +# @default -- See below +event-exporter: + # -- Enable event-exporter + enabled: false + # -- Set number of pods + replicasCount: 1 + # -- Upgrade strategy + updateStrategy: + type: Recreate + # -- Set pod annotations + podAnnotations: {} + # -- Set image + image: + registry: docker.io + repository: codefresh/k8s-event-exporter + tag: latest + digest: sha256:cf52048f1378fb6659dffd1394d68fdf23a7ea709585dc14b5007f3e5a1b7584 + # -- Add additional env vars + env: {} + # -- Service Account parameters + serviceAccount: + # -- Create service account + create: true + # -- Override service account name + name: "" + # -- Additional service account annotations + annotations: {} + # -- RBAC parameters + rbac: + # -- Create RBAC resources + create: true + # -- Add custom rule to the role + rules: [] + # -- Set security context for the pod + # @default -- See below + podSecurityContext: + enabled: false + # -- Set node selector + nodeSelector: {} + # -- Set resources + resources: {} + # -- Set tolerations + tolerations: [] + # -- Set affinity + affinity: {} +# -- Array of extra objects to deploy with the release +extraResources: [] +# E.g. +# extraResources: +# - apiVersion: rbac.authorization.k8s.io/v1 +# kind: ClusterRole +# metadata: +# name: codefresh-role +# rules: +# - apiGroups: [ "*"] +# resources: ["*"] +# verbs: ["get", "list", "watch", "create", "update", "patch", "delete"] +# - apiVersion: v1 +# kind: ServiceAccount +# metadata: +# name: codefresh-user +# namespace: "{{ .Release.Namespace }}" +# - apiVersion: rbac.authorization.k8s.io/v1 +# kind: ClusterRoleBinding +# metadata: +# name: codefresh-user +# roleRef: +# apiGroup: rbac.authorization.k8s.io +# kind: ClusterRole +# name: codefresh-role +# subjects: +# - kind: ServiceAccount +# name: codefresh-user +# namespace: "{{ .Release.Namespace }}" +# - apiVersion: v1 +# kind: Secret +# type: kubernetes.io/service-account-token +# metadata: +# name: codefresh-user-token +# namespace: "{{ .Release.Namespace }}" +# annotations: +# kubernetes.io/service-account.name: "codefresh-user" diff --git a/charts/instana/instana-agent/2.0.13/.helmignore b/charts/instana/instana-agent/2.0.13/.helmignore new file mode 100644 index 0000000000..b4fa6cb0de --- /dev/null +++ b/charts/instana/instana-agent/2.0.13/.helmignore @@ -0,0 +1,23 @@ +# 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 +# OWNERS file for helm +OWNERS diff --git a/charts/instana/instana-agent/2.0.13/Chart.yaml b/charts/instana/instana-agent/2.0.13/Chart.yaml new file mode 100644 index 0000000000..94c9b93052 --- /dev/null +++ b/charts/instana/instana-agent/2.0.13/Chart.yaml @@ -0,0 +1,39 @@ +annotations: + artifacthub.io/links: | + - name: Instana website + url: https://www.ibm.com/products/instana + - name: Instana Helm charts + url: https://github.com/instana/helm-charts + catalog.cattle.io/certified: partner + catalog.cattle.io/display-name: Instana Agent + catalog.cattle.io/kube-version: '>=1.21-0' + catalog.cattle.io/release-name: instana-agent +apiVersion: v2 +appVersion: 1.290.0 +description: Instana Agent for Kubernetes +home: https://www.instana.com/ +icon: file://assets/icons/instana-agent.png +kubeVersion: '>=1.21-0' +maintainers: +- email: felix.marx@ibm.com + name: FelixMarxIBM +- email: henning.treu@ibm.com + name: htreu +- email: konrad.ohms@de.ibm.com + name: Konrad-Ohms +- email: fredrik.gundersen@ibm.com + name: FredrikAtIBM +- email: jefiyamj@ibm.com + name: Jefiya-MJ +- email: milica.cvrkota@ibm.com + name: Milica-Cvrkota-IBM +- email: Nagaraj.Kandoor@ibm.com + name: nagaraj-kandoor +- email: Vineeth.Soman@ibm.com + name: vineethsoman03 +- email: Rashmi.Swamy@ibm.com + name: rashmiswamyibm +name: instana-agent +sources: +- https://github.com/instana/instana-agent-docker +version: 2.0.13 diff --git a/charts/instana/instana-agent/2.0.13/DEPLOYMENT.md b/charts/instana/instana-agent/2.0.13/DEPLOYMENT.md new file mode 100644 index 0000000000..510dea06d8 --- /dev/null +++ b/charts/instana/instana-agent/2.0.13/DEPLOYMENT.md @@ -0,0 +1,54 @@ +# Kubernetes Deployment Mode (tech preview) + +Instana has always endeavored to make the experience of using Instana as seamless as possible from auto-instrumentation to one-liner installs. To date for our customers with Kubernetes clusters containing more than 1,000 entities this wasn’t the case. The Kubernetes sensor as a deployment is one of many steps we’re taking to improve the experience of operating Instana in Kubernetes. This is a tech preview however we have a high degree of confidence it will work well in your production workloads. The fundamental change moves the Kubernetes sensor from the DaemonSet responsible for monitoring your hosts and processes into its own dedicated Deployment where it does not contend for resources with other sensors. An overview of this deployment is below: + +![kubernetes.deployment.enabled=true](kubernetes.deployment.enabled.png) + +This change provides a few primary benefits including: + +* Lower load on the Kubernetes api-server as it eliminates per node pod monitoring. +* Lower load on the Kubernetes api-server as it reduces the endpoint watch to 2 leader elector side cars. +* Lower memory and CPU requests in the DaemonSet as it is no longer responsible for monitoring Kubernetes. +* Elimination of the leader elector sidecar in the DaemonSet as it is only required for the Kubernetes sensor. +* Better performance of the Kubernetes sensor as it is isolated from other sensors and does not contend for CPU and memory. +* Better scaling behaviour as you can adjust the memory and CPU requirements to monitor your clusters without overprovisioning utilisation cluster wide. + +The primary drawback of this model in the tech preview include: + +* Reduced control and observability of the Kubernetes specific Agents in the Agent dashboard. +* Some unnecessary features are still enabled in the Kubernetes sensor (e.g. trace sinks, and host monitoring). + +Some limitations remain unchanged from the previous sensor: + +* Clusters with a high number of entities (e.g. pods, deployments, etc) are likely to have non-deterministic behaviour due to limitations we impose on message sizes. This is unlikely to be experienced in clusters with fewer than 500 hosts. +* The ServiceAccount is shared between both the DaemonSet and Deployment meaning no change in the security posture. We plan to add an additional service account to limit access to the api-server to only the Kubernetes sensor Deployment. + +## Installation + +For clusters with minimal controls you can install the tech preview with the following Helm install command: + +``` +helm template instana-agent \ + --repo https://agents.instana.io/helm \ + --namespace instana-agent \ + --create-namespace \ + --set agent.key=${AGENT_KEY} \ + --set agent.endpointHost=${BACKEND_URL} \ + --set agent.endpointPort=443 \ + --set cluster.name=${CLUSTER_NAME} \ + --set zone.name=${ZONE_NAME} \ + --set kubernetes.deployment.enabled=true \ + instana-agent +``` + +If your cluster employs Pod Security Policies you will need the following additional flag: + +``` +--set podSecurityPolicy.enable=true +``` + +If you are deploying into an OpenShift 4.x cluster you will need the following additional flag: + +``` +--set openshift=true +``` diff --git a/charts/instana/instana-agent/2.0.13/README.md b/charts/instana/instana-agent/2.0.13/README.md new file mode 100644 index 0000000000..704d2fd7ed --- /dev/null +++ b/charts/instana/instana-agent/2.0.13/README.md @@ -0,0 +1,810 @@ +# Instana + +Instana is an [APM solution](https://www.instana.com/) built for microservices that enables IT Ops to build applications faster and deliver higher quality services by automating monitoring, tracing and root cause analysis. +This solution is optimized for [Kubernetes](https://www.instana.com/automatic-kubernetes-monitoring/). + +This chart adds the Instana Agent to all schedulable nodes in your cluster via a privileged `DaemonSet` and accompanying resources like `ConfigurationMap`s, `Secret`s and RBAC settings. + +## Prerequisites + +* Kubernetes 1.21+ OR OpenShift 4.8+ +* Helm 3 + +## Installation + +To configure the installation you can either specify the options on the command line using the **--set** switch, or you can edit **values.yaml**. + +First, create a namespace for the instana-agent + +```bash +$ kubectl create namespace instana-agent +``` + +**OpenShift:** When targetting an OpenShift 4.x cluster, ensure proper permission before installing the helm chart, otherwise the agent pods will not be scheduled correctly. + +```bash +$ oc adm policy add-scc-to-user privileged -z instana-agent +``` + +To install the chart with the release name `instana-agent` and set the values on the command line run: + +```bash +$ helm install instana-agent \ +--namespace instana-agent \ +--repo https://agents.instana.io/helm \ +--set agent.key=INSTANA_AGENT_KEY \ +--set agent.endpointHost=HOST \ +--set zone.name=ZONE_NAME \ +--set cluster.name="CLUSTER_NAME" \ +instana-agent +``` + +## Upgrade + +The helm chart deploys a Kubernetes Operator internally that reconciles the agent resources based on an agent CustomResource (CR) that is created based on the helm values. As the Operator pattern requires a CustomResourceDefinition (CRD) to be present in the cluster before defining any CRs, the CRD definition is included in the helm chart. On initial installations the chart deploys the CRD before submitting the rest of the artifacts. + +Helm has known limitations around handling the CRD lifecycle as outlined in their [documentation](https://helm.sh/docs/chart_best_practices/custom_resource_definitions/). + +This leads to the problem, that CRD updates are only submitted to the Cluster on **initial installation**, but **NOT applied automatically during upgrades**. + +It is also worth noting, that CRDs must be removed manually if the chart should be removed completly, see more details in the [uninstall](#uninstallation) section. + +To ensure a proper update, apply the CRD updates before running the upgrade: + +``` +helm pull --repo https://agents.instana.io/helm --untar instana-agent; kubectl apply -f instana-agent/crds; helm upgrade instana-agent instana-agent \ + --namespace instana-agent \ + --repo https://agents.instana.io/helm \ + --reuse-values +``` + +This is especially important when migrating from helm charts v1 to v2, as the upgrade will fail otherwise as the CR artifact cannot be created. + +### Required Settings + +#### Configuring the Instana Backend + +In order to report the data it collects to the Instana backend for analysis, the Instana agent must know which backend to report to, and which credentials to use to authenticate, known as "agent key". + +As described by the [Install Using the Helm Chart](https://www.instana.com/docs/setup_and_manage/host_agent/on/kubernetes#install-using-the-helm-chart) documentation, you will find the right values for the following fields inside Instana itself: + +* `agent.endpointHost` +* `agent.endpointPort` +* `agent.key` + +_Note:_ You can find the options mentioned in the [configuration section below](#configuration-reference) + +If your agents report into a self-managed Instana unit (also known as "on-prem"), you will also need to configure a "download key", which allows the agent to fetch its components from the Instana repository. +The download key is set via the following value: + +* `agent.downloadKey` + +#### Zone and Cluster + +Instana needs to know how to name your Kubernetes cluster and, optionally, how to group your Instana agents in [Custom zones](https://www.instana.com/docs/setup_and_manage/host_agent/configuration/#custom-zones) using the following fields: + +* `zone.name` +* `cluster.name` + +Either `zone.name` or `cluster.name` are required. +If you omit `cluster.name`, the value of `zone.name` will be used as cluster name as well. +If you omit `zone.name`, the host zone will be automatically determined by the availability zone information provided by the [supported Cloud providers](https://www.instana.com/docs/setup_and_manage/cloud_service_agents). + +## Uninstallation + +To uninstall/delete the `instana-agent` release: + +```bash +helm uninstall instana-agent -n instana-agent && kubectl patch agent instana-agent -n instana-agent -p '{"metadata":{"finalizers":null}}' --type=merge && +kubectl delete crd/agents.instana.io +``` + +## Configuration Reference + +The following table lists the configurable parameters of the Instana chart and their default values. + +| Parameter | Description | Default | +| --------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------- | +| `agent.configuration_yaml` | Custom content for the agent configuration.yaml file | `nil` See [below](#agent-configuration) for more details | +| `agent.endpointHost` | Instana Agent backend endpoint host | `ingress-red-saas.instana.io` (US and ROW). If in Europe, please override with `ingress-blue-saas.instana.io` | +| `agent.endpointPort` | Instana Agent backend endpoint port | `443` | +| `agent.key` | Your Instana Agent key | `nil` You must provide your own key unless `agent.keysSecret` is specified | +| `agent.downloadKey` | Your Instana Download key | `nil` Usually not required | +| `agent.keysSecret` | As an alternative to specifying `agent.key` and, optionally, `agent.downloadKey`, you can instead specify the name of the secret in the namespace in which you install the Instana agent that carries the agent key and download key | `nil` Usually not required, see [Bring your own Keys secret](#bring-your-own-keys-secret) for more details | +| `agent.additionalBackends` | List of additional backends to report to; it must specify the `endpointHost` and `key` fields, and optionally `endpointPort` | `[]` Usually not required; see [Configuring Additional Backends](#configuring-additional-backends) for more info and examples | +| `agent.tls.secretName` | The name of the secret of type `kubernetes.io/tls` which contains the TLS relevant data. If the name is provided, `agent.tls.certificate` and `agent.tls.key` will be ignored. | `nil` | +| `agent.tls.certificate` | The certificate data encoded as base64. Which will be used to create a new secret of type `kubernetes.io/tls`. | `nil` | +| `agent.tls.key` | The private key data encoded as base64. Which will be used to create a new secret of type `kubernetes.io/tls`. | `nil` | +| `agent.image.name` | The image name to pull | `instana/agent` | +| `agent.image.digest` | The image digest to pull; if specified, it causes `agent.image.tag` to be ignored | `nil` | +| `agent.image.tag` | The image tag to pull; this property is ignored if `agent.image.digest` is specified | `latest` | +| `agent.image.pullPolicy` | Image pull policy | `Always` | +| `agent.image.pullSecrets` | Image pull secrets; if not specified (default) _and_ `agent.image.name` starts with `containers.instana.io`, it will be automatically set to `[{ "name": "containers-instana-io" }]` to match the default secret created in this case. | `nil` | +| `agent.listenAddress` | List of addresses to listen on, or "*" for all interfaces | `nil` | +| `agent.mode` | Agent mode. Supported values are `APM`, `INFRASTRUCTURE`, `AWS` | `APM` | +| `agent.instanaMvnRepoUrl` | Override for the Maven repository URL when the Agent needs to connect to a locally provided Maven repository 'proxy' | `nil` Usually not required | +| `agent.instanaMvnRepoFeaturesPath` | Override for the Maven repository features path the Agent needs to connect to a locally provided Maven repository 'proxy' | `nil` Usually not required | +| `agent.instanaMvnRepoSharedPath` | Override for the Maven repository shared path when the Agent needs to connect to a locally provided Maven repository 'proxy' | `nil` Usually not required | +| `agent.agentReleaseRepoMirrorUrl` | The URL of the agent features repository mirror. For more information, see [Configuring the agent repository as the mirror](https://www.ibm.com/docs/en/instana-observability/current?topic=agents-setting-up-agent-repositories-dynamic-host#configuring-the-agent-repository-as-the-mirror). | `nil` | +| `agent.agentReleaseRepoMirrorUsername` | The username for authentication for the agent features repository mirror. For more information, see [Configuring the agent repository as the mirror](https://www.ibm.com/docs/en/instana-observability/current?topic=agents-setting-up-agent-repositories-dynamic-host#configuring-the-agent-repository-as-the-mirror). | `nil` | +| `agent.agentReleaseRepoMirrorPassword` | The password for authentication for the agent features repository mirror. For more information, see [Configuring the agent repository as the mirror](https://www.ibm.com/docs/en/instana-observability/current?topic=agents-setting-up-agent-repositories-dynamic-host#configuring-the-agent-repository-as-the-mirror). | `nil` | +| `agent.instanaSharedRepoMirrorUrl` | The URL of the agent shared repository mirror. For more information, see [Configuring the agent repository as the mirror](https://www.ibm.com/docs/en/instana-observability/current?topic=agents-setting-up-agent-repositories-dynamic-host#configuring-the-agent-repository-as-the-mirror). | `nil` | +| `agent.instanaSharedRepoMirrorUsername` | The username for authentication for the for the agent shared repository mirror. For more information, see [Configuring the agent repository as the mirror](https://www.ibm.com/docs/en/instana-observability/current?topic=agents-setting-up-agent-repositories-dynamic-host#configuring-the-agent-repository-as-the-mirror). | `nil` | +| `agent.instanaSharedRepoMirrorPassword` | The password for authentication for the for the agent shared repository mirror. For more information, see [Configuring the agent repository as the mirror](https://www.ibm.com/docs/en/instana-observability/current?topic=agents-setting-up-agent-repositories-dynamic-host#configuring-the-agent-repository-as-the-mirror). | `nil` | +| `agent.updateStrategy.type` | [DaemonSet update strategy type](https://kubernetes.io/docs/tasks/manage-daemon/update-daemon-set/); valid values are `OnDelete` and `RollingUpdate` | `RollingUpdate` | +| `agent.updateStrategy.rollingUpdate.maxUnavailable` | How many agent pods can be updated at once; this value is ignored if `agent.updateStrategy.type` is different than `RollingUpdate` | `1` | +| `agent.minReadySeconds` | The minimum number of seconds for which a newly created Pod should be ready without any of its containers crashing, for it to be considered available | `0` | +| `agent.pod.annotations` | Additional annotations to apply to the pod | `{}` | +| `agent.pod.labels` | Additional labels to apply to the Agent pod | `{}` | +| `agent.pod.priorityClassName` | Name of an _existing_ PriorityClass that should be set on the agent pods | `nil` | +| `agent.proxyHost` | Hostname/address of a proxy | `nil` | +| `agent.proxyPort` | Port of a proxy | `nil` | +| `agent.proxyProtocol` | Proxy protocol. Supported proxy types are `http` (for both HTTP and HTTPS proxies), `socks4`, `socks5`. | `nil` | +| `agent.proxyUser` | Username of the proxy auth | `nil` | +| `agent.proxyPassword` | Password of the proxy auth | `nil` | +| `agent.proxyUseDNS` | Boolean if proxy also does DNS | `nil` | +| `agent.pod.limits.cpu` | Container cpu limits in cpu cores | `1.5` | +| `agent.pod.limits.memory` | Container memory limits in MiB | `768Mi` | +| `agent.pod.requests.cpu` | Container cpu requests in cpu cores | `0.5` | +| `agent.pod.requests.memory` | Container memory requests in MiB | `768Mi` | +| `agent.pod.tolerations` | Tolerations for pod assignment | `[]` | +| `agent.pod.affinity` | Affinity for pod assignment | `{}` | +| `agent.pod.volumes` | Custom volumes of the agent pod, see https://kubernetes.io/docs/concepts/storage/volumes/ | `[]` | +| `agent.pod.volumeMounts` | Custom volume mounts of the agent pod, see https://kubernetes.io/docs/concepts/storage/volumes/ | `[]` | +| `agent.serviceMesh.enabled` | Activate Instana Agent JVM monitoring service mesh support for Istio or OpenShift ServiceMesh | `true` | +| `agent.env` | Additional environment variables for the agent | `{}` | +| `agent.redactKubernetesSecrets` | Enable additional secrets redaction for selected Kubernetes resources | `nil` See [Kubernetes secrets](https://docs.instana.io/setup_and_manage/host_agent/on/kubernetes/#secrets) for more details. | +| `cluster.name` | Display name of the monitored cluster | Value of `zone.name` | +| `k8s_sensor.deployment.enabled` | Isolate k8sensor with a deployment | +| `k8s_sensor.deployment.minReadySeconds` | The minimum number of seconds for which a newly created Pod should be ready without any of its containers crashing, for it to be considered available | `0` | `true` | +| `k8s_sensor.image.name` | The k8sensor image name to pull | `gcr.io/instana/k8sensor` | +| `k8s_sensor.image.digest` | The image digest to pull; if specified, it causes `k8s_sensor.image.tag` to be ignored | `nil` | +| `k8s_sensor.image.tag` | The image tag to pull; this property is ignored if `k8s_sensor.image.digest` is specified | `latest` | +| `k8s_sensor.deployment.pod.limits.cpu` | CPU request for the `k8sensor` pods | `4` | +| `k8s_sensor.deployment.pod.limits.memory` | Memory request limits for the `k8sensor` pods | `6144Mi` | +| `k8s_sensor.deployment.pod.requests.cpu` | CPU limit for the `k8sensor` pods | `1.5` | +| `k8s_sensor.deployment.pod.requests.memory` | Memory limit for the `k8sensor` pods | `1024Mi` | +| `podSecurityPolicy.enable` | Whether a PodSecurityPolicy should be authorized for the Instana Agent pods. Requires `rbac.create` to be `true` as well and it is available until Kubernetes version v1.25. | `false` See [PodSecurityPolicy](https://docs.instana.io/setup_and_manage/host_agent/on/kubernetes/#podsecuritypolicy) for more details. | +| `podSecurityPolicy.name` | Name of an _existing_ PodSecurityPolicy to authorize for the Instana Agent pods. If not provided and `podSecurityPolicy.enable` is `true`, a PodSecurityPolicy will be created for you. | `nil` | +| `rbac.create` | Whether RBAC resources should be created | `true` | +| `opentelemetry.grpc.enabled` | Whether to configure the agent to accept telemetry from OpenTelemetry applications via gRPC. This option also implies `service.create=true`, and requires Kubernetes 1.21+, as it relies on `internalTrafficPolicy`. | `true` | +| `opentelemetry.http.enabled` | Whether to configure the agent to accept telemetry from OpenTelemetry applications via HTTP. This option also implies `service.create=true`, and requires Kubernetes 1.21+, as it relies on `internalTrafficPolicy`. | `true` | +| `prometheus.remoteWrite.enabled` | Whether to configure the agent to accept metrics over its implementation of the `remote_write` Prometheus endpoint. This option also implies `service.create=true`, and requires Kubernetes 1.21+, as it relies on `internalTrafficPolicy`. | `false` | +| `service.create` | Whether to create a service that exposes the agents' Prometheus, OpenTelemetry and other APIs inside the cluster. Requires Kubernetes 1.21+, as it relies on `internalTrafficPolicy`. The `ServiceInternalTrafficPolicy` feature gate needs to be enabled (default: enabled). | `true` | +| `serviceAccount.create` | Whether a ServiceAccount should be created | `true` | +| `serviceAccount.name` | Name of the ServiceAccount to use | `instana-agent` | +| `serviceAccount.annotations` | Annotations to add to the service account | `{}` | +| `zone.name` | Zone that detected technologies will be assigned to | `nil` You must provide either `zone.name` or `cluster.name`, see [above](#installation) for details | +| `zones` | Multi-zone daemonset configuration. | `nil` see [below](#multiple-zones) for details | +| `k8s_sensor.podDisruptionBudget.enabled` | Whether to create DisruptionBudget for k8sensor to limit the number of concurrent disruptions | `false` | +| `k8s_sensor.deployment.pod.affinity` | `k8sensor` deployment affinity format | `podAntiAffinity` defined in `values.yaml` | + +### Agent Modes + +Agent can have either `APM` or `INFRASTRUCTURE`. +Default is APM and if you want to override that, ensure you set value: + +* `agent.mode` + +For more information on agent modes, refer to the [Host Agent Modes](https://www.instana.com/docs/setup_and_manage/host_agent#host-agent-modes) documentation. + +### Agent Configuration + +Besides the settings listed above, there are many more settings that can be applied to the agent via the so-called "Agent Configuration File", often also referred to as `configuration.yaml` file. +An overview of the settings that can be applied is provided in the [Agent Configuration File](https://www.instana.com/docs/setup_and_manage/host_agent/configuration#agent-configuration-file) documentation. +To configure the agent, you need to provide the configuration via the `agent.configuration_yaml` parameter in [values.yaml](values.yaml). As all other settings, the Agent configuration is handled by the Operator and stored in Kubernetes Secret resources internally. This way, even plain text passwords are not exposed in any configmap after deployment. + +This configuration will be used for all Instana Agents on all nodes. Visit the [agent configuration documentation](https://docs.instana.io/setup_and_manage/host_agent/#agent-configuration-file) for more details on configuration options. + +_Note:_ This Helm Chart does not support configuring [Multiple Configuration Files](https://www.instana.com/docs/setup_and_manage/host_agent/configuration#multiple-configuration-files). + +### Agent Pod Sizing + +The `agent.pod.requests.cpu`, `agent.pod.requests.memory`, `agent.pod.limits.cpu` and `agent.pod.limits.memory` settings allow you to change the sizing of the `instana-agent` pods. +If you are using the [Kubernetes Sensor Deployment](#kubernetes-sensor-deployment) functionality, you may be able to reduce the default amount of resources, and especially memory, allocated to the Instana agents that monitor your applications. +Actual sizing data depends very much on how many pods, containers and applications are monitored, and how much traces they generate, so we cannot really provide a rule of thumb for the sizing. + +### Bring your own Keys secret + +In case you have automation that creates secrets for you, it may not be desirable for this Helm chart to create a secret containing the `agent.key` and `agent.downloadKey`. +In this case, you can instead specify the name of an alread-existing secret in the namespace in which you install the Instana agent that carries the agent key and download key. + +The secret you specify The secret you specify _must_ have a field called `key`, which would contain the value you would otherwise set to `agent.key`, and _may_ contain a field called `downloadKey`, which would contain the value you would otherwise set to `agent.downloadKey`. + +### Configuring Additional Configuration Files + +[Multiple configuration files](https://www.instana.com/docs/setup_and_manage/host_agent/configuration#multiple-configuration-files) is a capability of the Instana agent that allows for modularity in its configurations files. + +The experimental `agent.configuration.autoMountConfigEntries`, which uses functionality available in Helm 3.1+ to automatically look up the entries of the default `instana-agent` ConfigMap, and mount as agent configuration files in the `instana-agent` container under the `/opt/instana/agent/etc/instana` directory all ConfigMap entries with keys that match the `configuration-*.yaml` scheme. + +**IMPORTANT:** Needs Helm 3.1+ as it is built on the `lookup` function +**IMPORTANT:** Editing the ConfigMap adding keys requires a `helm upgrade` to take effect + +### Configuring Additional Backends + +You may want to have your Instana agents report to multiple backends. +The first backend must be configured as shown in the [Configuring the Instana Backend](#configuring-the-instana-backend); every backend after the first, is configured in the `agent.additionalBackends` list in the [values.yaml](values.yaml) as follows: + +```yaml +agent: + additionalBackends: + # Second backend + - endpointHost: my-instana.instana.io # endpoint host; e.g., my-instana.instana.io + endpointPort: 443 # default is 443, so this line could be omitted + key: ABCDEFG # agent key for this backend + # Third backend + - endpointHost: another-instana.instana.io # endpoint host; e.g., my-instana.instana.io + endpointPort: 1444 # default is 443, so this line could be omitted + key: LMNOPQR # agent key for this backend +``` + +The snippet above configures the agent to report to two additional backends. +The same effect as the above can be accomplished via the command line via: + +```sh +$ helm install -n instana-agent instana-agent ... \ + --repo https://agents.instana.io/helm \ + --set 'agent.additionalBackends[0].endpointHost=my-instana.instana.io' \ + --set 'agent.additionalBackends[0].endpointPort=443' \ + --set 'agent.additionalBackends[0].key=ABCDEFG' \ + --set 'agent.additionalBackends[1].endpointHost=another-instana.instana.io' \ + --set 'agent.additionalBackends[1].endpointPort=1444' \ + --set 'agent.additionalBackends[1].key=LMNOPQR' \ + instana-agent +``` + +_Note:_ There is no hard limitation on the number of backends an Instana agent can report to, although each comes at the cost of a slight increase in CPU and memory consumption. + +### Configuring a Proxy between the Instana agents and the Instana backend + +If your infrastructure uses a proxy, you should ensure that you set values for: + +* `agent.proxyHost` +* `agent.pod.proxyPort` +* `agent.pod.proxyProtocol` +* `agent.pod.proxyUser` +* `agent.pod.proxyPassword` +* `agent.pod.proxyUseDNS` + +#### Same Proxy for Repository and the Instana backend + +If the same proxy is utilized for both backend and repository, configure only the 'Agent' proxy settings using the following parameter: + ``` + --set agent.proxyHost='' + ``` + +#### Separate Proxies for Repository and the Instana backend + +In scenarios where distinct proxy settings are employed for the backend and repository, both proxies must be configured separately. The key is to ensure that `INSTANA_REPOSITORY_PROXY_ENABLED=true` is set. + +To use this variant, execute helm install with the following additional parameters: + +``` +--set agent.proxyHost='Hostname/address of a proxy' +--set agent.env.INSTANA_REPOSITORY_PROXY_ENABLED='true' +--set agent.env.INSTANA_REPOSITORY_PROXY_HOST='Hostname/address of a proxy' +``` +Make sure to replace 'Hostname/address of a proxy' with the actual hostname or address of your proxy. + +### Configuring which Networks the Instana Agent should listen on + +If your infrastructure has multiple networks defined, you might need to allow the agent to listen on all addresses (typically with value set to `*`): + +* `agent.listenAddress` + +### Setup TLS Encryption for Agent Endpoint + +TLS encryption can be added via two variants. +Either an existing secret can be used or a certificate and a private key can be used during the installation. + +#### Using existing secret + +An existing secret of type `kubernetes.io/tls` can be used. +Only the `secretName` must be provided during the installation with `--set 'agent.tls.secretName='`. +The files from the provided secret are then mounted into the agent. + +#### Provide certificate and private key + +On the other side, a certificate and a private key can be added during the installation. +The certificate and private key must be base64 encoded. + +To use this variant, execute `helm install` with the following additional parameters: + +``` +--set 'agent.tls.certificate=' +--set 'agent.tls.key=' +``` + +If `agent.tls.secretName` is set, then `agent.tls.certificate` and `agent.tls.key` are ignored. + +### Development and debugging options + +These options will be rarely used outside of development or debugging of the agent. + +| Parameter | Description | Default | +| ----------------------- | ------------------------------------------------ | ------- | +| `agent.host.repository` | Host path to mount as the agent maven repository | `nil` | + +### Kubernetes Sensor Deployment + + _Note: leader-elector and kubernetes sensor are fully deprecated and can no longer be chosen. Instead, the k8s_sensor will be used._ + +The Helm chart will schedule additional Instana agents running _only_ the Kubernetes sensor that runs in a dedicated `k8sensor` Deployment inside the `instana-agent` namespace. +The pods containing agents that run only the Kubernetes sensor are called `k8sensor` pods. +When `k8s_sensor.deployment.enabled=true`, the `instana-agent` pods running inside the daemonset do _not_ contain the `leader-elector` container, which is instead scheduled inside the `k8sensor` pods. + +The `instana-agent` and `k8sensor` pods share the same configurations in terms of backend-related configurations (including [additional backends](#configuring-additional-backends)). + +The `k8s_sensor.deployment.pod.requests.cpu`, `k8s_sensor.deployment.pod.requests.memory`, `k8s_sensor.deployment.pod.limits.cpu` and `k8s_sensor.deployment.pod.limits.memory` settings, allow you to change the sizing of the `k8sensor` pods. + +### Multiple Zones + +You can list zones to use affinities and tolerations as the basis to associate a specific daemonset per tainted node pool. Each zone will have the following data: + +* `name` (required) - zone name. +* `mode` (optional) - instana agent mode (e.g. APM, INFRASTRUCTURE, etc). +* `affinity` (optional) - standard kubernetes pod affinity list for the daemonset. +* `tolerations` (optional) - standard kubernetes pod toleration list for the daemonset. + +The following is an example that will create 2 zones an api-server and a worker zone: + +```yaml +zones: + - name: workers + mode: APM + - name: api-server + mode: INFRASTRUCTURE + affinity: + nodeAffinity: + requiredDuringSchedulingIgnoredDuringExecution: + nodeSelectorTerms: + - matchExpressions: + - key: node-role.kubernetes.io/control-plane + operator: Exists + tolerations: + - key: "node-role.kubernetes.io/master" + operator: "Exists" + effect: "NoSchedule" +``` + +### Volumes and volumeMounts + +You can define volumes and volumeMounts in the helm configuration to make files available to the agent pod, e.g. to provide client certificates or custom certificate authorities for a sensor to reach a monitored target. + +Example: + +An application requires the usage of a customer provided java key store (JKS) to interact with a monitored process, e.g. IBM MQ. The keystore file is created as secret in the cluster. + +To create the secret, the file can be uploaded with the Kubernetes `kubectl` or Openshift `oc` command line tools. + +```bash +kubectl create secret generic keystore-secret-name --from-file=./application.jks -n instana-agent +``` + +Create a custom values file for the helm installation, e.g. `custom-values.yaml` and adjust the following content accordingly. + +```yaml +agent: + pod: + volumeMounts: + - mountPath: /opt/instana/agent/etc/application.jks + name: jks-mount + subPath: application.jks + volumes: + - name: jks-mount + secret: + secretName: keystore-secret-name +``` + +To deploy the helm chart with the custom mount, specify the configuration as additional parameter. + +```bash +helm install instana-agent \ + --repo https://agents.instana.io/helm \ + --namespace instana-agent \ + --set agent.key='' \ + --set agent.endpointHost='' \ + --set agent.endpointPort=443 \ + --set cluster.name='' \ + --set zone.name='' \ + -f custom-values.yaml \ + instana-agent +``` + +See [Kubernetes documentation](https://kubernetes.io/docs/concepts/storage/volumes/) for other examples of volume options. + + +The mounted file will be available inside the agent pods after the installation. + +``` +$ kubectl exec instana-agent-xxxxx -- ls /opt/instana/agent/etc/application.jks +/opt/instana/agent/etc/application.jks +``` + +## Changelog + +### 2.0.13 + +* Fix rendering of agent.proxyPort in agent custom resource + +### 2.0.12 + +* Update operator to v2.1.19: Deploy operator as a single instance to reduce resource consumption + +### 2.0.11 + +* Update operator to v2.1.18: Adjust logic when to render configuration-opentelemetry.yaml and increase default k8sensor pollrate to 10s + +### 2.0.10 + +* Update operator to v2.1.16: Add ClusterRole/ClusterRoleBinding to instana-agent service account to enable prometheus sensor in Kubernetes +* Update operator to v2.1.15: Change deployment `controller-manager`, and RBAC `manager-role` and `manager-rolebinding` to instana-specifc names `instana-agent-controller-manager`, `instana-agent-clusterrole` and `instana-agent-clusterrolebinding` + +### 2.0.9 + +* Fix rendering of the agent zones + +### 2.0.8 + +* Add option to define custom volumes and volumeMounts for the agent pod + +### 2.0.7 + +* Fix handling of opentelemetry settings, if `spec.opentelemetry.grpc.enabled` or `spec.opentelemetry.http.enabled` are set to false +* Update operator to v2.1.13 + +### 2.0.6 + +* Rename flags for the agent repository mirror configuration + +### 2.0.5 + +* Add flags for the agent repository mirror configuration + +### 2.0.4 + +* Update to operator v2.1.10: Add roles for node metrics and stats for k8sensor + +### 2.0.3 + +* Fix k8sensor deployment rendering + +### 2.0.2 + +* Hardening for endpointPort and configuration parsing + +### 2.0.1 + +* Fix rendering of the `spec.agent.env`, `spec.configuration_yaml`, `spec.agent.image.pullSecrets` + +### 2.0.0 + +* Deploy the instana-agent operator instead of managing agent artifacts directly +* Always use the k8sensor, the deprecated kubernetes sensor is no longer supported (this is an internal change, Kubernetes clusters will still report into the Instana backend) +* BREAKING CHANGE: Due to limitations of helm to manage Custom Resource Definition (CRD) updates, the upgrade requires to apply the CRD from the helm chart crds folder manually. Find more details in the [upgrade](#upgrade) section. + +### 1.2.74 + +* Enable OTLP by default + +### 1.2.73 + +* Fix label for `io.instana/zone` to reflect the real agent mode +* Change the charts flag from ENABLE_AGENT_SOCKET to serviceMesh.enabled +* Add type: DirectoryOrCreate to DaemonSet definitions to ensure required directories exist + +### 1.2.72 + +* Add minReadySeconds field to agent daemonset yaml + +### 1.2.71 + +* Fix usage of digest for pulling images + +### 1.2.70 + +* Allow the configuration of `minReadySeconds` for the agent daemonset and deployment + +### 1.2.69 + +* Add possibility to set annotations for the serviceAccount. + +### 1.2.68 + +* Add leader elector configuration back to allow for proper deprecation + +### 1.2.67 + +* Fix variable name in the K8s deployment + +### 1.2.66 + +* Allign the default Memory requests to 768Mi for the Agent container. + +### 1.2.65 + +* Ensure we have appropriate SCC when running with new K8s sensor. + +### 1.2.64 + +* Remove RBAC not required by agent when kubernetes-sensor is disabed. +* Add settings override for k8s-sensor affinity +* Add optional pod disruption budget for k8s-sensor + +### 1.2.63 + +* Add RBAC required to allow access to /metrics end-points. + +### 1.2.62 + +* Include k8s-sensor resources in the default static YAML definitions + +### 1.2.61 + +* Increase timeout and initialDelay for the Agent container +* Add OTLP ports to headless service + +### 1.2.60 + +* Enable the k8s_sensor by default + +### 1.2.59 + +* Introduce unique selectorLabels and commonLabels for k8s-sensor deployment + +### 1.2.58 + +* Default to `internalTrafficPolicy` instead of `topologyKeys` for rendering of static YAMLs + +### 1.2.57 + +* Fix vulnerability in the leader-elector image + +### 1.2.49 + +* Add zone name to label `io.instana/zone` in daemonset + +### 1.2.48 + +* Set env var INSTANA_KUBERNETES_REDACT_SECRETS true if agent.redactKubernetesSecrets is enabled. +* Use feature PSP flag in k8sensor ClusterRole only when podsecuritypolicy.enable is true. + +### 1.2.47 + +* Roll back the changes from version 1.2.46 to be compatible with the Agent Operator installation + +### 1.2.46 + +* Use K8sensor by default. +* kubernetes.deployment.enabled setting overrides k8s_sensor.deployment.enabled setting. +* Use feature PSP flag in k8sensor ClusterRole only when podsecuritypolicy.enable is true. +* Throw failure if customer specifies proxy with k8sensor. +* Set env var INSTANA_KUBERNETES_REDACT_SECRETS true if agent.redactKubernetesSecrets is enabled. + +### 1.2.45 + +* Use agent key secret in k8sensor deployment. + +### 1.2.44 + +* Add support for enabling the hot-reload of `configuration.yaml` when the default `instana-agent` ConfigMap changes +* Enablement is done via the flag `--set agent.configuration.hotreloadEnabled=true` + +### 1.2.43 + +* Bump leader-elector image to v0.5.16 (Update dependencies) + +### 1.2.42 + +* Add support for creating multiple zones within the same cluster using affinity and tolerations. + +### 1.2.41 + +* Add additional permissions (HPA, ResourceQuotas, etc) to k8sensor clusterrole. + +### 1.2.40 + +* Mount all system mounts mountPropagation: HostToContainer. + +### 1.2.39 + +* Add NO_PROXY to k8sensor deployment to prevent api-server requests from being routed to the proxy. + +### 1.2.38 + +* Fix issue related to EKS version format when enabling OTel service. + +### 1.2.37 + +* Fix issue where cluster_zone is used as cluster_name when `k8s_sensor.deployment.enabled=true`. +* Set `HTTPS_PROXY` in k8s deployment when proxy information is set. + +### 1.2.36 + +* Remove Service `topologyKeys`, which was removed in Kubernetes v1.22. Replaced by `internalTrafficPolicy` which is available with Kubernetes v1.21+. + +### 1.2.35 + +* Fix invalid backend port for new Kubernetes sensor (k8sensor) + +### 1.2.34 + +* Add support for new Kubernetes sensor (k8sensor) + * New Kubernetes sensor can be used via the flag `--set k8s_sensor.deployment.enabled=true` + +### 1.2.33 + +* Bump leader-elector image to v0.5.15 (Update dependencies) + +### 1.2.32 + +* Add support for containerd montoring on TKGI + +### 1.2.31 + +* Bump leader-elector image to v0.5.14 (Update dependencies) + +### 1.2.30 + +* Pull agent image from IBM Cloud Container Registry (icr.io/instana/agent). No code changes have been made. +* Bump leader-elector image to v0.5.13 and pull from IBM Cloud Container Registry (icr.io/instana/leader-elector). No code changes have been made. + +### 1.2.29 + +* Add an additional port to the Instana Agent `Service` definition, for the OpenTelemetry registered IANA port 4317. + +### 1.2.28 + +* Fix deployment when `cluster.name` is not specified. Should be allowed according to docs but previously broke the Pod + when starting up. + +### 1.2.27 + +* Update leader elector image to `0.5.10` to tone down logging and make it configurable + +### 1.2.26 + +* Add TLS support. An existing secret can be used of type `kubernetes.io/tls`. Or provide a certificate and a private key, which creates a new secret. +* Update leader elector image version to 0.5.9 to support PPCle + +### 1.2.25 + +* Add `agent.pod.labels` to add custom labels to the Instana Agent pods + +### 1.2.24 + +* Bump leader-elector image to v0.5.8 which includes a health-check endpoint. Update the `livenessProbe` + correspondingly. + +### 1.2.23 + +* Bump leader-elector image to v0.5.7 to fix a potential Golang bug in the elector + +### 1.2.22 + +* Fix templating scope when defining multiple backends + +### 1.2.21 + +* Internal updates + +### 1.2.20 + +* upgrade leader-elector image to v0.5.6 to enable usage on s390x and arm64 + +### 1.2.18 / 1.2.19 + +* Internal change on generated DaemonSet YAML from the Helm charts + +### 1.2.17 + +* Update Pod Security Policies as the `readOnly: true` appears not to be working for the mount points and + actually causes the Agent deployment to fail when these policies are enforced in the cluster. + +### 1.2.16 + +* Add configuration option for `INSTANA_MVN_REPOSITORY_URL` setting on the Agent container. + +### 1.2.15 + +* Internal pipeline changes. No significant changes to the Helm charts + +### v1.2.14 + +* Update Agent container mounts. Make some read-only as we don't need all mounts with read-write permissions. + Additionally add the mount for `/var/data` which is needed in certain environments for the Agent to function + properly. + +### v1.2.13 + +* Update memory settings specifically for the Kubernetes sensor (Technical Preview) + +### v1.2.11 + +* Simplify setup for using OpenTelemetry and the Prometheus `remote_write` endpoint using the `opentelemetry.enabled` and `prometheus.remoteWrite.enabled` settings, respectively. + +### v1.2.9 + +* **Technical Preview:** Introduce a new mode of running to the Kubernetes sensor using a dedicated deployment. + See the [Kubernetes Sensor Deployment](#kubernetes-sensor-deployment) section for more information. + +### v1.2.7 + +* Fix: Make service opt-in, as it uses functionality (`topologyKeys`) that is available only in K8S 1.17+. + +### v1.2.6 + +* Fix bug that might cause some OpenShift-specific resources to be created in other flavours of Kubernetes. + +### v1.2.5 + +* Introduce the `instana-agent:instana-agent` Kubernetes service that allows you to talk to the Instana agent on the same node. + +### v1.2.3 + +* Bug fix: Extend the built-in Pod Security Policy to cover the Docker socket mount for Tanzu Kubernetes Grid systems. + +### v1.2.1 + +* Support OpenShift 4.x: just add --set openshift=true to the usual settings, and off you go :-) +* Restructure documentation for consistency and readability +* Deprecation: Helm 2 is no longer supported; the minimum Helm API version is now v2, which will make Helm 2 refuse to process the chart. + +### v1.1.10 + +* Some linting of the whitespaces in the generated YAML + +### v1.1.9 + +* Update the README to replace all references of `stable/instana-agent` with specifically setting the repo flag to `https://agents.instana.io/helm`. +* Add support for TKGI and PKS systems, providing a workaround for the [unexpected Docker socket location](https://github.com/cloudfoundry-incubator/kubo-release/issues/329). + +### v1.1.7 + +* Store the cluster name in a new `cluster-name` entry of the `instana-agent` ConfigMap rather than directly as the value of the `INSTANA_KUBERNETES_CLUSTER_NAME`, so that you can edit the cluster name in the ConfigMap in deployments like VMware Tanzu Kubernetes Grid in which, when installing the Instana agent over the [Instana tile](https://www.instana.com/docs/setup_and_manage/host_agent/on/vmware_tanzu), you do not have directly control to the configuration of the cluster name. +If you edit the ConfigMap, you will need to delete the `instana-agent` pods for its new value to take effect. + +### v1.1.6 + +* Allow to use user-specified memony measurement units in `agent.pod.requests.memory` and `agent.pod.limits.memory`. + If the value set is numerical, the Chart will assume it to be expressed in `Mi` for backwards compatibility. +* Exposed `agent.updateStrategy.type` and `agent.updateStrategy.rollingUpdate.maxUnavailable` settings. + +### v1.1.5 + +Restore compatibility with Helm 2 that was broken in v1.1.4 by the usage of the `lookup` function, a function actually introduced only with Helm 3.1. +Coincidentally, this has been an _excellent_ opportunity to introduce `helm lint` to our validation pipeline and end-to-end tests with Helm 2 ;-) + +### v1.1.4 + +* Bring-your-own secret for agent keys: using the new `agent.keysSecret` setting, you can specify the name of the secret that contains the agent key and, optionally, the download key; refer to [Bring your own Keys secret](#bring-your-own-keys-secret) for more details. +* Add support for affinities for the instana agent pod via the `agent.pod.affinity` setting. +* Put some love into the ArtifactHub.io metadata; likely to add some more improvements related to this over time. + +### v1.1.3 + +* No new features, just ironing some wrinkles out of our release automation. + +### v1.1.2 + +* Improvement: Seamless support for Instana static agent images: When using an `agent.image.name` starting with `containers.instana.io`, automatically create a secret called `containers-instana-io` containing the `.dockerconfigjson` for `containers.instana.io`, using `_` as username and `agent.downloadKey` or, if missing, `agent.key` as password. If you want to control the creation of the image pull secret, or disable it, you can use `agent.image.pullSecrets`, passing to it the YAML to use for the `imagePullSecrets` field of the Daemonset spec, including an empty array `[]` to mount no pull secrets, no matter what. + +### v1.1.1 + +* Fix: Recreate the `instana-agent` pods when there is a change in one of the following configuration, which are mapped to the chart-managed ConfigMap: + +* `agent.configuration_yaml` +* `agent.additional_backends` + +The pod recreation is achieved by annotating the `instana-agent` Pod with a new `instana-configuration-hash` annotation that has, as value, the SHA-1 hash of the configurations used to populate the ConfigMap. +This way, when the configuration changes, the respective change in the `instana-configuration-hash` annotation will cause the agent pods to be recreated. +This technique has been described at [1] (or, at least, that is were we learned about it) and it is pretty cool :-) + +### v1.1.0 + +* Improvement: The `instana-agent` Helm chart has a new home at `https://agents.instana.io/helm` and `https://github.com/instana/helm-charts/instana-agent`! +This release is functionally equivalent to `1.0.34`, but we bumped the major to denote the new location ;-) + +## References + +[1] ["Using Kubernetes Helm to push ConfigMap changes to your Deployments", by Sander Knape; Mar 7, 2019](https://sanderknape.com/2019/03/kubernetes-helm-configmaps-changes-deployments/) diff --git a/charts/instana/instana-agent/2.0.13/app-readme.md b/charts/instana/instana-agent/2.0.13/app-readme.md new file mode 100644 index 0000000000..0e26c86231 --- /dev/null +++ b/charts/instana/instana-agent/2.0.13/app-readme.md @@ -0,0 +1,5 @@ +# Instana + +Instana is an [APM solution(https://www.instana.com/) built for microservices that enables IT Ops to build applications faster and deliver higher quality services by automating monitoring tracing and root cause analysis. This solution is optimized for [Rancher](https://www.instana.com/rancher/). + +This chart adds the Instana Agent to all schedulable nodes in your cluster via a `DaemonSet`. diff --git a/charts/instana/instana-agent/2.0.13/crds/operator_customresourcedefinition_agents_instana_io.yml b/charts/instana/instana-agent/2.0.13/crds/operator_customresourcedefinition_agents_instana_io.yml new file mode 100644 index 0000000000..2dc9540dc5 --- /dev/null +++ b/charts/instana/instana-agent/2.0.13/crds/operator_customresourcedefinition_agents_instana_io.yml @@ -0,0 +1,6195 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.14.0 + name: agents.instana.io +spec: + group: instana.io + names: + categories: + - monitoring + - openshift-optional + kind: InstanaAgent + listKind: InstanaAgentList + plural: agents + shortNames: + - ia + singular: agent + scope: Namespaced + versions: + - name: v1 + schema: + openAPIV3Schema: + description: InstanaAgent is the Schema for the agents API + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + description: InstanaAgentSpec defines the desired state of the Instana Agent + properties: + agent: + description: Agent deployment specific fields. + properties: + additionalBackends: + description: |- + These are additional backends the Instana agent will report to besides + the one configured via the `agent.endpointHost`, `agent.endpointPort` and `agent.key` setting. + items: + properties: + endpointHost: + type: string + endpointPort: + type: string + key: + type: string + required: + - endpointHost + - endpointPort + type: object + type: array + agentReleaseRepoMirrorPassword: + description: Sets the AGENT_RELEASE_REPOSITORY_MIRROR_PASSWORD environment variable + type: string + agentReleaseRepoMirrorUrl: + description: Sets the AGENT_RELEASE_REPOSITORY_MIRROR_URL environment variable + type: string + agentReleaseRepoMirrorUsername: + description: Sets the AGENT_RELEASE_REPOSITORY_MIRROR_USERNAME environment variable + type: string + configuration_yaml: + description: Supply Agent configuration e.g. for configuring certain Sensors. + type: string + downloadKey: + description: |- + The DownloadKey, sometimes known as "sales key", that allows you to download software from Instana. It might be needed to + specify this in addition to the Key. + type: string + endpointHost: + description: EndpointHost is the hostname of the Instana server your agents will connect to. + type: string + endpointPort: + description: EndpointPort is the port number (as a String) of the Instana server your agents will connect to. + type: string + env: + additionalProperties: + type: string + description: |- + Use the `env` field to set additional environment variables for the Instana Agent, for example: + env: + INSTANA_AGENT_TAGS: dev + type: object + host: + description: Host sets a host path to be mounted as the Agent Maven repository (mainly for debugging or development purposes) + properties: + repository: + type: string + type: object + image: + description: Override the container image used for the Instana Agent pods. + properties: + digest: + description: |- + Digest (a.k.a. Image ID) of the agent container image. If specified, it has priority over `agent.image.tag`, + which will then be ignored. + type: string + name: + description: Name is the name of the container image of the Instana agent. + type: string + pullPolicy: + description: PullPolicy specifies when to pull the image container. + type: string + pullSecrets: + description: |- + PullSecrets allows you to override the default pull secret that is created when `agent.image.name` starts with + "containers.instana.io". Setting `agent.image.pullSecrets` prevents the creation of the default "containers-instana-io" secret. + items: + description: |- + LocalObjectReference contains enough information to let you locate the + referenced object inside the same namespace. + properties: + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + TODO: Add other useful fields. apiVersion, kind, uid? + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Drop `kubebuilder:default` when controller-gen doesn't need it https://github.com/kubernetes-sigs/kubebuilder/issues/3896. + type: string + type: object + x-kubernetes-map-type: atomic + type: array + tag: + description: Tag is the name of the agent container image; if `agent.image.digest` is specified, this property is ignored. + type: string + type: object + instanaMvnRepoFeaturesPath: + description: Sets the INSTANA_MVN_REPOSITORY_FEATURES_PATH environment variable + type: string + instanaMvnRepoSharedPath: + description: Sets the INSTANA_MVN_REPOSITORY_SHARED_PATH environment variable + type: string + instanaMvnRepoUrl: + description: |- + Override for the Maven repository URL when the Agent needs to connect to a locally provided Maven repository 'proxy' + Alternative to `Host` for referencing a different Maven repo. + type: string + instanaSharedRepoMirrorPassword: + description: Sets the INSTANA_SHARED_REPOSITORY_MIRROR_PASSWORD environment variable + type: string + instanaSharedRepoMirrorUrl: + description: Sets the INSTANA_SHARED_REPOSITORY_MIRROR_URL environment variable + type: string + instanaSharedRepoMirrorUsername: + description: Sets the INSTANA_SHARED_REPOSITORY_MIRROR_USERNAME environment variable + type: string + key: + description: Key is the secret token which your agent uses to authenticate to Instana's servers. + type: string + keysSecret: + description: |- + Rather than specifying the Key and optionally the DownloadKey, you can "bring your + own secret" creating it in the namespace in which you install the `instana-agent` and + specify its name in the `KeysSecret` field. The secret you create must contain a field called `key` and optionally one + called `downloadKey`, which contain, respectively, the values you'd otherwise set in `.agent.key` and `agent.downloadKey`. + type: string + listenAddress: + description: |- + ListenAddress is the IP addresses the Agent HTTP server will listen on. Normally this will just be localhost (`127.0.0.1`), + the pod public IP and any container runtime bridge interfaces. Set `listenAddress: *` for making the Agent listen on all + network interfaces. + type: string + minReadySeconds: + description: The minimum number of seconds for which a newly created Pod should be ready without any of its containers crashing, for it to be considered available + type: integer + mode: + description: |- + Set agent mode, possible options are APM, INFRASTRUCTURE or AWS. KUBERNETES should not be used but instead enabled via + `kubernetes.deployment.enabled: true`. + type: string + pod: + description: Override Agent Pod specific settings such as annotations, labels and resources. + properties: + affinity: + description: |- + agent.pod.affinity are affinities to influence agent pod assignment. + https://kubernetes.io/docs/concepts/configuration/taint-and-toleration/ + properties: + nodeAffinity: + description: Describes node affinity scheduling rules for the pod. + properties: + preferredDuringSchedulingIgnoredDuringExecution: + description: |- + The scheduler will prefer to schedule pods to nodes that satisfy + the affinity expressions specified by this field, but it may choose + a node that violates one or more of the expressions. The node that is + most preferred is the one with the greatest sum of weights, i.e. + for each node that meets all of the scheduling requirements (resource + request, requiredDuringScheduling affinity expressions, etc.), + compute a sum by iterating through the elements of this field and adding + "weight" to the sum if the node matches the corresponding matchExpressions; the + node(s) with the highest sum are the most preferred. + items: + description: |- + An empty preferred scheduling term matches all objects with implicit weight 0 + (i.e. it's a no-op). A null preferred scheduling term matches no objects (i.e. is also a no-op). + properties: + preference: + description: A node selector term, associated with the corresponding weight. + properties: + matchExpressions: + description: A list of node selector requirements by node's labels. + items: + description: |- + A node selector requirement is a selector that contains values, a key, and an operator + that relates the key and values. + properties: + key: + description: The label key that the selector applies to. + type: string + operator: + description: |- + Represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt. + type: string + values: + description: |- + An array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. If the operator is Gt or Lt, the values + array must have a single element, which will be interpreted as an integer. + This array is replaced during a strategic merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchFields: + description: A list of node selector requirements by node's fields. + items: + description: |- + A node selector requirement is a selector that contains values, a key, and an operator + that relates the key and values. + properties: + key: + description: The label key that the selector applies to. + type: string + operator: + description: |- + Represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt. + type: string + values: + description: |- + An array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. If the operator is Gt or Lt, the values + array must have a single element, which will be interpreted as an integer. + This array is replaced during a strategic merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + type: object + x-kubernetes-map-type: atomic + weight: + description: Weight associated with matching the corresponding nodeSelectorTerm, in the range 1-100. + format: int32 + type: integer + required: + - preference + - weight + type: object + type: array + x-kubernetes-list-type: atomic + requiredDuringSchedulingIgnoredDuringExecution: + description: |- + If the affinity requirements specified by this field are not met at + scheduling time, the pod will not be scheduled onto the node. + If the affinity requirements specified by this field cease to be met + at some point during pod execution (e.g. due to an update), the system + may or may not try to eventually evict the pod from its node. + properties: + nodeSelectorTerms: + description: Required. A list of node selector terms. The terms are ORed. + items: + description: |- + A null or empty node selector term matches no objects. The requirements of + them are ANDed. + The TopologySelectorTerm type implements a subset of the NodeSelectorTerm. + properties: + matchExpressions: + description: A list of node selector requirements by node's labels. + items: + description: |- + A node selector requirement is a selector that contains values, a key, and an operator + that relates the key and values. + properties: + key: + description: The label key that the selector applies to. + type: string + operator: + description: |- + Represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt. + type: string + values: + description: |- + An array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. If the operator is Gt or Lt, the values + array must have a single element, which will be interpreted as an integer. + This array is replaced during a strategic merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchFields: + description: A list of node selector requirements by node's fields. + items: + description: |- + A node selector requirement is a selector that contains values, a key, and an operator + that relates the key and values. + properties: + key: + description: The label key that the selector applies to. + type: string + operator: + description: |- + Represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt. + type: string + values: + description: |- + An array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. If the operator is Gt or Lt, the values + array must have a single element, which will be interpreted as an integer. + This array is replaced during a strategic merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + type: object + x-kubernetes-map-type: atomic + type: array + x-kubernetes-list-type: atomic + required: + - nodeSelectorTerms + type: object + x-kubernetes-map-type: atomic + type: object + podAffinity: + description: Describes pod affinity scheduling rules (e.g. co-locate this pod in the same node, zone, etc. as some other pod(s)). + properties: + preferredDuringSchedulingIgnoredDuringExecution: + description: |- + The scheduler will prefer to schedule pods to nodes that satisfy + the affinity expressions specified by this field, but it may choose + a node that violates one or more of the expressions. The node that is + most preferred is the one with the greatest sum of weights, i.e. + for each node that meets all of the scheduling requirements (resource + request, requiredDuringScheduling affinity expressions, etc.), + compute a sum by iterating through the elements of this field and adding + "weight" to the sum if the node has pods which matches the corresponding podAffinityTerm; the + node(s) with the highest sum are the most preferred. + items: + description: The weights of all of the matched WeightedPodAffinityTerm fields are added per-node to find the most preferred node(s) + properties: + podAffinityTerm: + description: Required. A pod affinity term, associated with the corresponding weight. + properties: + labelSelector: + description: |- + A label query over a set of resources, in this case pods. + If it's null, this PodAffinityTerm matches with no Pods. + properties: + matchExpressions: + description: matchExpressions is a list of label selector requirements. The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + matchLabelKeys: + description: |- + MatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `labelSelector` as `key in (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both matchLabelKeys and labelSelector. + Also, matchLabelKeys cannot be set when labelSelector isn't set. + This is an alpha field and requires enabling MatchLabelKeysInPodAffinity feature gate. + items: + type: string + type: array + x-kubernetes-list-type: atomic + mismatchLabelKeys: + description: |- + MismatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `labelSelector` as `key notin (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both mismatchLabelKeys and labelSelector. + Also, mismatchLabelKeys cannot be set when labelSelector isn't set. + This is an alpha field and requires enabling MatchLabelKeysInPodAffinity feature gate. + items: + type: string + type: array + x-kubernetes-list-type: atomic + namespaceSelector: + description: |- + A label query over the set of namespaces that the term applies to. + The term is applied to the union of the namespaces selected by this field + and the ones listed in the namespaces field. + null selector and null or empty namespaces list means "this pod's namespace". + An empty selector ({}) matches all namespaces. + properties: + matchExpressions: + description: matchExpressions is a list of label selector requirements. The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + namespaces: + description: |- + namespaces specifies a static list of namespace names that the term applies to. + The term is applied to the union of the namespaces listed in this field + and the ones selected by namespaceSelector. + null or empty namespaces list and null namespaceSelector means "this pod's namespace". + items: + type: string + type: array + x-kubernetes-list-type: atomic + topologyKey: + description: |- + This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching + the labelSelector in the specified namespaces, where co-located is defined as running on a node + whose value of the label with key topologyKey matches that of any node on which any of the + selected pods is running. + Empty topologyKey is not allowed. + type: string + required: + - topologyKey + type: object + weight: + description: |- + weight associated with matching the corresponding podAffinityTerm, + in the range 1-100. + format: int32 + type: integer + required: + - podAffinityTerm + - weight + type: object + type: array + x-kubernetes-list-type: atomic + requiredDuringSchedulingIgnoredDuringExecution: + description: |- + If the affinity requirements specified by this field are not met at + scheduling time, the pod will not be scheduled onto the node. + If the affinity requirements specified by this field cease to be met + at some point during pod execution (e.g. due to a pod label update), the + system may or may not try to eventually evict the pod from its node. + When there are multiple elements, the lists of nodes corresponding to each + podAffinityTerm are intersected, i.e. all terms must be satisfied. + items: + description: |- + Defines a set of pods (namely those matching the labelSelector + relative to the given namespace(s)) that this pod should be + co-located (affinity) or not co-located (anti-affinity) with, + where co-located is defined as running on a node whose value of + the label with key matches that of any node on which + a pod of the set of pods is running + properties: + labelSelector: + description: |- + A label query over a set of resources, in this case pods. + If it's null, this PodAffinityTerm matches with no Pods. + properties: + matchExpressions: + description: matchExpressions is a list of label selector requirements. The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + matchLabelKeys: + description: |- + MatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `labelSelector` as `key in (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both matchLabelKeys and labelSelector. + Also, matchLabelKeys cannot be set when labelSelector isn't set. + This is an alpha field and requires enabling MatchLabelKeysInPodAffinity feature gate. + items: + type: string + type: array + x-kubernetes-list-type: atomic + mismatchLabelKeys: + description: |- + MismatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `labelSelector` as `key notin (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both mismatchLabelKeys and labelSelector. + Also, mismatchLabelKeys cannot be set when labelSelector isn't set. + This is an alpha field and requires enabling MatchLabelKeysInPodAffinity feature gate. + items: + type: string + type: array + x-kubernetes-list-type: atomic + namespaceSelector: + description: |- + A label query over the set of namespaces that the term applies to. + The term is applied to the union of the namespaces selected by this field + and the ones listed in the namespaces field. + null selector and null or empty namespaces list means "this pod's namespace". + An empty selector ({}) matches all namespaces. + properties: + matchExpressions: + description: matchExpressions is a list of label selector requirements. The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + namespaces: + description: |- + namespaces specifies a static list of namespace names that the term applies to. + The term is applied to the union of the namespaces listed in this field + and the ones selected by namespaceSelector. + null or empty namespaces list and null namespaceSelector means "this pod's namespace". + items: + type: string + type: array + x-kubernetes-list-type: atomic + topologyKey: + description: |- + This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching + the labelSelector in the specified namespaces, where co-located is defined as running on a node + whose value of the label with key topologyKey matches that of any node on which any of the + selected pods is running. + Empty topologyKey is not allowed. + type: string + required: + - topologyKey + type: object + type: array + x-kubernetes-list-type: atomic + type: object + podAntiAffinity: + description: Describes pod anti-affinity scheduling rules (e.g. avoid putting this pod in the same node, zone, etc. as some other pod(s)). + properties: + preferredDuringSchedulingIgnoredDuringExecution: + description: |- + The scheduler will prefer to schedule pods to nodes that satisfy + the anti-affinity expressions specified by this field, but it may choose + a node that violates one or more of the expressions. The node that is + most preferred is the one with the greatest sum of weights, i.e. + for each node that meets all of the scheduling requirements (resource + request, requiredDuringScheduling anti-affinity expressions, etc.), + compute a sum by iterating through the elements of this field and adding + "weight" to the sum if the node has pods which matches the corresponding podAffinityTerm; the + node(s) with the highest sum are the most preferred. + items: + description: The weights of all of the matched WeightedPodAffinityTerm fields are added per-node to find the most preferred node(s) + properties: + podAffinityTerm: + description: Required. A pod affinity term, associated with the corresponding weight. + properties: + labelSelector: + description: |- + A label query over a set of resources, in this case pods. + If it's null, this PodAffinityTerm matches with no Pods. + properties: + matchExpressions: + description: matchExpressions is a list of label selector requirements. The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + matchLabelKeys: + description: |- + MatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `labelSelector` as `key in (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both matchLabelKeys and labelSelector. + Also, matchLabelKeys cannot be set when labelSelector isn't set. + This is an alpha field and requires enabling MatchLabelKeysInPodAffinity feature gate. + items: + type: string + type: array + x-kubernetes-list-type: atomic + mismatchLabelKeys: + description: |- + MismatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `labelSelector` as `key notin (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both mismatchLabelKeys and labelSelector. + Also, mismatchLabelKeys cannot be set when labelSelector isn't set. + This is an alpha field and requires enabling MatchLabelKeysInPodAffinity feature gate. + items: + type: string + type: array + x-kubernetes-list-type: atomic + namespaceSelector: + description: |- + A label query over the set of namespaces that the term applies to. + The term is applied to the union of the namespaces selected by this field + and the ones listed in the namespaces field. + null selector and null or empty namespaces list means "this pod's namespace". + An empty selector ({}) matches all namespaces. + properties: + matchExpressions: + description: matchExpressions is a list of label selector requirements. The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + namespaces: + description: |- + namespaces specifies a static list of namespace names that the term applies to. + The term is applied to the union of the namespaces listed in this field + and the ones selected by namespaceSelector. + null or empty namespaces list and null namespaceSelector means "this pod's namespace". + items: + type: string + type: array + x-kubernetes-list-type: atomic + topologyKey: + description: |- + This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching + the labelSelector in the specified namespaces, where co-located is defined as running on a node + whose value of the label with key topologyKey matches that of any node on which any of the + selected pods is running. + Empty topologyKey is not allowed. + type: string + required: + - topologyKey + type: object + weight: + description: |- + weight associated with matching the corresponding podAffinityTerm, + in the range 1-100. + format: int32 + type: integer + required: + - podAffinityTerm + - weight + type: object + type: array + x-kubernetes-list-type: atomic + requiredDuringSchedulingIgnoredDuringExecution: + description: |- + If the anti-affinity requirements specified by this field are not met at + scheduling time, the pod will not be scheduled onto the node. + If the anti-affinity requirements specified by this field cease to be met + at some point during pod execution (e.g. due to a pod label update), the + system may or may not try to eventually evict the pod from its node. + When there are multiple elements, the lists of nodes corresponding to each + podAffinityTerm are intersected, i.e. all terms must be satisfied. + items: + description: |- + Defines a set of pods (namely those matching the labelSelector + relative to the given namespace(s)) that this pod should be + co-located (affinity) or not co-located (anti-affinity) with, + where co-located is defined as running on a node whose value of + the label with key matches that of any node on which + a pod of the set of pods is running + properties: + labelSelector: + description: |- + A label query over a set of resources, in this case pods. + If it's null, this PodAffinityTerm matches with no Pods. + properties: + matchExpressions: + description: matchExpressions is a list of label selector requirements. The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + matchLabelKeys: + description: |- + MatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `labelSelector` as `key in (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both matchLabelKeys and labelSelector. + Also, matchLabelKeys cannot be set when labelSelector isn't set. + This is an alpha field and requires enabling MatchLabelKeysInPodAffinity feature gate. + items: + type: string + type: array + x-kubernetes-list-type: atomic + mismatchLabelKeys: + description: |- + MismatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `labelSelector` as `key notin (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both mismatchLabelKeys and labelSelector. + Also, mismatchLabelKeys cannot be set when labelSelector isn't set. + This is an alpha field and requires enabling MatchLabelKeysInPodAffinity feature gate. + items: + type: string + type: array + x-kubernetes-list-type: atomic + namespaceSelector: + description: |- + A label query over the set of namespaces that the term applies to. + The term is applied to the union of the namespaces selected by this field + and the ones listed in the namespaces field. + null selector and null or empty namespaces list means "this pod's namespace". + An empty selector ({}) matches all namespaces. + properties: + matchExpressions: + description: matchExpressions is a list of label selector requirements. The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + namespaces: + description: |- + namespaces specifies a static list of namespace names that the term applies to. + The term is applied to the union of the namespaces listed in this field + and the ones selected by namespaceSelector. + null or empty namespaces list and null namespaceSelector means "this pod's namespace". + items: + type: string + type: array + x-kubernetes-list-type: atomic + topologyKey: + description: |- + This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching + the labelSelector in the specified namespaces, where co-located is defined as running on a node + whose value of the label with key topologyKey matches that of any node on which any of the + selected pods is running. + Empty topologyKey is not allowed. + type: string + required: + - topologyKey + type: object + type: array + x-kubernetes-list-type: atomic + type: object + type: object + annotations: + additionalProperties: + type: string + description: agent.pod.annotations are additional annotations to be added to the agent pods. + type: object + claims: + description: |- + Claims lists the names of resources, defined in spec.resourceClaims, + that are used by this container. + + + This is an alpha field and requires enabling the + DynamicResourceAllocation feature gate. + + + This field is immutable. It can only be set for containers. + items: + description: ResourceClaim references one entry in PodSpec.ResourceClaims. + properties: + name: + description: |- + Name must match the name of one entry in pod.spec.resourceClaims of + the Pod where this field is used. It makes that resource available + inside a container. + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + labels: + additionalProperties: + type: string + description: agent.pod.labels are additional labels to be added to the agent pods. + type: object + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: |- + Limits describes the maximum amount of compute resources allowed. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + type: object + nodeSelector: + additionalProperties: + type: string + type: object + priorityClassName: + description: |- + agent.pod.priorityClassName is the name of an existing PriorityClass that should be set on the agent pods + https://kubernetes.io/docs/concepts/configuration/pod-priority-preemption/ + type: string + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: |- + Requests describes the minimum amount of compute resources required. + If Requests is omitted for a container, it defaults to Limits if that is explicitly specified, + otherwise to an implementation-defined value. Requests cannot exceed Limits. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + type: object + tolerations: + description: agent.pod.tolerations are tolerations to influence agent pod assignment. + items: + description: |- + The pod this Toleration is attached to tolerates any taint that matches + the triple using the matching operator . + properties: + effect: + description: |- + Effect indicates the taint effect to match. Empty means match all taint effects. + When specified, allowed values are NoSchedule, PreferNoSchedule and NoExecute. + type: string + key: + description: |- + Key is the taint key that the toleration applies to. Empty means match all taint keys. + If the key is empty, operator must be Exists; this combination means to match all values and all keys. + type: string + operator: + description: |- + Operator represents a key's relationship to the value. + Valid operators are Exists and Equal. Defaults to Equal. + Exists is equivalent to wildcard for value, so that a pod can + tolerate all taints of a particular category. + type: string + tolerationSeconds: + description: |- + TolerationSeconds represents the period of time the toleration (which must be + of effect NoExecute, otherwise this field is ignored) tolerates the taint. By default, + it is not set, which means tolerate the taint forever (do not evict). Zero and + negative values will be treated as 0 (evict immediately) by the system. + format: int64 + type: integer + value: + description: |- + Value is the taint value the toleration matches to. + If the operator is Exists, the value should be empty, otherwise just a regular string. + type: string + type: object + type: array + volumeMounts: + description: Set additional volume mounts for the agent pod. + items: + description: VolumeMount describes a mounting of a Volume within a container. + properties: + mountPath: + description: |- + Path within the container at which the volume should be mounted. Must + not contain ':'. + type: string + mountPropagation: + description: |- + mountPropagation determines how mounts are propagated from the host + to container and the other way around. + When not set, MountPropagationNone is used. + This field is beta in 1.10. + When RecursiveReadOnly is set to IfPossible or to Enabled, MountPropagation must be None or unspecified + (which defaults to None). + type: string + name: + description: This must match the Name of a Volume. + type: string + readOnly: + description: |- + Mounted read-only if true, read-write otherwise (false or unspecified). + Defaults to false. + type: boolean + recursiveReadOnly: + description: |- + RecursiveReadOnly specifies whether read-only mounts should be handled + recursively. + + + If ReadOnly is false, this field has no meaning and must be unspecified. + + + If ReadOnly is true, and this field is set to Disabled, the mount is not made + recursively read-only. If this field is set to IfPossible, the mount is made + recursively read-only, if it is supported by the container runtime. If this + field is set to Enabled, the mount is made recursively read-only if it is + supported by the container runtime, otherwise the pod will not be started and + an error will be generated to indicate the reason. + + + If this field is set to IfPossible or Enabled, MountPropagation must be set to + None (or be unspecified, which defaults to None). + + + If this field is not specified, it is treated as an equivalent of Disabled. + type: string + subPath: + description: |- + Path within the volume from which the container's volume should be mounted. + Defaults to "" (volume's root). + type: string + subPathExpr: + description: |- + Expanded path within the volume from which the container's volume should be mounted. + Behaves similarly to SubPath but environment variable references $(VAR_NAME) are expanded using the container's environment. + Defaults to "" (volume's root). + SubPathExpr and SubPath are mutually exclusive. + type: string + required: + - mountPath + - name + type: object + type: array + volumes: + description: Set additional volumes for the agent pod. + items: + description: Volume represents a named volume in a pod that may be accessed by any container in the pod. + properties: + awsElasticBlockStore: + description: |- + awsElasticBlockStore represents an AWS Disk resource that is attached to a + kubelet's host machine and then exposed to the pod. + More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore + properties: + fsType: + description: |- + fsType is the filesystem type of the volume that you want to mount. + Tip: Ensure that the filesystem type is supported by the host operating system. + Examples: "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. + More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore + TODO: how do we prevent errors in the filesystem from compromising the machine + type: string + partition: + description: |- + partition is the partition in the volume that you want to mount. + If omitted, the default is to mount by volume name. + Examples: For volume /dev/sda1, you specify the partition as "1". + Similarly, the volume partition for /dev/sda is "0" (or you can leave the property empty). + format: int32 + type: integer + readOnly: + description: |- + readOnly value true will force the readOnly setting in VolumeMounts. + More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore + type: boolean + volumeID: + description: |- + volumeID is unique ID of the persistent disk resource in AWS (Amazon EBS volume). + More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore + type: string + required: + - volumeID + type: object + azureDisk: + description: azureDisk represents an Azure Data Disk mount on the host and bind mount to the pod. + properties: + cachingMode: + description: 'cachingMode is the Host Caching mode: None, Read Only, Read Write.' + type: string + diskName: + description: diskName is the Name of the data disk in the blob storage + type: string + diskURI: + description: diskURI is the URI of data disk in the blob storage + type: string + fsType: + description: |- + fsType is Filesystem type to mount. + Must be a filesystem type supported by the host operating system. + Ex. "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. + type: string + kind: + description: 'kind expected values are Shared: multiple blob disks per storage account Dedicated: single blob disk per storage account Managed: azure managed data disk (only in managed availability set). defaults to shared' + type: string + readOnly: + description: |- + readOnly Defaults to false (read/write). ReadOnly here will force + the ReadOnly setting in VolumeMounts. + type: boolean + required: + - diskName + - diskURI + type: object + azureFile: + description: azureFile represents an Azure File Service mount on the host and bind mount to the pod. + properties: + readOnly: + description: |- + readOnly defaults to false (read/write). ReadOnly here will force + the ReadOnly setting in VolumeMounts. + type: boolean + secretName: + description: secretName is the name of secret that contains Azure Storage Account Name and Key + type: string + shareName: + description: shareName is the azure share Name + type: string + required: + - secretName + - shareName + type: object + cephfs: + description: cephFS represents a Ceph FS mount on the host that shares a pod's lifetime + properties: + monitors: + description: |- + monitors is Required: Monitors is a collection of Ceph monitors + More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it + items: + type: string + type: array + x-kubernetes-list-type: atomic + path: + description: 'path is Optional: Used as the mounted root, rather than the full Ceph tree, default is /' + type: string + readOnly: + description: |- + readOnly is Optional: Defaults to false (read/write). ReadOnly here will force + the ReadOnly setting in VolumeMounts. + More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it + type: boolean + secretFile: + description: |- + secretFile is Optional: SecretFile is the path to key ring for User, default is /etc/ceph/user.secret + More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it + type: string + secretRef: + description: |- + secretRef is Optional: SecretRef is reference to the authentication secret for User, default is empty. + More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it + properties: + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + TODO: Add other useful fields. apiVersion, kind, uid? + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Drop `kubebuilder:default` when controller-gen doesn't need it https://github.com/kubernetes-sigs/kubebuilder/issues/3896. + type: string + type: object + x-kubernetes-map-type: atomic + user: + description: |- + user is optional: User is the rados user name, default is admin + More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it + type: string + required: + - monitors + type: object + cinder: + description: |- + cinder represents a cinder volume attached and mounted on kubelets host machine. + More info: https://examples.k8s.io/mysql-cinder-pd/README.md + properties: + fsType: + description: |- + fsType is the filesystem type to mount. + Must be a filesystem type supported by the host operating system. + Examples: "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. + More info: https://examples.k8s.io/mysql-cinder-pd/README.md + type: string + readOnly: + description: |- + readOnly defaults to false (read/write). ReadOnly here will force + the ReadOnly setting in VolumeMounts. + More info: https://examples.k8s.io/mysql-cinder-pd/README.md + type: boolean + secretRef: + description: |- + secretRef is optional: points to a secret object containing parameters used to connect + to OpenStack. + properties: + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + TODO: Add other useful fields. apiVersion, kind, uid? + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Drop `kubebuilder:default` when controller-gen doesn't need it https://github.com/kubernetes-sigs/kubebuilder/issues/3896. + type: string + type: object + x-kubernetes-map-type: atomic + volumeID: + description: |- + volumeID used to identify the volume in cinder. + More info: https://examples.k8s.io/mysql-cinder-pd/README.md + type: string + required: + - volumeID + type: object + configMap: + description: configMap represents a configMap that should populate this volume + properties: + defaultMode: + description: |- + defaultMode is optional: mode bits used to set permissions on created files by default. + Must be an octal value between 0000 and 0777 or a decimal value between 0 and 511. + YAML accepts both octal and decimal values, JSON requires decimal values for mode bits. + Defaults to 0644. + Directories within the path are not affected by this setting. + This might be in conflict with other options that affect the file + mode, like fsGroup, and the result can be other mode bits set. + format: int32 + type: integer + items: + description: |- + items if unspecified, each key-value pair in the Data field of the referenced + ConfigMap will be projected into the volume as a file whose name is the + key and content is the value. If specified, the listed keys will be + projected into the specified paths, and unlisted keys will not be + present. If a key is specified which is not present in the ConfigMap, + the volume setup will error unless it is marked optional. Paths must be + relative and may not contain the '..' path or start with '..'. + items: + description: Maps a string key to a path within a volume. + properties: + key: + description: key is the key to project. + type: string + mode: + description: |- + mode is Optional: mode bits used to set permissions on this file. + Must be an octal value between 0000 and 0777 or a decimal value between 0 and 511. + YAML accepts both octal and decimal values, JSON requires decimal values for mode bits. + If not specified, the volume defaultMode will be used. + This might be in conflict with other options that affect the file + mode, like fsGroup, and the result can be other mode bits set. + format: int32 + type: integer + path: + description: |- + path is the relative path of the file to map the key to. + May not be an absolute path. + May not contain the path element '..'. + May not start with the string '..'. + type: string + required: + - key + - path + type: object + type: array + x-kubernetes-list-type: atomic + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + TODO: Add other useful fields. apiVersion, kind, uid? + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Drop `kubebuilder:default` when controller-gen doesn't need it https://github.com/kubernetes-sigs/kubebuilder/issues/3896. + type: string + optional: + description: optional specify whether the ConfigMap or its keys must be defined + type: boolean + type: object + x-kubernetes-map-type: atomic + csi: + description: csi (Container Storage Interface) represents ephemeral storage that is handled by certain external CSI drivers (Beta feature). + properties: + driver: + description: |- + driver is the name of the CSI driver that handles this volume. + Consult with your admin for the correct name as registered in the cluster. + type: string + fsType: + description: |- + fsType to mount. Ex. "ext4", "xfs", "ntfs". + If not provided, the empty value is passed to the associated CSI driver + which will determine the default filesystem to apply. + type: string + nodePublishSecretRef: + description: |- + nodePublishSecretRef is a reference to the secret object containing + sensitive information to pass to the CSI driver to complete the CSI + NodePublishVolume and NodeUnpublishVolume calls. + This field is optional, and may be empty if no secret is required. If the + secret object contains more than one secret, all secret references are passed. + properties: + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + TODO: Add other useful fields. apiVersion, kind, uid? + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Drop `kubebuilder:default` when controller-gen doesn't need it https://github.com/kubernetes-sigs/kubebuilder/issues/3896. + type: string + type: object + x-kubernetes-map-type: atomic + readOnly: + description: |- + readOnly specifies a read-only configuration for the volume. + Defaults to false (read/write). + type: boolean + volumeAttributes: + additionalProperties: + type: string + description: |- + volumeAttributes stores driver-specific properties that are passed to the CSI + driver. Consult your driver's documentation for supported values. + type: object + required: + - driver + type: object + downwardAPI: + description: downwardAPI represents downward API about the pod that should populate this volume + properties: + defaultMode: + description: |- + Optional: mode bits to use on created files by default. Must be a + Optional: mode bits used to set permissions on created files by default. + Must be an octal value between 0000 and 0777 or a decimal value between 0 and 511. + YAML accepts both octal and decimal values, JSON requires decimal values for mode bits. + Defaults to 0644. + Directories within the path are not affected by this setting. + This might be in conflict with other options that affect the file + mode, like fsGroup, and the result can be other mode bits set. + format: int32 + type: integer + items: + description: Items is a list of downward API volume file + items: + description: DownwardAPIVolumeFile represents information to create the file containing the pod field + properties: + fieldRef: + description: 'Required: Selects a field of the pod: only annotations, labels, name, namespace and uid are supported.' + properties: + apiVersion: + description: Version of the schema the FieldPath is written in terms of, defaults to "v1". + type: string + fieldPath: + description: Path of the field to select in the specified API version. + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + mode: + description: |- + Optional: mode bits used to set permissions on this file, must be an octal value + between 0000 and 0777 or a decimal value between 0 and 511. + YAML accepts both octal and decimal values, JSON requires decimal values for mode bits. + If not specified, the volume defaultMode will be used. + This might be in conflict with other options that affect the file + mode, like fsGroup, and the result can be other mode bits set. + format: int32 + type: integer + path: + description: 'Required: Path is the relative path name of the file to be created. Must not be absolute or contain the ''..'' path. Must be utf-8 encoded. The first item of the relative path must not start with ''..''' + type: string + resourceFieldRef: + description: |- + Selects a resource of the container: only resources limits and requests + (limits.cpu, limits.memory, requests.cpu and requests.memory) are currently supported. + properties: + containerName: + description: 'Container name: required for volumes, optional for env vars' + type: string + divisor: + anyOf: + - type: integer + - type: string + description: Specifies the output format of the exposed resources, defaults to "1" + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + description: 'Required: resource to select' + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + required: + - path + type: object + type: array + x-kubernetes-list-type: atomic + type: object + emptyDir: + description: |- + emptyDir represents a temporary directory that shares a pod's lifetime. + More info: https://kubernetes.io/docs/concepts/storage/volumes#emptydir + properties: + medium: + description: |- + medium represents what type of storage medium should back this directory. + The default is "" which means to use the node's default medium. + Must be an empty string (default) or Memory. + More info: https://kubernetes.io/docs/concepts/storage/volumes#emptydir + type: string + sizeLimit: + anyOf: + - type: integer + - type: string + description: |- + sizeLimit is the total amount of local storage required for this EmptyDir volume. + The size limit is also applicable for memory medium. + The maximum usage on memory medium EmptyDir would be the minimum value between + the SizeLimit specified here and the sum of memory limits of all containers in a pod. + The default is nil which means that the limit is undefined. + More info: https://kubernetes.io/docs/concepts/storage/volumes#emptydir + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + ephemeral: + description: |- + ephemeral represents a volume that is handled by a cluster storage driver. + The volume's lifecycle is tied to the pod that defines it - it will be created before the pod starts, + and deleted when the pod is removed. + + + Use this if: + a) the volume is only needed while the pod runs, + b) features of normal volumes like restoring from snapshot or capacity + tracking are needed, + c) the storage driver is specified through a storage class, and + d) the storage driver supports dynamic volume provisioning through + a PersistentVolumeClaim (see EphemeralVolumeSource for more + information on the connection between this volume type + and PersistentVolumeClaim). + + + Use PersistentVolumeClaim or one of the vendor-specific + APIs for volumes that persist for longer than the lifecycle + of an individual pod. + + + Use CSI for light-weight local ephemeral volumes if the CSI driver is meant to + be used that way - see the documentation of the driver for + more information. + + + A pod can use both types of ephemeral volumes and + persistent volumes at the same time. + properties: + volumeClaimTemplate: + description: |- + Will be used to create a stand-alone PVC to provision the volume. + The pod in which this EphemeralVolumeSource is embedded will be the + owner of the PVC, i.e. the PVC will be deleted together with the + pod. The name of the PVC will be `-` where + `` is the name from the `PodSpec.Volumes` array + entry. Pod validation will reject the pod if the concatenated name + is not valid for a PVC (for example, too long). + + + An existing PVC with that name that is not owned by the pod + will *not* be used for the pod to avoid using an unrelated + volume by mistake. Starting the pod is then blocked until + the unrelated PVC is removed. If such a pre-created PVC is + meant to be used by the pod, the PVC has to updated with an + owner reference to the pod once the pod exists. Normally + this should not be necessary, but it may be useful when + manually reconstructing a broken cluster. + + + This field is read-only and no changes will be made by Kubernetes + to the PVC after it has been created. + + + Required, must not be nil. + properties: + metadata: + description: |- + May contain labels and annotations that will be copied into the PVC + when creating it. No other fields are allowed and will be rejected during + validation. + type: object + spec: + description: |- + The specification for the PersistentVolumeClaim. The entire content is + copied unchanged into the PVC that gets created from this + template. The same fields as in a PersistentVolumeClaim + are also valid here. + properties: + accessModes: + description: |- + accessModes contains the desired access modes the volume should have. + More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#access-modes-1 + items: + type: string + type: array + x-kubernetes-list-type: atomic + dataSource: + description: |- + dataSource field can be used to specify either: + * An existing VolumeSnapshot object (snapshot.storage.k8s.io/VolumeSnapshot) + * An existing PVC (PersistentVolumeClaim) + If the provisioner or an external controller can support the specified data source, + it will create a new volume based on the contents of the specified data source. + When the AnyVolumeDataSource feature gate is enabled, dataSource contents will be copied to dataSourceRef, + and dataSourceRef contents will be copied to dataSource when dataSourceRef.namespace is not specified. + If the namespace is specified, then dataSourceRef will not be copied to dataSource. + properties: + apiGroup: + description: |- + APIGroup is the group for the resource being referenced. + If APIGroup is not specified, the specified Kind must be in the core API group. + For any other third-party types, APIGroup is required. + type: string + kind: + description: Kind is the type of resource being referenced + type: string + name: + description: Name is the name of resource being referenced + type: string + required: + - kind + - name + type: object + x-kubernetes-map-type: atomic + dataSourceRef: + description: |- + dataSourceRef specifies the object from which to populate the volume with data, if a non-empty + volume is desired. This may be any object from a non-empty API group (non + core object) or a PersistentVolumeClaim object. + When this field is specified, volume binding will only succeed if the type of + the specified object matches some installed volume populator or dynamic + provisioner. + This field will replace the functionality of the dataSource field and as such + if both fields are non-empty, they must have the same value. For backwards + compatibility, when namespace isn't specified in dataSourceRef, + both fields (dataSource and dataSourceRef) will be set to the same + value automatically if one of them is empty and the other is non-empty. + When namespace is specified in dataSourceRef, + dataSource isn't set to the same value and must be empty. + There are three important differences between dataSource and dataSourceRef: + * While dataSource only allows two specific types of objects, dataSourceRef + allows any non-core object, as well as PersistentVolumeClaim objects. + * While dataSource ignores disallowed values (dropping them), dataSourceRef + preserves all values, and generates an error if a disallowed value is + specified. + * While dataSource only allows local objects, dataSourceRef allows objects + in any namespaces. + (Beta) Using this field requires the AnyVolumeDataSource feature gate to be enabled. + (Alpha) Using the namespace field of dataSourceRef requires the CrossNamespaceVolumeDataSource feature gate to be enabled. + properties: + apiGroup: + description: |- + APIGroup is the group for the resource being referenced. + If APIGroup is not specified, the specified Kind must be in the core API group. + For any other third-party types, APIGroup is required. + type: string + kind: + description: Kind is the type of resource being referenced + type: string + name: + description: Name is the name of resource being referenced + type: string + namespace: + description: |- + Namespace is the namespace of resource being referenced + Note that when a namespace is specified, a gateway.networking.k8s.io/ReferenceGrant object is required in the referent namespace to allow that namespace's owner to accept the reference. See the ReferenceGrant documentation for details. + (Alpha) This field requires the CrossNamespaceVolumeDataSource feature gate to be enabled. + type: string + required: + - kind + - name + type: object + resources: + description: |- + resources represents the minimum resources the volume should have. + If RecoverVolumeExpansionFailure feature is enabled users are allowed to specify resource requirements + that are lower than previous value but must still be higher than capacity recorded in the + status field of the claim. + More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#resources + properties: + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: |- + Limits describes the maximum amount of compute resources allowed. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: |- + Requests describes the minimum amount of compute resources required. + If Requests is omitted for a container, it defaults to Limits if that is explicitly specified, + otherwise to an implementation-defined value. Requests cannot exceed Limits. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + type: object + type: object + selector: + description: selector is a label query over volumes to consider for binding. + properties: + matchExpressions: + description: matchExpressions is a list of label selector requirements. The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + storageClassName: + description: |- + storageClassName is the name of the StorageClass required by the claim. + More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#class-1 + type: string + volumeAttributesClassName: + description: |- + volumeAttributesClassName may be used to set the VolumeAttributesClass used by this claim. + If specified, the CSI driver will create or update the volume with the attributes defined + in the corresponding VolumeAttributesClass. This has a different purpose than storageClassName, + it can be changed after the claim is created. An empty string value means that no VolumeAttributesClass + will be applied to the claim but it's not allowed to reset this field to empty string once it is set. + If unspecified and the PersistentVolumeClaim is unbound, the default VolumeAttributesClass + will be set by the persistentvolume controller if it exists. + If the resource referred to by volumeAttributesClass does not exist, this PersistentVolumeClaim will be + set to a Pending state, as reflected by the modifyVolumeStatus field, until such as a resource + exists. + More info: https://kubernetes.io/docs/concepts/storage/volume-attributes-classes/ + (Alpha) Using this field requires the VolumeAttributesClass feature gate to be enabled. + type: string + volumeMode: + description: |- + volumeMode defines what type of volume is required by the claim. + Value of Filesystem is implied when not included in claim spec. + type: string + volumeName: + description: volumeName is the binding reference to the PersistentVolume backing this claim. + type: string + type: object + required: + - spec + type: object + type: object + fc: + description: fc represents a Fibre Channel resource that is attached to a kubelet's host machine and then exposed to the pod. + properties: + fsType: + description: |- + fsType is the filesystem type to mount. + Must be a filesystem type supported by the host operating system. + Ex. "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. + TODO: how do we prevent errors in the filesystem from compromising the machine + type: string + lun: + description: 'lun is Optional: FC target lun number' + format: int32 + type: integer + readOnly: + description: |- + readOnly is Optional: Defaults to false (read/write). ReadOnly here will force + the ReadOnly setting in VolumeMounts. + type: boolean + targetWWNs: + description: 'targetWWNs is Optional: FC target worldwide names (WWNs)' + items: + type: string + type: array + x-kubernetes-list-type: atomic + wwids: + description: |- + wwids Optional: FC volume world wide identifiers (wwids) + Either wwids or combination of targetWWNs and lun must be set, but not both simultaneously. + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + flexVolume: + description: |- + flexVolume represents a generic volume resource that is + provisioned/attached using an exec based plugin. + properties: + driver: + description: driver is the name of the driver to use for this volume. + type: string + fsType: + description: |- + fsType is the filesystem type to mount. + Must be a filesystem type supported by the host operating system. + Ex. "ext4", "xfs", "ntfs". The default filesystem depends on FlexVolume script. + type: string + options: + additionalProperties: + type: string + description: 'options is Optional: this field holds extra command options if any.' + type: object + readOnly: + description: |- + readOnly is Optional: defaults to false (read/write). ReadOnly here will force + the ReadOnly setting in VolumeMounts. + type: boolean + secretRef: + description: |- + secretRef is Optional: secretRef is reference to the secret object containing + sensitive information to pass to the plugin scripts. This may be + empty if no secret object is specified. If the secret object + contains more than one secret, all secrets are passed to the plugin + scripts. + properties: + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + TODO: Add other useful fields. apiVersion, kind, uid? + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Drop `kubebuilder:default` when controller-gen doesn't need it https://github.com/kubernetes-sigs/kubebuilder/issues/3896. + type: string + type: object + x-kubernetes-map-type: atomic + required: + - driver + type: object + flocker: + description: flocker represents a Flocker volume attached to a kubelet's host machine. This depends on the Flocker control service being running + properties: + datasetName: + description: |- + datasetName is Name of the dataset stored as metadata -> name on the dataset for Flocker + should be considered as deprecated + type: string + datasetUUID: + description: datasetUUID is the UUID of the dataset. This is unique identifier of a Flocker dataset + type: string + type: object + gcePersistentDisk: + description: |- + gcePersistentDisk represents a GCE Disk resource that is attached to a + kubelet's host machine and then exposed to the pod. + More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk + properties: + fsType: + description: |- + fsType is filesystem type of the volume that you want to mount. + Tip: Ensure that the filesystem type is supported by the host operating system. + Examples: "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. + More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk + TODO: how do we prevent errors in the filesystem from compromising the machine + type: string + partition: + description: |- + partition is the partition in the volume that you want to mount. + If omitted, the default is to mount by volume name. + Examples: For volume /dev/sda1, you specify the partition as "1". + Similarly, the volume partition for /dev/sda is "0" (or you can leave the property empty). + More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk + format: int32 + type: integer + pdName: + description: |- + pdName is unique name of the PD resource in GCE. Used to identify the disk in GCE. + More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk + type: string + readOnly: + description: |- + readOnly here will force the ReadOnly setting in VolumeMounts. + Defaults to false. + More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk + type: boolean + required: + - pdName + type: object + gitRepo: + description: |- + gitRepo represents a git repository at a particular revision. + DEPRECATED: GitRepo is deprecated. To provision a container with a git repo, mount an + EmptyDir into an InitContainer that clones the repo using git, then mount the EmptyDir + into the Pod's container. + properties: + directory: + description: |- + directory is the target directory name. + Must not contain or start with '..'. If '.' is supplied, the volume directory will be the + git repository. Otherwise, if specified, the volume will contain the git repository in + the subdirectory with the given name. + type: string + repository: + description: repository is the URL + type: string + revision: + description: revision is the commit hash for the specified revision. + type: string + required: + - repository + type: object + glusterfs: + description: |- + glusterfs represents a Glusterfs mount on the host that shares a pod's lifetime. + More info: https://examples.k8s.io/volumes/glusterfs/README.md + properties: + endpoints: + description: |- + endpoints is the endpoint name that details Glusterfs topology. + More info: https://examples.k8s.io/volumes/glusterfs/README.md#create-a-pod + type: string + path: + description: |- + path is the Glusterfs volume path. + More info: https://examples.k8s.io/volumes/glusterfs/README.md#create-a-pod + type: string + readOnly: + description: |- + readOnly here will force the Glusterfs volume to be mounted with read-only permissions. + Defaults to false. + More info: https://examples.k8s.io/volumes/glusterfs/README.md#create-a-pod + type: boolean + required: + - endpoints + - path + type: object + hostPath: + description: |- + hostPath represents a pre-existing file or directory on the host + machine that is directly exposed to the container. This is generally + used for system agents or other privileged things that are allowed + to see the host machine. Most containers will NOT need this. + More info: https://kubernetes.io/docs/concepts/storage/volumes#hostpath + --- + TODO(jonesdl) We need to restrict who can use host directory mounts and who can/can not + mount host directories as read/write. + properties: + path: + description: |- + path of the directory on the host. + If the path is a symlink, it will follow the link to the real path. + More info: https://kubernetes.io/docs/concepts/storage/volumes#hostpath + type: string + type: + description: |- + type for HostPath Volume + Defaults to "" + More info: https://kubernetes.io/docs/concepts/storage/volumes#hostpath + type: string + required: + - path + type: object + iscsi: + description: |- + iscsi represents an ISCSI Disk resource that is attached to a + kubelet's host machine and then exposed to the pod. + More info: https://examples.k8s.io/volumes/iscsi/README.md + properties: + chapAuthDiscovery: + description: chapAuthDiscovery defines whether support iSCSI Discovery CHAP authentication + type: boolean + chapAuthSession: + description: chapAuthSession defines whether support iSCSI Session CHAP authentication + type: boolean + fsType: + description: |- + fsType is the filesystem type of the volume that you want to mount. + Tip: Ensure that the filesystem type is supported by the host operating system. + Examples: "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. + More info: https://kubernetes.io/docs/concepts/storage/volumes#iscsi + TODO: how do we prevent errors in the filesystem from compromising the machine + type: string + initiatorName: + description: |- + initiatorName is the custom iSCSI Initiator Name. + If initiatorName is specified with iscsiInterface simultaneously, new iSCSI interface + : will be created for the connection. + type: string + iqn: + description: iqn is the target iSCSI Qualified Name. + type: string + iscsiInterface: + description: |- + iscsiInterface is the interface Name that uses an iSCSI transport. + Defaults to 'default' (tcp). + type: string + lun: + description: lun represents iSCSI Target Lun number. + format: int32 + type: integer + portals: + description: |- + portals is the iSCSI Target Portal List. The portal is either an IP or ip_addr:port if the port + is other than default (typically TCP ports 860 and 3260). + items: + type: string + type: array + x-kubernetes-list-type: atomic + readOnly: + description: |- + readOnly here will force the ReadOnly setting in VolumeMounts. + Defaults to false. + type: boolean + secretRef: + description: secretRef is the CHAP Secret for iSCSI target and initiator authentication + properties: + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + TODO: Add other useful fields. apiVersion, kind, uid? + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Drop `kubebuilder:default` when controller-gen doesn't need it https://github.com/kubernetes-sigs/kubebuilder/issues/3896. + type: string + type: object + x-kubernetes-map-type: atomic + targetPortal: + description: |- + targetPortal is iSCSI Target Portal. The Portal is either an IP or ip_addr:port if the port + is other than default (typically TCP ports 860 and 3260). + type: string + required: + - iqn + - lun + - targetPortal + type: object + name: + description: |- + name of the volume. + Must be a DNS_LABEL and unique within the pod. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + nfs: + description: |- + nfs represents an NFS mount on the host that shares a pod's lifetime + More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs + properties: + path: + description: |- + path that is exported by the NFS server. + More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs + type: string + readOnly: + description: |- + readOnly here will force the NFS export to be mounted with read-only permissions. + Defaults to false. + More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs + type: boolean + server: + description: |- + server is the hostname or IP address of the NFS server. + More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs + type: string + required: + - path + - server + type: object + persistentVolumeClaim: + description: |- + persistentVolumeClaimVolumeSource represents a reference to a + PersistentVolumeClaim in the same namespace. + More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#persistentvolumeclaims + properties: + claimName: + description: |- + claimName is the name of a PersistentVolumeClaim in the same namespace as the pod using this volume. + More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#persistentvolumeclaims + type: string + readOnly: + description: |- + readOnly Will force the ReadOnly setting in VolumeMounts. + Default false. + type: boolean + required: + - claimName + type: object + photonPersistentDisk: + description: photonPersistentDisk represents a PhotonController persistent disk attached and mounted on kubelets host machine + properties: + fsType: + description: |- + fsType is the filesystem type to mount. + Must be a filesystem type supported by the host operating system. + Ex. "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. + type: string + pdID: + description: pdID is the ID that identifies Photon Controller persistent disk + type: string + required: + - pdID + type: object + portworxVolume: + description: portworxVolume represents a portworx volume attached and mounted on kubelets host machine + properties: + fsType: + description: |- + fSType represents the filesystem type to mount + Must be a filesystem type supported by the host operating system. + Ex. "ext4", "xfs". Implicitly inferred to be "ext4" if unspecified. + type: string + readOnly: + description: |- + readOnly defaults to false (read/write). ReadOnly here will force + the ReadOnly setting in VolumeMounts. + type: boolean + volumeID: + description: volumeID uniquely identifies a Portworx volume + type: string + required: + - volumeID + type: object + projected: + description: projected items for all in one resources secrets, configmaps, and downward API + properties: + defaultMode: + description: |- + defaultMode are the mode bits used to set permissions on created files by default. + Must be an octal value between 0000 and 0777 or a decimal value between 0 and 511. + YAML accepts both octal and decimal values, JSON requires decimal values for mode bits. + Directories within the path are not affected by this setting. + This might be in conflict with other options that affect the file + mode, like fsGroup, and the result can be other mode bits set. + format: int32 + type: integer + sources: + description: sources is the list of volume projections + items: + description: Projection that may be projected along with other supported volume types + properties: + clusterTrustBundle: + description: |- + ClusterTrustBundle allows a pod to access the `.spec.trustBundle` field + of ClusterTrustBundle objects in an auto-updating file. + + + Alpha, gated by the ClusterTrustBundleProjection feature gate. + + + ClusterTrustBundle objects can either be selected by name, or by the + combination of signer name and a label selector. + + + Kubelet performs aggressive normalization of the PEM contents written + into the pod filesystem. Esoteric PEM features such as inter-block + comments and block headers are stripped. Certificates are deduplicated. + The ordering of certificates within the file is arbitrary, and Kubelet + may change the order over time. + properties: + labelSelector: + description: |- + Select all ClusterTrustBundles that match this label selector. Only has + effect if signerName is set. Mutually-exclusive with name. If unset, + interpreted as "match nothing". If set but empty, interpreted as "match + everything". + properties: + matchExpressions: + description: matchExpressions is a list of label selector requirements. The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + name: + description: |- + Select a single ClusterTrustBundle by object name. Mutually-exclusive + with signerName and labelSelector. + type: string + optional: + description: |- + If true, don't block pod startup if the referenced ClusterTrustBundle(s) + aren't available. If using name, then the named ClusterTrustBundle is + allowed not to exist. If using signerName, then the combination of + signerName and labelSelector is allowed to match zero + ClusterTrustBundles. + type: boolean + path: + description: Relative path from the volume root to write the bundle. + type: string + signerName: + description: |- + Select all ClusterTrustBundles that match this signer name. + Mutually-exclusive with name. The contents of all selected + ClusterTrustBundles will be unified and deduplicated. + type: string + required: + - path + type: object + configMap: + description: configMap information about the configMap data to project + properties: + items: + description: |- + items if unspecified, each key-value pair in the Data field of the referenced + ConfigMap will be projected into the volume as a file whose name is the + key and content is the value. If specified, the listed keys will be + projected into the specified paths, and unlisted keys will not be + present. If a key is specified which is not present in the ConfigMap, + the volume setup will error unless it is marked optional. Paths must be + relative and may not contain the '..' path or start with '..'. + items: + description: Maps a string key to a path within a volume. + properties: + key: + description: key is the key to project. + type: string + mode: + description: |- + mode is Optional: mode bits used to set permissions on this file. + Must be an octal value between 0000 and 0777 or a decimal value between 0 and 511. + YAML accepts both octal and decimal values, JSON requires decimal values for mode bits. + If not specified, the volume defaultMode will be used. + This might be in conflict with other options that affect the file + mode, like fsGroup, and the result can be other mode bits set. + format: int32 + type: integer + path: + description: |- + path is the relative path of the file to map the key to. + May not be an absolute path. + May not contain the path element '..'. + May not start with the string '..'. + type: string + required: + - key + - path + type: object + type: array + x-kubernetes-list-type: atomic + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + TODO: Add other useful fields. apiVersion, kind, uid? + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Drop `kubebuilder:default` when controller-gen doesn't need it https://github.com/kubernetes-sigs/kubebuilder/issues/3896. + type: string + optional: + description: optional specify whether the ConfigMap or its keys must be defined + type: boolean + type: object + x-kubernetes-map-type: atomic + downwardAPI: + description: downwardAPI information about the downwardAPI data to project + properties: + items: + description: Items is a list of DownwardAPIVolume file + items: + description: DownwardAPIVolumeFile represents information to create the file containing the pod field + properties: + fieldRef: + description: 'Required: Selects a field of the pod: only annotations, labels, name, namespace and uid are supported.' + properties: + apiVersion: + description: Version of the schema the FieldPath is written in terms of, defaults to "v1". + type: string + fieldPath: + description: Path of the field to select in the specified API version. + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + mode: + description: |- + Optional: mode bits used to set permissions on this file, must be an octal value + between 0000 and 0777 or a decimal value between 0 and 511. + YAML accepts both octal and decimal values, JSON requires decimal values for mode bits. + If not specified, the volume defaultMode will be used. + This might be in conflict with other options that affect the file + mode, like fsGroup, and the result can be other mode bits set. + format: int32 + type: integer + path: + description: 'Required: Path is the relative path name of the file to be created. Must not be absolute or contain the ''..'' path. Must be utf-8 encoded. The first item of the relative path must not start with ''..''' + type: string + resourceFieldRef: + description: |- + Selects a resource of the container: only resources limits and requests + (limits.cpu, limits.memory, requests.cpu and requests.memory) are currently supported. + properties: + containerName: + description: 'Container name: required for volumes, optional for env vars' + type: string + divisor: + anyOf: + - type: integer + - type: string + description: Specifies the output format of the exposed resources, defaults to "1" + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + description: 'Required: resource to select' + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + required: + - path + type: object + type: array + x-kubernetes-list-type: atomic + type: object + secret: + description: secret information about the secret data to project + properties: + items: + description: |- + items if unspecified, each key-value pair in the Data field of the referenced + Secret will be projected into the volume as a file whose name is the + key and content is the value. If specified, the listed keys will be + projected into the specified paths, and unlisted keys will not be + present. If a key is specified which is not present in the Secret, + the volume setup will error unless it is marked optional. Paths must be + relative and may not contain the '..' path or start with '..'. + items: + description: Maps a string key to a path within a volume. + properties: + key: + description: key is the key to project. + type: string + mode: + description: |- + mode is Optional: mode bits used to set permissions on this file. + Must be an octal value between 0000 and 0777 or a decimal value between 0 and 511. + YAML accepts both octal and decimal values, JSON requires decimal values for mode bits. + If not specified, the volume defaultMode will be used. + This might be in conflict with other options that affect the file + mode, like fsGroup, and the result can be other mode bits set. + format: int32 + type: integer + path: + description: |- + path is the relative path of the file to map the key to. + May not be an absolute path. + May not contain the path element '..'. + May not start with the string '..'. + type: string + required: + - key + - path + type: object + type: array + x-kubernetes-list-type: atomic + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + TODO: Add other useful fields. apiVersion, kind, uid? + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Drop `kubebuilder:default` when controller-gen doesn't need it https://github.com/kubernetes-sigs/kubebuilder/issues/3896. + type: string + optional: + description: optional field specify whether the Secret or its key must be defined + type: boolean + type: object + x-kubernetes-map-type: atomic + serviceAccountToken: + description: serviceAccountToken is information about the serviceAccountToken data to project + properties: + audience: + description: |- + audience is the intended audience of the token. A recipient of a token + must identify itself with an identifier specified in the audience of the + token, and otherwise should reject the token. The audience defaults to the + identifier of the apiserver. + type: string + expirationSeconds: + description: |- + expirationSeconds is the requested duration of validity of the service + account token. As the token approaches expiration, the kubelet volume + plugin will proactively rotate the service account token. The kubelet will + start trying to rotate the token if the token is older than 80 percent of + its time to live or if the token is older than 24 hours.Defaults to 1 hour + and must be at least 10 minutes. + format: int64 + type: integer + path: + description: |- + path is the path relative to the mount point of the file to project the + token into. + type: string + required: + - path + type: object + type: object + type: array + x-kubernetes-list-type: atomic + type: object + quobyte: + description: quobyte represents a Quobyte mount on the host that shares a pod's lifetime + properties: + group: + description: |- + group to map volume access to + Default is no group + type: string + readOnly: + description: |- + readOnly here will force the Quobyte volume to be mounted with read-only permissions. + Defaults to false. + type: boolean + registry: + description: |- + registry represents a single or multiple Quobyte Registry services + specified as a string as host:port pair (multiple entries are separated with commas) + which acts as the central registry for volumes + type: string + tenant: + description: |- + tenant owning the given Quobyte volume in the Backend + Used with dynamically provisioned Quobyte volumes, value is set by the plugin + type: string + user: + description: |- + user to map volume access to + Defaults to serivceaccount user + type: string + volume: + description: volume is a string that references an already created Quobyte volume by name. + type: string + required: + - registry + - volume + type: object + rbd: + description: |- + rbd represents a Rados Block Device mount on the host that shares a pod's lifetime. + More info: https://examples.k8s.io/volumes/rbd/README.md + properties: + fsType: + description: |- + fsType is the filesystem type of the volume that you want to mount. + Tip: Ensure that the filesystem type is supported by the host operating system. + Examples: "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. + More info: https://kubernetes.io/docs/concepts/storage/volumes#rbd + TODO: how do we prevent errors in the filesystem from compromising the machine + type: string + image: + description: |- + image is the rados image name. + More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it + type: string + keyring: + description: |- + keyring is the path to key ring for RBDUser. + Default is /etc/ceph/keyring. + More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it + type: string + monitors: + description: |- + monitors is a collection of Ceph monitors. + More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it + items: + type: string + type: array + x-kubernetes-list-type: atomic + pool: + description: |- + pool is the rados pool name. + Default is rbd. + More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it + type: string + readOnly: + description: |- + readOnly here will force the ReadOnly setting in VolumeMounts. + Defaults to false. + More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it + type: boolean + secretRef: + description: |- + secretRef is name of the authentication secret for RBDUser. If provided + overrides keyring. + Default is nil. + More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it + properties: + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + TODO: Add other useful fields. apiVersion, kind, uid? + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Drop `kubebuilder:default` when controller-gen doesn't need it https://github.com/kubernetes-sigs/kubebuilder/issues/3896. + type: string + type: object + x-kubernetes-map-type: atomic + user: + description: |- + user is the rados user name. + Default is admin. + More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it + type: string + required: + - image + - monitors + type: object + scaleIO: + description: scaleIO represents a ScaleIO persistent volume attached and mounted on Kubernetes nodes. + properties: + fsType: + description: |- + fsType is the filesystem type to mount. + Must be a filesystem type supported by the host operating system. + Ex. "ext4", "xfs", "ntfs". + Default is "xfs". + type: string + gateway: + description: gateway is the host address of the ScaleIO API Gateway. + type: string + protectionDomain: + description: protectionDomain is the name of the ScaleIO Protection Domain for the configured storage. + type: string + readOnly: + description: |- + readOnly Defaults to false (read/write). ReadOnly here will force + the ReadOnly setting in VolumeMounts. + type: boolean + secretRef: + description: |- + secretRef references to the secret for ScaleIO user and other + sensitive information. If this is not provided, Login operation will fail. + properties: + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + TODO: Add other useful fields. apiVersion, kind, uid? + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Drop `kubebuilder:default` when controller-gen doesn't need it https://github.com/kubernetes-sigs/kubebuilder/issues/3896. + type: string + type: object + x-kubernetes-map-type: atomic + sslEnabled: + description: sslEnabled Flag enable/disable SSL communication with Gateway, default false + type: boolean + storageMode: + description: |- + storageMode indicates whether the storage for a volume should be ThickProvisioned or ThinProvisioned. + Default is ThinProvisioned. + type: string + storagePool: + description: storagePool is the ScaleIO Storage Pool associated with the protection domain. + type: string + system: + description: system is the name of the storage system as configured in ScaleIO. + type: string + volumeName: + description: |- + volumeName is the name of a volume already created in the ScaleIO system + that is associated with this volume source. + type: string + required: + - gateway + - secretRef + - system + type: object + secret: + description: |- + secret represents a secret that should populate this volume. + More info: https://kubernetes.io/docs/concepts/storage/volumes#secret + properties: + defaultMode: + description: |- + defaultMode is Optional: mode bits used to set permissions on created files by default. + Must be an octal value between 0000 and 0777 or a decimal value between 0 and 511. + YAML accepts both octal and decimal values, JSON requires decimal values + for mode bits. Defaults to 0644. + Directories within the path are not affected by this setting. + This might be in conflict with other options that affect the file + mode, like fsGroup, and the result can be other mode bits set. + format: int32 + type: integer + items: + description: |- + items If unspecified, each key-value pair in the Data field of the referenced + Secret will be projected into the volume as a file whose name is the + key and content is the value. If specified, the listed keys will be + projected into the specified paths, and unlisted keys will not be + present. If a key is specified which is not present in the Secret, + the volume setup will error unless it is marked optional. Paths must be + relative and may not contain the '..' path or start with '..'. + items: + description: Maps a string key to a path within a volume. + properties: + key: + description: key is the key to project. + type: string + mode: + description: |- + mode is Optional: mode bits used to set permissions on this file. + Must be an octal value between 0000 and 0777 or a decimal value between 0 and 511. + YAML accepts both octal and decimal values, JSON requires decimal values for mode bits. + If not specified, the volume defaultMode will be used. + This might be in conflict with other options that affect the file + mode, like fsGroup, and the result can be other mode bits set. + format: int32 + type: integer + path: + description: |- + path is the relative path of the file to map the key to. + May not be an absolute path. + May not contain the path element '..'. + May not start with the string '..'. + type: string + required: + - key + - path + type: object + type: array + x-kubernetes-list-type: atomic + optional: + description: optional field specify whether the Secret or its keys must be defined + type: boolean + secretName: + description: |- + secretName is the name of the secret in the pod's namespace to use. + More info: https://kubernetes.io/docs/concepts/storage/volumes#secret + type: string + type: object + storageos: + description: storageOS represents a StorageOS volume attached and mounted on Kubernetes nodes. + properties: + fsType: + description: |- + fsType is the filesystem type to mount. + Must be a filesystem type supported by the host operating system. + Ex. "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. + type: string + readOnly: + description: |- + readOnly defaults to false (read/write). ReadOnly here will force + the ReadOnly setting in VolumeMounts. + type: boolean + secretRef: + description: |- + secretRef specifies the secret to use for obtaining the StorageOS API + credentials. If not specified, default values will be attempted. + properties: + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + TODO: Add other useful fields. apiVersion, kind, uid? + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Drop `kubebuilder:default` when controller-gen doesn't need it https://github.com/kubernetes-sigs/kubebuilder/issues/3896. + type: string + type: object + x-kubernetes-map-type: atomic + volumeName: + description: |- + volumeName is the human-readable name of the StorageOS volume. Volume + names are only unique within a namespace. + type: string + volumeNamespace: + description: |- + volumeNamespace specifies the scope of the volume within StorageOS. If no + namespace is specified then the Pod's namespace will be used. This allows the + Kubernetes name scoping to be mirrored within StorageOS for tighter integration. + Set VolumeName to any name to override the default behaviour. + Set to "default" if you are not using namespaces within StorageOS. + Namespaces that do not pre-exist within StorageOS will be created. + type: string + type: object + vsphereVolume: + description: vsphereVolume represents a vSphere volume attached and mounted on kubelets host machine + properties: + fsType: + description: |- + fsType is filesystem type to mount. + Must be a filesystem type supported by the host operating system. + Ex. "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. + type: string + storagePolicyID: + description: storagePolicyID is the storage Policy Based Management (SPBM) profile ID associated with the StoragePolicyName. + type: string + storagePolicyName: + description: storagePolicyName is the storage Policy Based Management (SPBM) profile name. + type: string + volumePath: + description: volumePath is the path that identifies vSphere volume vmdk + type: string + required: + - volumePath + type: object + required: + - name + type: object + type: array + type: object + proxyHost: + description: proxyHost sets the INSTANA_AGENT_PROXY_HOST environment variable. + type: string + proxyPassword: + description: proxyPassword sets the INSTANA_AGENT_PROXY_PASSWORD environment variable. + type: string + proxyPort: + description: proxyPort sets the INSTANA_AGENT_PROXY_PORT environment variable. + type: string + proxyProtocol: + description: proxyProtocol sets the INSTANA_AGENT_PROXY_PROTOCOL environment variable. + type: string + proxyUseDNS: + description: proxyUseDNS sets the INSTANA_AGENT_PROXY_USE_DNS environment variable. + type: boolean + proxyUser: + description: proxyUser sets the INSTANA_AGENT_PROXY_USER environment variable. + type: string + redactKubernetesSecrets: + description: RedactKubernetesSecrets sets the INSTANA_KUBERNETES_REDACT_SECRETS environment variable. + type: string + serviceMesh: + description: ServiceMesh sets the ENABLE_AGENT_SOCKET environment variable. + properties: + enabled: + type: boolean + type: object + tls: + description: |- + TLS for end-to-end encryption between the Instana Agent and clients accessing the Agent. + The Instana Agent does not yet allow enforcing TLS encryption, enabling makes it possible for clients to 'opt-in'. + So TLS is only enabled on a connection when requested by the client. + properties: + certificate: + description: certificate (together with key) is the alternative to an existing Secret. Must be base64 encoded. + format: byte + type: string + key: + description: key (together with certificate) is the alternative to an existing Secret. Must be base64 encoded. + format: byte + type: string + secretName: + description: secretName is the name of the secret that has the relevant files. + type: string + type: object + updateStrategy: + description: Control how to update the Agent DaemonSet + properties: + rollingUpdate: + description: |- + Rolling update config params. Present only if type = "RollingUpdate". + --- + TODO: Update this to follow our convention for oneOf, whatever we decide it + to be. Same as Deployment `strategy.rollingUpdate`. + See https://github.com/kubernetes/kubernetes/issues/35345 + properties: + maxSurge: + anyOf: + - type: integer + - type: string + description: |- + The maximum number of nodes with an existing available DaemonSet pod that + can have an updated DaemonSet pod during during an update. + Value can be an absolute number (ex: 5) or a percentage of desired pods (ex: 10%). + This can not be 0 if MaxUnavailable is 0. + Absolute number is calculated from percentage by rounding up to a minimum of 1. + Default value is 0. + Example: when this is set to 30%, at most 30% of the total number of nodes + that should be running the daemon pod (i.e. status.desiredNumberScheduled) + can have their a new pod created before the old pod is marked as deleted. + The update starts by launching new pods on 30% of nodes. Once an updated + pod is available (Ready for at least minReadySeconds) the old DaemonSet pod + on that node is marked deleted. If the old pod becomes unavailable for any + reason (Ready transitions to false, is evicted, or is drained) an updated + pod is immediatedly created on that node without considering surge limits. + Allowing surge implies the possibility that the resources consumed by the + daemonset on any given node can double if the readiness check fails, and + so resource intensive daemonsets should take into account that they may + cause evictions during disruption. + x-kubernetes-int-or-string: true + maxUnavailable: + anyOf: + - type: integer + - type: string + description: |- + The maximum number of DaemonSet pods that can be unavailable during the + update. Value can be an absolute number (ex: 5) or a percentage of total + number of DaemonSet pods at the start of the update (ex: 10%). Absolute + number is calculated from percentage by rounding up. + This cannot be 0 if MaxSurge is 0 + Default value is 1. + Example: when this is set to 30%, at most 30% of the total number of nodes + that should be running the daemon pod (i.e. status.desiredNumberScheduled) + can have their pods stopped for an update at any given time. The update + starts by stopping at most 30% of those DaemonSet pods and then brings + up new DaemonSet pods in their place. Once the new pods are available, + it then proceeds onto other DaemonSet pods, thus ensuring that at least + 70% of original number of DaemonSet pods are available at all times during + the update. + x-kubernetes-int-or-string: true + type: object + type: + description: Type of daemon set update. Can be "RollingUpdate" or "OnDelete". Default is RollingUpdate. + type: string + type: object + required: + - endpointHost + - endpointPort + type: object + cluster: + description: |- + Name of the cluster, that will be assigned to this cluster in Instana. Either specifying the 'cluster.name' or 'zone.name' + is mandatory. + properties: + name: + type: string + type: object + k8s_sensor: + properties: + deployment: + properties: + enabled: + type: boolean + minReadySeconds: + description: The minimum number of seconds for which a newly created Pod should be ready without any of its containers crashing, for it to be considered available + type: integer + pod: + description: Override pod resource requirements for the Kubernetes Sensor pods. + properties: + affinity: + description: |- + agent.pod.affinity are affinities to influence agent pod assignment. + https://kubernetes.io/docs/concepts/configuration/taint-and-toleration/ + properties: + nodeAffinity: + description: Describes node affinity scheduling rules for the pod. + properties: + preferredDuringSchedulingIgnoredDuringExecution: + description: |- + The scheduler will prefer to schedule pods to nodes that satisfy + the affinity expressions specified by this field, but it may choose + a node that violates one or more of the expressions. The node that is + most preferred is the one with the greatest sum of weights, i.e. + for each node that meets all of the scheduling requirements (resource + request, requiredDuringScheduling affinity expressions, etc.), + compute a sum by iterating through the elements of this field and adding + "weight" to the sum if the node matches the corresponding matchExpressions; the + node(s) with the highest sum are the most preferred. + items: + description: |- + An empty preferred scheduling term matches all objects with implicit weight 0 + (i.e. it's a no-op). A null preferred scheduling term matches no objects (i.e. is also a no-op). + properties: + preference: + description: A node selector term, associated with the corresponding weight. + properties: + matchExpressions: + description: A list of node selector requirements by node's labels. + items: + description: |- + A node selector requirement is a selector that contains values, a key, and an operator + that relates the key and values. + properties: + key: + description: The label key that the selector applies to. + type: string + operator: + description: |- + Represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt. + type: string + values: + description: |- + An array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. If the operator is Gt or Lt, the values + array must have a single element, which will be interpreted as an integer. + This array is replaced during a strategic merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchFields: + description: A list of node selector requirements by node's fields. + items: + description: |- + A node selector requirement is a selector that contains values, a key, and an operator + that relates the key and values. + properties: + key: + description: The label key that the selector applies to. + type: string + operator: + description: |- + Represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt. + type: string + values: + description: |- + An array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. If the operator is Gt or Lt, the values + array must have a single element, which will be interpreted as an integer. + This array is replaced during a strategic merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + type: object + x-kubernetes-map-type: atomic + weight: + description: Weight associated with matching the corresponding nodeSelectorTerm, in the range 1-100. + format: int32 + type: integer + required: + - preference + - weight + type: object + type: array + x-kubernetes-list-type: atomic + requiredDuringSchedulingIgnoredDuringExecution: + description: |- + If the affinity requirements specified by this field are not met at + scheduling time, the pod will not be scheduled onto the node. + If the affinity requirements specified by this field cease to be met + at some point during pod execution (e.g. due to an update), the system + may or may not try to eventually evict the pod from its node. + properties: + nodeSelectorTerms: + description: Required. A list of node selector terms. The terms are ORed. + items: + description: |- + A null or empty node selector term matches no objects. The requirements of + them are ANDed. + The TopologySelectorTerm type implements a subset of the NodeSelectorTerm. + properties: + matchExpressions: + description: A list of node selector requirements by node's labels. + items: + description: |- + A node selector requirement is a selector that contains values, a key, and an operator + that relates the key and values. + properties: + key: + description: The label key that the selector applies to. + type: string + operator: + description: |- + Represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt. + type: string + values: + description: |- + An array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. If the operator is Gt or Lt, the values + array must have a single element, which will be interpreted as an integer. + This array is replaced during a strategic merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchFields: + description: A list of node selector requirements by node's fields. + items: + description: |- + A node selector requirement is a selector that contains values, a key, and an operator + that relates the key and values. + properties: + key: + description: The label key that the selector applies to. + type: string + operator: + description: |- + Represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt. + type: string + values: + description: |- + An array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. If the operator is Gt or Lt, the values + array must have a single element, which will be interpreted as an integer. + This array is replaced during a strategic merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + type: object + x-kubernetes-map-type: atomic + type: array + x-kubernetes-list-type: atomic + required: + - nodeSelectorTerms + type: object + x-kubernetes-map-type: atomic + type: object + podAffinity: + description: Describes pod affinity scheduling rules (e.g. co-locate this pod in the same node, zone, etc. as some other pod(s)). + properties: + preferredDuringSchedulingIgnoredDuringExecution: + description: |- + The scheduler will prefer to schedule pods to nodes that satisfy + the affinity expressions specified by this field, but it may choose + a node that violates one or more of the expressions. The node that is + most preferred is the one with the greatest sum of weights, i.e. + for each node that meets all of the scheduling requirements (resource + request, requiredDuringScheduling affinity expressions, etc.), + compute a sum by iterating through the elements of this field and adding + "weight" to the sum if the node has pods which matches the corresponding podAffinityTerm; the + node(s) with the highest sum are the most preferred. + items: + description: The weights of all of the matched WeightedPodAffinityTerm fields are added per-node to find the most preferred node(s) + properties: + podAffinityTerm: + description: Required. A pod affinity term, associated with the corresponding weight. + properties: + labelSelector: + description: |- + A label query over a set of resources, in this case pods. + If it's null, this PodAffinityTerm matches with no Pods. + properties: + matchExpressions: + description: matchExpressions is a list of label selector requirements. The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + matchLabelKeys: + description: |- + MatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `labelSelector` as `key in (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both matchLabelKeys and labelSelector. + Also, matchLabelKeys cannot be set when labelSelector isn't set. + This is an alpha field and requires enabling MatchLabelKeysInPodAffinity feature gate. + items: + type: string + type: array + x-kubernetes-list-type: atomic + mismatchLabelKeys: + description: |- + MismatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `labelSelector` as `key notin (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both mismatchLabelKeys and labelSelector. + Also, mismatchLabelKeys cannot be set when labelSelector isn't set. + This is an alpha field and requires enabling MatchLabelKeysInPodAffinity feature gate. + items: + type: string + type: array + x-kubernetes-list-type: atomic + namespaceSelector: + description: |- + A label query over the set of namespaces that the term applies to. + The term is applied to the union of the namespaces selected by this field + and the ones listed in the namespaces field. + null selector and null or empty namespaces list means "this pod's namespace". + An empty selector ({}) matches all namespaces. + properties: + matchExpressions: + description: matchExpressions is a list of label selector requirements. The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + namespaces: + description: |- + namespaces specifies a static list of namespace names that the term applies to. + The term is applied to the union of the namespaces listed in this field + and the ones selected by namespaceSelector. + null or empty namespaces list and null namespaceSelector means "this pod's namespace". + items: + type: string + type: array + x-kubernetes-list-type: atomic + topologyKey: + description: |- + This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching + the labelSelector in the specified namespaces, where co-located is defined as running on a node + whose value of the label with key topologyKey matches that of any node on which any of the + selected pods is running. + Empty topologyKey is not allowed. + type: string + required: + - topologyKey + type: object + weight: + description: |- + weight associated with matching the corresponding podAffinityTerm, + in the range 1-100. + format: int32 + type: integer + required: + - podAffinityTerm + - weight + type: object + type: array + x-kubernetes-list-type: atomic + requiredDuringSchedulingIgnoredDuringExecution: + description: |- + If the affinity requirements specified by this field are not met at + scheduling time, the pod will not be scheduled onto the node. + If the affinity requirements specified by this field cease to be met + at some point during pod execution (e.g. due to a pod label update), the + system may or may not try to eventually evict the pod from its node. + When there are multiple elements, the lists of nodes corresponding to each + podAffinityTerm are intersected, i.e. all terms must be satisfied. + items: + description: |- + Defines a set of pods (namely those matching the labelSelector + relative to the given namespace(s)) that this pod should be + co-located (affinity) or not co-located (anti-affinity) with, + where co-located is defined as running on a node whose value of + the label with key matches that of any node on which + a pod of the set of pods is running + properties: + labelSelector: + description: |- + A label query over a set of resources, in this case pods. + If it's null, this PodAffinityTerm matches with no Pods. + properties: + matchExpressions: + description: matchExpressions is a list of label selector requirements. The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + matchLabelKeys: + description: |- + MatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `labelSelector` as `key in (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both matchLabelKeys and labelSelector. + Also, matchLabelKeys cannot be set when labelSelector isn't set. + This is an alpha field and requires enabling MatchLabelKeysInPodAffinity feature gate. + items: + type: string + type: array + x-kubernetes-list-type: atomic + mismatchLabelKeys: + description: |- + MismatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `labelSelector` as `key notin (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both mismatchLabelKeys and labelSelector. + Also, mismatchLabelKeys cannot be set when labelSelector isn't set. + This is an alpha field and requires enabling MatchLabelKeysInPodAffinity feature gate. + items: + type: string + type: array + x-kubernetes-list-type: atomic + namespaceSelector: + description: |- + A label query over the set of namespaces that the term applies to. + The term is applied to the union of the namespaces selected by this field + and the ones listed in the namespaces field. + null selector and null or empty namespaces list means "this pod's namespace". + An empty selector ({}) matches all namespaces. + properties: + matchExpressions: + description: matchExpressions is a list of label selector requirements. The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + namespaces: + description: |- + namespaces specifies a static list of namespace names that the term applies to. + The term is applied to the union of the namespaces listed in this field + and the ones selected by namespaceSelector. + null or empty namespaces list and null namespaceSelector means "this pod's namespace". + items: + type: string + type: array + x-kubernetes-list-type: atomic + topologyKey: + description: |- + This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching + the labelSelector in the specified namespaces, where co-located is defined as running on a node + whose value of the label with key topologyKey matches that of any node on which any of the + selected pods is running. + Empty topologyKey is not allowed. + type: string + required: + - topologyKey + type: object + type: array + x-kubernetes-list-type: atomic + type: object + podAntiAffinity: + description: Describes pod anti-affinity scheduling rules (e.g. avoid putting this pod in the same node, zone, etc. as some other pod(s)). + properties: + preferredDuringSchedulingIgnoredDuringExecution: + description: |- + The scheduler will prefer to schedule pods to nodes that satisfy + the anti-affinity expressions specified by this field, but it may choose + a node that violates one or more of the expressions. The node that is + most preferred is the one with the greatest sum of weights, i.e. + for each node that meets all of the scheduling requirements (resource + request, requiredDuringScheduling anti-affinity expressions, etc.), + compute a sum by iterating through the elements of this field and adding + "weight" to the sum if the node has pods which matches the corresponding podAffinityTerm; the + node(s) with the highest sum are the most preferred. + items: + description: The weights of all of the matched WeightedPodAffinityTerm fields are added per-node to find the most preferred node(s) + properties: + podAffinityTerm: + description: Required. A pod affinity term, associated with the corresponding weight. + properties: + labelSelector: + description: |- + A label query over a set of resources, in this case pods. + If it's null, this PodAffinityTerm matches with no Pods. + properties: + matchExpressions: + description: matchExpressions is a list of label selector requirements. The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + matchLabelKeys: + description: |- + MatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `labelSelector` as `key in (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both matchLabelKeys and labelSelector. + Also, matchLabelKeys cannot be set when labelSelector isn't set. + This is an alpha field and requires enabling MatchLabelKeysInPodAffinity feature gate. + items: + type: string + type: array + x-kubernetes-list-type: atomic + mismatchLabelKeys: + description: |- + MismatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `labelSelector` as `key notin (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both mismatchLabelKeys and labelSelector. + Also, mismatchLabelKeys cannot be set when labelSelector isn't set. + This is an alpha field and requires enabling MatchLabelKeysInPodAffinity feature gate. + items: + type: string + type: array + x-kubernetes-list-type: atomic + namespaceSelector: + description: |- + A label query over the set of namespaces that the term applies to. + The term is applied to the union of the namespaces selected by this field + and the ones listed in the namespaces field. + null selector and null or empty namespaces list means "this pod's namespace". + An empty selector ({}) matches all namespaces. + properties: + matchExpressions: + description: matchExpressions is a list of label selector requirements. The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + namespaces: + description: |- + namespaces specifies a static list of namespace names that the term applies to. + The term is applied to the union of the namespaces listed in this field + and the ones selected by namespaceSelector. + null or empty namespaces list and null namespaceSelector means "this pod's namespace". + items: + type: string + type: array + x-kubernetes-list-type: atomic + topologyKey: + description: |- + This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching + the labelSelector in the specified namespaces, where co-located is defined as running on a node + whose value of the label with key topologyKey matches that of any node on which any of the + selected pods is running. + Empty topologyKey is not allowed. + type: string + required: + - topologyKey + type: object + weight: + description: |- + weight associated with matching the corresponding podAffinityTerm, + in the range 1-100. + format: int32 + type: integer + required: + - podAffinityTerm + - weight + type: object + type: array + x-kubernetes-list-type: atomic + requiredDuringSchedulingIgnoredDuringExecution: + description: |- + If the anti-affinity requirements specified by this field are not met at + scheduling time, the pod will not be scheduled onto the node. + If the anti-affinity requirements specified by this field cease to be met + at some point during pod execution (e.g. due to a pod label update), the + system may or may not try to eventually evict the pod from its node. + When there are multiple elements, the lists of nodes corresponding to each + podAffinityTerm are intersected, i.e. all terms must be satisfied. + items: + description: |- + Defines a set of pods (namely those matching the labelSelector + relative to the given namespace(s)) that this pod should be + co-located (affinity) or not co-located (anti-affinity) with, + where co-located is defined as running on a node whose value of + the label with key matches that of any node on which + a pod of the set of pods is running + properties: + labelSelector: + description: |- + A label query over a set of resources, in this case pods. + If it's null, this PodAffinityTerm matches with no Pods. + properties: + matchExpressions: + description: matchExpressions is a list of label selector requirements. The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + matchLabelKeys: + description: |- + MatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `labelSelector` as `key in (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both matchLabelKeys and labelSelector. + Also, matchLabelKeys cannot be set when labelSelector isn't set. + This is an alpha field and requires enabling MatchLabelKeysInPodAffinity feature gate. + items: + type: string + type: array + x-kubernetes-list-type: atomic + mismatchLabelKeys: + description: |- + MismatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `labelSelector` as `key notin (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both mismatchLabelKeys and labelSelector. + Also, mismatchLabelKeys cannot be set when labelSelector isn't set. + This is an alpha field and requires enabling MatchLabelKeysInPodAffinity feature gate. + items: + type: string + type: array + x-kubernetes-list-type: atomic + namespaceSelector: + description: |- + A label query over the set of namespaces that the term applies to. + The term is applied to the union of the namespaces selected by this field + and the ones listed in the namespaces field. + null selector and null or empty namespaces list means "this pod's namespace". + An empty selector ({}) matches all namespaces. + properties: + matchExpressions: + description: matchExpressions is a list of label selector requirements. The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + namespaces: + description: |- + namespaces specifies a static list of namespace names that the term applies to. + The term is applied to the union of the namespaces listed in this field + and the ones selected by namespaceSelector. + null or empty namespaces list and null namespaceSelector means "this pod's namespace". + items: + type: string + type: array + x-kubernetes-list-type: atomic + topologyKey: + description: |- + This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching + the labelSelector in the specified namespaces, where co-located is defined as running on a node + whose value of the label with key topologyKey matches that of any node on which any of the + selected pods is running. + Empty topologyKey is not allowed. + type: string + required: + - topologyKey + type: object + type: array + x-kubernetes-list-type: atomic + type: object + type: object + claims: + description: |- + Claims lists the names of resources, defined in spec.resourceClaims, + that are used by this container. + + + This is an alpha field and requires enabling the + DynamicResourceAllocation feature gate. + + + This field is immutable. It can only be set for containers. + items: + description: ResourceClaim references one entry in PodSpec.ResourceClaims. + properties: + name: + description: |- + Name must match the name of one entry in pod.spec.resourceClaims of + the Pod where this field is used. It makes that resource available + inside a container. + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: |- + Limits describes the maximum amount of compute resources allowed. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + type: object + nodeSelector: + additionalProperties: + type: string + type: object + priorityClassName: + type: string + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: |- + Requests describes the minimum amount of compute resources required. + If Requests is omitted for a container, it defaults to Limits if that is explicitly specified, + otherwise to an implementation-defined value. Requests cannot exceed Limits. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + type: object + tolerations: + description: agent.pod.tolerations are tolerations to influence agent pod assignment. + items: + description: |- + The pod this Toleration is attached to tolerates any taint that matches + the triple using the matching operator . + properties: + effect: + description: |- + Effect indicates the taint effect to match. Empty means match all taint effects. + When specified, allowed values are NoSchedule, PreferNoSchedule and NoExecute. + type: string + key: + description: |- + Key is the taint key that the toleration applies to. Empty means match all taint keys. + If the key is empty, operator must be Exists; this combination means to match all values and all keys. + type: string + operator: + description: |- + Operator represents a key's relationship to the value. + Valid operators are Exists and Equal. Defaults to Equal. + Exists is equivalent to wildcard for value, so that a pod can + tolerate all taints of a particular category. + type: string + tolerationSeconds: + description: |- + TolerationSeconds represents the period of time the toleration (which must be + of effect NoExecute, otherwise this field is ignored) tolerates the taint. By default, + it is not set, which means tolerate the taint forever (do not evict). Zero and + negative values will be treated as 0 (evict immediately) by the system. + format: int64 + type: integer + value: + description: |- + Value is the taint value the toleration matches to. + If the operator is Exists, the value should be empty, otherwise just a regular string. + type: string + type: object + type: array + type: object + replicas: + description: Specify the number of replicas for the Kubernetes Sensor. + type: integer + type: object + image: + properties: + digest: + description: |- + Digest (a.k.a. Image ID) of the agent container image. If specified, it has priority over `agent.image.tag`, + which will then be ignored. + type: string + name: + description: Name is the name of the container image of the Instana agent. + type: string + pullPolicy: + description: PullPolicy specifies when to pull the image container. + type: string + tag: + description: Tag is the name of the agent container image; if `agent.image.digest` is specified, this property is ignored. + type: string + type: object + podDisruptionBudget: + description: Toggles the PDB for the K8s Sensor + properties: + enabled: + type: boolean + type: object + type: object + kubernetes: + description: |- + Allows for installment of the Kubernetes Sensor as separate pod. Which allows for better tailored resource settings + (mainly memory) both for the Agent pods and the Kubernetes Sensor pod. + properties: + deployment: + properties: + enabled: + type: boolean + minReadySeconds: + description: The minimum number of seconds for which a newly created Pod should be ready without any of its containers crashing, for it to be considered available + type: integer + pod: + description: Override pod resource requirements for the Kubernetes Sensor pods. + properties: + affinity: + description: |- + agent.pod.affinity are affinities to influence agent pod assignment. + https://kubernetes.io/docs/concepts/configuration/taint-and-toleration/ + properties: + nodeAffinity: + description: Describes node affinity scheduling rules for the pod. + properties: + preferredDuringSchedulingIgnoredDuringExecution: + description: |- + The scheduler will prefer to schedule pods to nodes that satisfy + the affinity expressions specified by this field, but it may choose + a node that violates one or more of the expressions. The node that is + most preferred is the one with the greatest sum of weights, i.e. + for each node that meets all of the scheduling requirements (resource + request, requiredDuringScheduling affinity expressions, etc.), + compute a sum by iterating through the elements of this field and adding + "weight" to the sum if the node matches the corresponding matchExpressions; the + node(s) with the highest sum are the most preferred. + items: + description: |- + An empty preferred scheduling term matches all objects with implicit weight 0 + (i.e. it's a no-op). A null preferred scheduling term matches no objects (i.e. is also a no-op). + properties: + preference: + description: A node selector term, associated with the corresponding weight. + properties: + matchExpressions: + description: A list of node selector requirements by node's labels. + items: + description: |- + A node selector requirement is a selector that contains values, a key, and an operator + that relates the key and values. + properties: + key: + description: The label key that the selector applies to. + type: string + operator: + description: |- + Represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt. + type: string + values: + description: |- + An array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. If the operator is Gt or Lt, the values + array must have a single element, which will be interpreted as an integer. + This array is replaced during a strategic merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchFields: + description: A list of node selector requirements by node's fields. + items: + description: |- + A node selector requirement is a selector that contains values, a key, and an operator + that relates the key and values. + properties: + key: + description: The label key that the selector applies to. + type: string + operator: + description: |- + Represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt. + type: string + values: + description: |- + An array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. If the operator is Gt or Lt, the values + array must have a single element, which will be interpreted as an integer. + This array is replaced during a strategic merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + type: object + x-kubernetes-map-type: atomic + weight: + description: Weight associated with matching the corresponding nodeSelectorTerm, in the range 1-100. + format: int32 + type: integer + required: + - preference + - weight + type: object + type: array + x-kubernetes-list-type: atomic + requiredDuringSchedulingIgnoredDuringExecution: + description: |- + If the affinity requirements specified by this field are not met at + scheduling time, the pod will not be scheduled onto the node. + If the affinity requirements specified by this field cease to be met + at some point during pod execution (e.g. due to an update), the system + may or may not try to eventually evict the pod from its node. + properties: + nodeSelectorTerms: + description: Required. A list of node selector terms. The terms are ORed. + items: + description: |- + A null or empty node selector term matches no objects. The requirements of + them are ANDed. + The TopologySelectorTerm type implements a subset of the NodeSelectorTerm. + properties: + matchExpressions: + description: A list of node selector requirements by node's labels. + items: + description: |- + A node selector requirement is a selector that contains values, a key, and an operator + that relates the key and values. + properties: + key: + description: The label key that the selector applies to. + type: string + operator: + description: |- + Represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt. + type: string + values: + description: |- + An array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. If the operator is Gt or Lt, the values + array must have a single element, which will be interpreted as an integer. + This array is replaced during a strategic merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchFields: + description: A list of node selector requirements by node's fields. + items: + description: |- + A node selector requirement is a selector that contains values, a key, and an operator + that relates the key and values. + properties: + key: + description: The label key that the selector applies to. + type: string + operator: + description: |- + Represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt. + type: string + values: + description: |- + An array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. If the operator is Gt or Lt, the values + array must have a single element, which will be interpreted as an integer. + This array is replaced during a strategic merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + type: object + x-kubernetes-map-type: atomic + type: array + x-kubernetes-list-type: atomic + required: + - nodeSelectorTerms + type: object + x-kubernetes-map-type: atomic + type: object + podAffinity: + description: Describes pod affinity scheduling rules (e.g. co-locate this pod in the same node, zone, etc. as some other pod(s)). + properties: + preferredDuringSchedulingIgnoredDuringExecution: + description: |- + The scheduler will prefer to schedule pods to nodes that satisfy + the affinity expressions specified by this field, but it may choose + a node that violates one or more of the expressions. The node that is + most preferred is the one with the greatest sum of weights, i.e. + for each node that meets all of the scheduling requirements (resource + request, requiredDuringScheduling affinity expressions, etc.), + compute a sum by iterating through the elements of this field and adding + "weight" to the sum if the node has pods which matches the corresponding podAffinityTerm; the + node(s) with the highest sum are the most preferred. + items: + description: The weights of all of the matched WeightedPodAffinityTerm fields are added per-node to find the most preferred node(s) + properties: + podAffinityTerm: + description: Required. A pod affinity term, associated with the corresponding weight. + properties: + labelSelector: + description: |- + A label query over a set of resources, in this case pods. + If it's null, this PodAffinityTerm matches with no Pods. + properties: + matchExpressions: + description: matchExpressions is a list of label selector requirements. The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + matchLabelKeys: + description: |- + MatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `labelSelector` as `key in (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both matchLabelKeys and labelSelector. + Also, matchLabelKeys cannot be set when labelSelector isn't set. + This is an alpha field and requires enabling MatchLabelKeysInPodAffinity feature gate. + items: + type: string + type: array + x-kubernetes-list-type: atomic + mismatchLabelKeys: + description: |- + MismatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `labelSelector` as `key notin (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both mismatchLabelKeys and labelSelector. + Also, mismatchLabelKeys cannot be set when labelSelector isn't set. + This is an alpha field and requires enabling MatchLabelKeysInPodAffinity feature gate. + items: + type: string + type: array + x-kubernetes-list-type: atomic + namespaceSelector: + description: |- + A label query over the set of namespaces that the term applies to. + The term is applied to the union of the namespaces selected by this field + and the ones listed in the namespaces field. + null selector and null or empty namespaces list means "this pod's namespace". + An empty selector ({}) matches all namespaces. + properties: + matchExpressions: + description: matchExpressions is a list of label selector requirements. The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + namespaces: + description: |- + namespaces specifies a static list of namespace names that the term applies to. + The term is applied to the union of the namespaces listed in this field + and the ones selected by namespaceSelector. + null or empty namespaces list and null namespaceSelector means "this pod's namespace". + items: + type: string + type: array + x-kubernetes-list-type: atomic + topologyKey: + description: |- + This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching + the labelSelector in the specified namespaces, where co-located is defined as running on a node + whose value of the label with key topologyKey matches that of any node on which any of the + selected pods is running. + Empty topologyKey is not allowed. + type: string + required: + - topologyKey + type: object + weight: + description: |- + weight associated with matching the corresponding podAffinityTerm, + in the range 1-100. + format: int32 + type: integer + required: + - podAffinityTerm + - weight + type: object + type: array + x-kubernetes-list-type: atomic + requiredDuringSchedulingIgnoredDuringExecution: + description: |- + If the affinity requirements specified by this field are not met at + scheduling time, the pod will not be scheduled onto the node. + If the affinity requirements specified by this field cease to be met + at some point during pod execution (e.g. due to a pod label update), the + system may or may not try to eventually evict the pod from its node. + When there are multiple elements, the lists of nodes corresponding to each + podAffinityTerm are intersected, i.e. all terms must be satisfied. + items: + description: |- + Defines a set of pods (namely those matching the labelSelector + relative to the given namespace(s)) that this pod should be + co-located (affinity) or not co-located (anti-affinity) with, + where co-located is defined as running on a node whose value of + the label with key matches that of any node on which + a pod of the set of pods is running + properties: + labelSelector: + description: |- + A label query over a set of resources, in this case pods. + If it's null, this PodAffinityTerm matches with no Pods. + properties: + matchExpressions: + description: matchExpressions is a list of label selector requirements. The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + matchLabelKeys: + description: |- + MatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `labelSelector` as `key in (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both matchLabelKeys and labelSelector. + Also, matchLabelKeys cannot be set when labelSelector isn't set. + This is an alpha field and requires enabling MatchLabelKeysInPodAffinity feature gate. + items: + type: string + type: array + x-kubernetes-list-type: atomic + mismatchLabelKeys: + description: |- + MismatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `labelSelector` as `key notin (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both mismatchLabelKeys and labelSelector. + Also, mismatchLabelKeys cannot be set when labelSelector isn't set. + This is an alpha field and requires enabling MatchLabelKeysInPodAffinity feature gate. + items: + type: string + type: array + x-kubernetes-list-type: atomic + namespaceSelector: + description: |- + A label query over the set of namespaces that the term applies to. + The term is applied to the union of the namespaces selected by this field + and the ones listed in the namespaces field. + null selector and null or empty namespaces list means "this pod's namespace". + An empty selector ({}) matches all namespaces. + properties: + matchExpressions: + description: matchExpressions is a list of label selector requirements. The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + namespaces: + description: |- + namespaces specifies a static list of namespace names that the term applies to. + The term is applied to the union of the namespaces listed in this field + and the ones selected by namespaceSelector. + null or empty namespaces list and null namespaceSelector means "this pod's namespace". + items: + type: string + type: array + x-kubernetes-list-type: atomic + topologyKey: + description: |- + This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching + the labelSelector in the specified namespaces, where co-located is defined as running on a node + whose value of the label with key topologyKey matches that of any node on which any of the + selected pods is running. + Empty topologyKey is not allowed. + type: string + required: + - topologyKey + type: object + type: array + x-kubernetes-list-type: atomic + type: object + podAntiAffinity: + description: Describes pod anti-affinity scheduling rules (e.g. avoid putting this pod in the same node, zone, etc. as some other pod(s)). + properties: + preferredDuringSchedulingIgnoredDuringExecution: + description: |- + The scheduler will prefer to schedule pods to nodes that satisfy + the anti-affinity expressions specified by this field, but it may choose + a node that violates one or more of the expressions. The node that is + most preferred is the one with the greatest sum of weights, i.e. + for each node that meets all of the scheduling requirements (resource + request, requiredDuringScheduling anti-affinity expressions, etc.), + compute a sum by iterating through the elements of this field and adding + "weight" to the sum if the node has pods which matches the corresponding podAffinityTerm; the + node(s) with the highest sum are the most preferred. + items: + description: The weights of all of the matched WeightedPodAffinityTerm fields are added per-node to find the most preferred node(s) + properties: + podAffinityTerm: + description: Required. A pod affinity term, associated with the corresponding weight. + properties: + labelSelector: + description: |- + A label query over a set of resources, in this case pods. + If it's null, this PodAffinityTerm matches with no Pods. + properties: + matchExpressions: + description: matchExpressions is a list of label selector requirements. The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + matchLabelKeys: + description: |- + MatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `labelSelector` as `key in (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both matchLabelKeys and labelSelector. + Also, matchLabelKeys cannot be set when labelSelector isn't set. + This is an alpha field and requires enabling MatchLabelKeysInPodAffinity feature gate. + items: + type: string + type: array + x-kubernetes-list-type: atomic + mismatchLabelKeys: + description: |- + MismatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `labelSelector` as `key notin (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both mismatchLabelKeys and labelSelector. + Also, mismatchLabelKeys cannot be set when labelSelector isn't set. + This is an alpha field and requires enabling MatchLabelKeysInPodAffinity feature gate. + items: + type: string + type: array + x-kubernetes-list-type: atomic + namespaceSelector: + description: |- + A label query over the set of namespaces that the term applies to. + The term is applied to the union of the namespaces selected by this field + and the ones listed in the namespaces field. + null selector and null or empty namespaces list means "this pod's namespace". + An empty selector ({}) matches all namespaces. + properties: + matchExpressions: + description: matchExpressions is a list of label selector requirements. The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + namespaces: + description: |- + namespaces specifies a static list of namespace names that the term applies to. + The term is applied to the union of the namespaces listed in this field + and the ones selected by namespaceSelector. + null or empty namespaces list and null namespaceSelector means "this pod's namespace". + items: + type: string + type: array + x-kubernetes-list-type: atomic + topologyKey: + description: |- + This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching + the labelSelector in the specified namespaces, where co-located is defined as running on a node + whose value of the label with key topologyKey matches that of any node on which any of the + selected pods is running. + Empty topologyKey is not allowed. + type: string + required: + - topologyKey + type: object + weight: + description: |- + weight associated with matching the corresponding podAffinityTerm, + in the range 1-100. + format: int32 + type: integer + required: + - podAffinityTerm + - weight + type: object + type: array + x-kubernetes-list-type: atomic + requiredDuringSchedulingIgnoredDuringExecution: + description: |- + If the anti-affinity requirements specified by this field are not met at + scheduling time, the pod will not be scheduled onto the node. + If the anti-affinity requirements specified by this field cease to be met + at some point during pod execution (e.g. due to a pod label update), the + system may or may not try to eventually evict the pod from its node. + When there are multiple elements, the lists of nodes corresponding to each + podAffinityTerm are intersected, i.e. all terms must be satisfied. + items: + description: |- + Defines a set of pods (namely those matching the labelSelector + relative to the given namespace(s)) that this pod should be + co-located (affinity) or not co-located (anti-affinity) with, + where co-located is defined as running on a node whose value of + the label with key matches that of any node on which + a pod of the set of pods is running + properties: + labelSelector: + description: |- + A label query over a set of resources, in this case pods. + If it's null, this PodAffinityTerm matches with no Pods. + properties: + matchExpressions: + description: matchExpressions is a list of label selector requirements. The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + matchLabelKeys: + description: |- + MatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `labelSelector` as `key in (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both matchLabelKeys and labelSelector. + Also, matchLabelKeys cannot be set when labelSelector isn't set. + This is an alpha field and requires enabling MatchLabelKeysInPodAffinity feature gate. + items: + type: string + type: array + x-kubernetes-list-type: atomic + mismatchLabelKeys: + description: |- + MismatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `labelSelector` as `key notin (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both mismatchLabelKeys and labelSelector. + Also, mismatchLabelKeys cannot be set when labelSelector isn't set. + This is an alpha field and requires enabling MatchLabelKeysInPodAffinity feature gate. + items: + type: string + type: array + x-kubernetes-list-type: atomic + namespaceSelector: + description: |- + A label query over the set of namespaces that the term applies to. + The term is applied to the union of the namespaces selected by this field + and the ones listed in the namespaces field. + null selector and null or empty namespaces list means "this pod's namespace". + An empty selector ({}) matches all namespaces. + properties: + matchExpressions: + description: matchExpressions is a list of label selector requirements. The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + namespaces: + description: |- + namespaces specifies a static list of namespace names that the term applies to. + The term is applied to the union of the namespaces listed in this field + and the ones selected by namespaceSelector. + null or empty namespaces list and null namespaceSelector means "this pod's namespace". + items: + type: string + type: array + x-kubernetes-list-type: atomic + topologyKey: + description: |- + This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching + the labelSelector in the specified namespaces, where co-located is defined as running on a node + whose value of the label with key topologyKey matches that of any node on which any of the + selected pods is running. + Empty topologyKey is not allowed. + type: string + required: + - topologyKey + type: object + type: array + x-kubernetes-list-type: atomic + type: object + type: object + claims: + description: |- + Claims lists the names of resources, defined in spec.resourceClaims, + that are used by this container. + + + This is an alpha field and requires enabling the + DynamicResourceAllocation feature gate. + + + This field is immutable. It can only be set for containers. + items: + description: ResourceClaim references one entry in PodSpec.ResourceClaims. + properties: + name: + description: |- + Name must match the name of one entry in pod.spec.resourceClaims of + the Pod where this field is used. It makes that resource available + inside a container. + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: |- + Limits describes the maximum amount of compute resources allowed. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + type: object + nodeSelector: + additionalProperties: + type: string + type: object + priorityClassName: + type: string + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: |- + Requests describes the minimum amount of compute resources required. + If Requests is omitted for a container, it defaults to Limits if that is explicitly specified, + otherwise to an implementation-defined value. Requests cannot exceed Limits. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + type: object + tolerations: + description: agent.pod.tolerations are tolerations to influence agent pod assignment. + items: + description: |- + The pod this Toleration is attached to tolerates any taint that matches + the triple using the matching operator . + properties: + effect: + description: |- + Effect indicates the taint effect to match. Empty means match all taint effects. + When specified, allowed values are NoSchedule, PreferNoSchedule and NoExecute. + type: string + key: + description: |- + Key is the taint key that the toleration applies to. Empty means match all taint keys. + If the key is empty, operator must be Exists; this combination means to match all values and all keys. + type: string + operator: + description: |- + Operator represents a key's relationship to the value. + Valid operators are Exists and Equal. Defaults to Equal. + Exists is equivalent to wildcard for value, so that a pod can + tolerate all taints of a particular category. + type: string + tolerationSeconds: + description: |- + TolerationSeconds represents the period of time the toleration (which must be + of effect NoExecute, otherwise this field is ignored) tolerates the taint. By default, + it is not set, which means tolerate the taint forever (do not evict). Zero and + negative values will be treated as 0 (evict immediately) by the system. + format: int64 + type: integer + value: + description: |- + Value is the taint value the toleration matches to. + If the operator is Exists, the value should be empty, otherwise just a regular string. + type: string + type: object + type: array + type: object + replicas: + description: Specify the number of replicas for the Kubernetes Sensor. + type: integer + type: object + type: object + openshift: + description: |- + Set to `True` to indicate the Operator is being deployed in a OpenShift cluster. Provides a hint so that RBAC etc is + configured correctly. Will attempt to auto-detect if unset. + type: boolean + opentelemetry: + description: 'Enables the OpenTelemetry gRPC endpoint on the Agent. If true, it will also apply `service.create: true`.' + properties: + enabled: + type: boolean + grpc: + properties: + enabled: + type: boolean + type: object + http: + properties: + enabled: + type: boolean + type: object + type: object + pinnedChartVersion: + description: |- + Specifying the PinnedChartVersion allows for 'pinning' the Helm Chart used by the Operator for installing the Agent + DaemonSet. Normally the Operator will always install and update to the latest Helm Chart version. + The Operator will check and make sure no 'unsupported' Chart versions can be selected. + type: string + podSecurityPolicy: + description: 'Specify a PodSecurityPolicy for the Instana Agent Pods. If enabled requires `rbac.create: true`.' + properties: + enabled: + type: boolean + name: + type: string + type: object + prometheus: + description: 'Enables the Prometheus endpoint on the Agent. If true, it will also apply `service.create: true`.' + properties: + remoteWrite: + properties: + enabled: + type: boolean + type: object + type: object + rbac: + description: Specifies whether RBAC resources should be created. + properties: + create: + type: boolean + type: object + service: + description: |- + Specifies whether to create the instana-agent `Service` to expose within the cluster. The Service can then be used e.g. + for the Prometheus remote-write, OpenTelemetry GRCP endpoint and other APIs. + Note: Requires Kubernetes 1.17+, as it uses topologyKeys. + properties: + create: + type: boolean + type: object + serviceAccount: + description: Specifies whether a ServiceAccount should be created (default `true`), and possibly the name to use. + properties: + annotations: + additionalProperties: + type: string + type: object + create: + type: boolean + name: + type: string + type: object + serviceMesh: + properties: + enabled: + type: boolean + type: object + zone: + description: Name of the zone in which the host(s) will be displayed on the map. Optional, but then 'cluster.name' must be specified. + properties: + name: + type: string + type: object + zones: + description: Zones can be used to specify agents in multiple zones split across different nodes in the cluster + items: + properties: + affinity: + description: Affinity is a group of affinity scheduling rules. + properties: + nodeAffinity: + description: Describes node affinity scheduling rules for the pod. + properties: + preferredDuringSchedulingIgnoredDuringExecution: + description: |- + The scheduler will prefer to schedule pods to nodes that satisfy + the affinity expressions specified by this field, but it may choose + a node that violates one or more of the expressions. The node that is + most preferred is the one with the greatest sum of weights, i.e. + for each node that meets all of the scheduling requirements (resource + request, requiredDuringScheduling affinity expressions, etc.), + compute a sum by iterating through the elements of this field and adding + "weight" to the sum if the node matches the corresponding matchExpressions; the + node(s) with the highest sum are the most preferred. + items: + description: |- + An empty preferred scheduling term matches all objects with implicit weight 0 + (i.e. it's a no-op). A null preferred scheduling term matches no objects (i.e. is also a no-op). + properties: + preference: + description: A node selector term, associated with the corresponding weight. + properties: + matchExpressions: + description: A list of node selector requirements by node's labels. + items: + description: |- + A node selector requirement is a selector that contains values, a key, and an operator + that relates the key and values. + properties: + key: + description: The label key that the selector applies to. + type: string + operator: + description: |- + Represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt. + type: string + values: + description: |- + An array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. If the operator is Gt or Lt, the values + array must have a single element, which will be interpreted as an integer. + This array is replaced during a strategic merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchFields: + description: A list of node selector requirements by node's fields. + items: + description: |- + A node selector requirement is a selector that contains values, a key, and an operator + that relates the key and values. + properties: + key: + description: The label key that the selector applies to. + type: string + operator: + description: |- + Represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt. + type: string + values: + description: |- + An array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. If the operator is Gt or Lt, the values + array must have a single element, which will be interpreted as an integer. + This array is replaced during a strategic merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + type: object + x-kubernetes-map-type: atomic + weight: + description: Weight associated with matching the corresponding nodeSelectorTerm, in the range 1-100. + format: int32 + type: integer + required: + - preference + - weight + type: object + type: array + x-kubernetes-list-type: atomic + requiredDuringSchedulingIgnoredDuringExecution: + description: |- + If the affinity requirements specified by this field are not met at + scheduling time, the pod will not be scheduled onto the node. + If the affinity requirements specified by this field cease to be met + at some point during pod execution (e.g. due to an update), the system + may or may not try to eventually evict the pod from its node. + properties: + nodeSelectorTerms: + description: Required. A list of node selector terms. The terms are ORed. + items: + description: |- + A null or empty node selector term matches no objects. The requirements of + them are ANDed. + The TopologySelectorTerm type implements a subset of the NodeSelectorTerm. + properties: + matchExpressions: + description: A list of node selector requirements by node's labels. + items: + description: |- + A node selector requirement is a selector that contains values, a key, and an operator + that relates the key and values. + properties: + key: + description: The label key that the selector applies to. + type: string + operator: + description: |- + Represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt. + type: string + values: + description: |- + An array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. If the operator is Gt or Lt, the values + array must have a single element, which will be interpreted as an integer. + This array is replaced during a strategic merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchFields: + description: A list of node selector requirements by node's fields. + items: + description: |- + A node selector requirement is a selector that contains values, a key, and an operator + that relates the key and values. + properties: + key: + description: The label key that the selector applies to. + type: string + operator: + description: |- + Represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt. + type: string + values: + description: |- + An array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. If the operator is Gt or Lt, the values + array must have a single element, which will be interpreted as an integer. + This array is replaced during a strategic merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + type: object + x-kubernetes-map-type: atomic + type: array + x-kubernetes-list-type: atomic + required: + - nodeSelectorTerms + type: object + x-kubernetes-map-type: atomic + type: object + podAffinity: + description: Describes pod affinity scheduling rules (e.g. co-locate this pod in the same node, zone, etc. as some other pod(s)). + properties: + preferredDuringSchedulingIgnoredDuringExecution: + description: |- + The scheduler will prefer to schedule pods to nodes that satisfy + the affinity expressions specified by this field, but it may choose + a node that violates one or more of the expressions. The node that is + most preferred is the one with the greatest sum of weights, i.e. + for each node that meets all of the scheduling requirements (resource + request, requiredDuringScheduling affinity expressions, etc.), + compute a sum by iterating through the elements of this field and adding + "weight" to the sum if the node has pods which matches the corresponding podAffinityTerm; the + node(s) with the highest sum are the most preferred. + items: + description: The weights of all of the matched WeightedPodAffinityTerm fields are added per-node to find the most preferred node(s) + properties: + podAffinityTerm: + description: Required. A pod affinity term, associated with the corresponding weight. + properties: + labelSelector: + description: |- + A label query over a set of resources, in this case pods. + If it's null, this PodAffinityTerm matches with no Pods. + properties: + matchExpressions: + description: matchExpressions is a list of label selector requirements. The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + matchLabelKeys: + description: |- + MatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `labelSelector` as `key in (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both matchLabelKeys and labelSelector. + Also, matchLabelKeys cannot be set when labelSelector isn't set. + This is an alpha field and requires enabling MatchLabelKeysInPodAffinity feature gate. + items: + type: string + type: array + x-kubernetes-list-type: atomic + mismatchLabelKeys: + description: |- + MismatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `labelSelector` as `key notin (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both mismatchLabelKeys and labelSelector. + Also, mismatchLabelKeys cannot be set when labelSelector isn't set. + This is an alpha field and requires enabling MatchLabelKeysInPodAffinity feature gate. + items: + type: string + type: array + x-kubernetes-list-type: atomic + namespaceSelector: + description: |- + A label query over the set of namespaces that the term applies to. + The term is applied to the union of the namespaces selected by this field + and the ones listed in the namespaces field. + null selector and null or empty namespaces list means "this pod's namespace". + An empty selector ({}) matches all namespaces. + properties: + matchExpressions: + description: matchExpressions is a list of label selector requirements. The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + namespaces: + description: |- + namespaces specifies a static list of namespace names that the term applies to. + The term is applied to the union of the namespaces listed in this field + and the ones selected by namespaceSelector. + null or empty namespaces list and null namespaceSelector means "this pod's namespace". + items: + type: string + type: array + x-kubernetes-list-type: atomic + topologyKey: + description: |- + This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching + the labelSelector in the specified namespaces, where co-located is defined as running on a node + whose value of the label with key topologyKey matches that of any node on which any of the + selected pods is running. + Empty topologyKey is not allowed. + type: string + required: + - topologyKey + type: object + weight: + description: |- + weight associated with matching the corresponding podAffinityTerm, + in the range 1-100. + format: int32 + type: integer + required: + - podAffinityTerm + - weight + type: object + type: array + x-kubernetes-list-type: atomic + requiredDuringSchedulingIgnoredDuringExecution: + description: |- + If the affinity requirements specified by this field are not met at + scheduling time, the pod will not be scheduled onto the node. + If the affinity requirements specified by this field cease to be met + at some point during pod execution (e.g. due to a pod label update), the + system may or may not try to eventually evict the pod from its node. + When there are multiple elements, the lists of nodes corresponding to each + podAffinityTerm are intersected, i.e. all terms must be satisfied. + items: + description: |- + Defines a set of pods (namely those matching the labelSelector + relative to the given namespace(s)) that this pod should be + co-located (affinity) or not co-located (anti-affinity) with, + where co-located is defined as running on a node whose value of + the label with key matches that of any node on which + a pod of the set of pods is running + properties: + labelSelector: + description: |- + A label query over a set of resources, in this case pods. + If it's null, this PodAffinityTerm matches with no Pods. + properties: + matchExpressions: + description: matchExpressions is a list of label selector requirements. The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + matchLabelKeys: + description: |- + MatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `labelSelector` as `key in (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both matchLabelKeys and labelSelector. + Also, matchLabelKeys cannot be set when labelSelector isn't set. + This is an alpha field and requires enabling MatchLabelKeysInPodAffinity feature gate. + items: + type: string + type: array + x-kubernetes-list-type: atomic + mismatchLabelKeys: + description: |- + MismatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `labelSelector` as `key notin (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both mismatchLabelKeys and labelSelector. + Also, mismatchLabelKeys cannot be set when labelSelector isn't set. + This is an alpha field and requires enabling MatchLabelKeysInPodAffinity feature gate. + items: + type: string + type: array + x-kubernetes-list-type: atomic + namespaceSelector: + description: |- + A label query over the set of namespaces that the term applies to. + The term is applied to the union of the namespaces selected by this field + and the ones listed in the namespaces field. + null selector and null or empty namespaces list means "this pod's namespace". + An empty selector ({}) matches all namespaces. + properties: + matchExpressions: + description: matchExpressions is a list of label selector requirements. The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + namespaces: + description: |- + namespaces specifies a static list of namespace names that the term applies to. + The term is applied to the union of the namespaces listed in this field + and the ones selected by namespaceSelector. + null or empty namespaces list and null namespaceSelector means "this pod's namespace". + items: + type: string + type: array + x-kubernetes-list-type: atomic + topologyKey: + description: |- + This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching + the labelSelector in the specified namespaces, where co-located is defined as running on a node + whose value of the label with key topologyKey matches that of any node on which any of the + selected pods is running. + Empty topologyKey is not allowed. + type: string + required: + - topologyKey + type: object + type: array + x-kubernetes-list-type: atomic + type: object + podAntiAffinity: + description: Describes pod anti-affinity scheduling rules (e.g. avoid putting this pod in the same node, zone, etc. as some other pod(s)). + properties: + preferredDuringSchedulingIgnoredDuringExecution: + description: |- + The scheduler will prefer to schedule pods to nodes that satisfy + the anti-affinity expressions specified by this field, but it may choose + a node that violates one or more of the expressions. The node that is + most preferred is the one with the greatest sum of weights, i.e. + for each node that meets all of the scheduling requirements (resource + request, requiredDuringScheduling anti-affinity expressions, etc.), + compute a sum by iterating through the elements of this field and adding + "weight" to the sum if the node has pods which matches the corresponding podAffinityTerm; the + node(s) with the highest sum are the most preferred. + items: + description: The weights of all of the matched WeightedPodAffinityTerm fields are added per-node to find the most preferred node(s) + properties: + podAffinityTerm: + description: Required. A pod affinity term, associated with the corresponding weight. + properties: + labelSelector: + description: |- + A label query over a set of resources, in this case pods. + If it's null, this PodAffinityTerm matches with no Pods. + properties: + matchExpressions: + description: matchExpressions is a list of label selector requirements. The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + matchLabelKeys: + description: |- + MatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `labelSelector` as `key in (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both matchLabelKeys and labelSelector. + Also, matchLabelKeys cannot be set when labelSelector isn't set. + This is an alpha field and requires enabling MatchLabelKeysInPodAffinity feature gate. + items: + type: string + type: array + x-kubernetes-list-type: atomic + mismatchLabelKeys: + description: |- + MismatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `labelSelector` as `key notin (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both mismatchLabelKeys and labelSelector. + Also, mismatchLabelKeys cannot be set when labelSelector isn't set. + This is an alpha field and requires enabling MatchLabelKeysInPodAffinity feature gate. + items: + type: string + type: array + x-kubernetes-list-type: atomic + namespaceSelector: + description: |- + A label query over the set of namespaces that the term applies to. + The term is applied to the union of the namespaces selected by this field + and the ones listed in the namespaces field. + null selector and null or empty namespaces list means "this pod's namespace". + An empty selector ({}) matches all namespaces. + properties: + matchExpressions: + description: matchExpressions is a list of label selector requirements. The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + namespaces: + description: |- + namespaces specifies a static list of namespace names that the term applies to. + The term is applied to the union of the namespaces listed in this field + and the ones selected by namespaceSelector. + null or empty namespaces list and null namespaceSelector means "this pod's namespace". + items: + type: string + type: array + x-kubernetes-list-type: atomic + topologyKey: + description: |- + This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching + the labelSelector in the specified namespaces, where co-located is defined as running on a node + whose value of the label with key topologyKey matches that of any node on which any of the + selected pods is running. + Empty topologyKey is not allowed. + type: string + required: + - topologyKey + type: object + weight: + description: |- + weight associated with matching the corresponding podAffinityTerm, + in the range 1-100. + format: int32 + type: integer + required: + - podAffinityTerm + - weight + type: object + type: array + x-kubernetes-list-type: atomic + requiredDuringSchedulingIgnoredDuringExecution: + description: |- + If the anti-affinity requirements specified by this field are not met at + scheduling time, the pod will not be scheduled onto the node. + If the anti-affinity requirements specified by this field cease to be met + at some point during pod execution (e.g. due to a pod label update), the + system may or may not try to eventually evict the pod from its node. + When there are multiple elements, the lists of nodes corresponding to each + podAffinityTerm are intersected, i.e. all terms must be satisfied. + items: + description: |- + Defines a set of pods (namely those matching the labelSelector + relative to the given namespace(s)) that this pod should be + co-located (affinity) or not co-located (anti-affinity) with, + where co-located is defined as running on a node whose value of + the label with key matches that of any node on which + a pod of the set of pods is running + properties: + labelSelector: + description: |- + A label query over a set of resources, in this case pods. + If it's null, this PodAffinityTerm matches with no Pods. + properties: + matchExpressions: + description: matchExpressions is a list of label selector requirements. The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + matchLabelKeys: + description: |- + MatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `labelSelector` as `key in (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both matchLabelKeys and labelSelector. + Also, matchLabelKeys cannot be set when labelSelector isn't set. + This is an alpha field and requires enabling MatchLabelKeysInPodAffinity feature gate. + items: + type: string + type: array + x-kubernetes-list-type: atomic + mismatchLabelKeys: + description: |- + MismatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `labelSelector` as `key notin (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both mismatchLabelKeys and labelSelector. + Also, mismatchLabelKeys cannot be set when labelSelector isn't set. + This is an alpha field and requires enabling MatchLabelKeysInPodAffinity feature gate. + items: + type: string + type: array + x-kubernetes-list-type: atomic + namespaceSelector: + description: |- + A label query over the set of namespaces that the term applies to. + The term is applied to the union of the namespaces selected by this field + and the ones listed in the namespaces field. + null selector and null or empty namespaces list means "this pod's namespace". + An empty selector ({}) matches all namespaces. + properties: + matchExpressions: + description: matchExpressions is a list of label selector requirements. The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + namespaces: + description: |- + namespaces specifies a static list of namespace names that the term applies to. + The term is applied to the union of the namespaces listed in this field + and the ones selected by namespaceSelector. + null or empty namespaces list and null namespaceSelector means "this pod's namespace". + items: + type: string + type: array + x-kubernetes-list-type: atomic + topologyKey: + description: |- + This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching + the labelSelector in the specified namespaces, where co-located is defined as running on a node + whose value of the label with key topologyKey matches that of any node on which any of the + selected pods is running. + Empty topologyKey is not allowed. + type: string + required: + - topologyKey + type: object + type: array + x-kubernetes-list-type: atomic + type: object + type: object + mode: + type: string + name: + type: string + tolerations: + items: + description: |- + The pod this Toleration is attached to tolerates any taint that matches + the triple using the matching operator . + properties: + effect: + description: |- + Effect indicates the taint effect to match. Empty means match all taint effects. + When specified, allowed values are NoSchedule, PreferNoSchedule and NoExecute. + type: string + key: + description: |- + Key is the taint key that the toleration applies to. Empty means match all taint keys. + If the key is empty, operator must be Exists; this combination means to match all values and all keys. + type: string + operator: + description: |- + Operator represents a key's relationship to the value. + Valid operators are Exists and Equal. Defaults to Equal. + Exists is equivalent to wildcard for value, so that a pod can + tolerate all taints of a particular category. + type: string + tolerationSeconds: + description: |- + TolerationSeconds represents the period of time the toleration (which must be + of effect NoExecute, otherwise this field is ignored) tolerates the taint. By default, + it is not set, which means tolerate the taint forever (do not evict). Zero and + negative values will be treated as 0 (evict immediately) by the system. + format: int64 + type: integer + value: + description: |- + Value is the taint value the toleration matches to. + If the operator is Exists, the value should be empty, otherwise just a regular string. + type: string + type: object + type: array + type: object + type: array + required: + - agent + type: object + status: + properties: + conditions: + items: + description: "Condition contains details for one aspect of the current state of this API Resource.\n---\nThis struct is intended for direct use as an array at the field path .status.conditions. For example,\n\n\n\ttype FooStatus struct{\n\t // Represents the observations of a foo's current state.\n\t // Known .status.conditions.type are: \"Available\", \"Progressing\", and \"Degraded\"\n\t // +patchMergeKey=type\n\t // +patchStrategy=merge\n\t // +listType=map\n\t // +listMapKey=type\n\t Conditions []metav1.Condition `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"`\n\n\n\t // other fields\n\t}" + properties: + lastTransitionTime: + description: |- + lastTransitionTime is the last time the condition transitioned from one status to another. + This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: |- + message is a human readable message indicating details about the transition. + This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: |- + observedGeneration represents the .metadata.generation that the condition was set based upon. + For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date + with respect to the current state of the instance. + format: int64 + minimum: 0 + type: integer + reason: + description: |- + reason contains a programmatic identifier indicating the reason for the condition's last transition. + Producers of specific condition types may define expected values and meanings for this field, + and whether the values are considered a guaranteed API. + The value should be a CamelCase string. + This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: |- + type of condition in CamelCase or in foo.example.com/CamelCase. + --- + Many .condition.type values are consistent across resources like Available, but because arbitrary conditions can be + useful (see .node.status.conditions), the ability to deconflict is important. + The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + configmap: + description: ResourceInfo holds Name and UID to given object + properties: + name: + type: string + uid: + type: string + required: + - name + - uid + type: object + configsecret: + description: ResourceInfo holds Name and UID to given object + properties: + name: + type: string + uid: + type: string + required: + - name + - uid + type: object + daemonset: + description: ResourceInfo holds Name and UID to given object + properties: + name: + type: string + uid: + type: string + required: + - name + - uid + type: object + lastUpdate: + format: date-time + type: string + leadingAgentPod: + additionalProperties: + description: ResourceInfo holds Name and UID to given object + properties: + name: + type: string + uid: + type: string + required: + - name + - uid + type: object + type: object + observedGeneration: + format: int64 + minimum: 0 + type: integer + oldVersionsUpdated: + type: boolean + operatorVersion: + pattern: ^v?(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$ + type: string + reason: + type: string + status: + description: AgentOperatorState type representing the running state of the Agent Operator itself. + type: string + type: object + type: object + served: true + storage: true + subresources: + status: {} diff --git a/charts/instana/instana-agent/2.0.13/kubernetes.deployment.enabled.png b/charts/instana/instana-agent/2.0.13/kubernetes.deployment.enabled.png new file mode 100644 index 0000000000000000000000000000000000000000..9358f6f1428da753b468891bb443da5b607c0c5c GIT binary patch literal 71985 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 deploy + cm >> ds + dcm >> deploy \ No newline at end of file diff --git a/charts/instana/instana-agent/2.0.13/questions.yml b/charts/instana/instana-agent/2.0.13/questions.yml new file mode 100644 index 0000000000..bf9d1a46d4 --- /dev/null +++ b/charts/instana/instana-agent/2.0.13/questions.yml @@ -0,0 +1,236 @@ +questions: +# Basic agent configuration +- variable: agent.key + label: agent.key + description: "Your Instana Agent key is the secret token which your agent uses to authenticate to Instana's servers" + type: string + required: true + group: "Agent Configuration" +- variable: agent.endpointHost + label: agent.endpointHost + description: "The hostname of the Instana server your agents will connect to. Defaults to ingress-red-saas.instana.io for US and ROW. If in Europe, please use ingress-blue-saas.instana.io" + type: string + required: true + default: "ingress-red-saas.instana.io" + group: "Agent Configuration" +- variable: zone.name + label: zone.name + description: "Custom zone that detected technologies will be assigned to" + type: string + required: true + group: "Agent Configuration" +# Advanced agent configuration +- variable: advancedAgentConfiguration + description: "Show advanced configuration for the Instana Agent" + label: Show advanced configuration + type: boolean + default: false + show_subquestion_if: true + group: "Advanced Agent Configuration" + subquestions: + - variable: agent.configuration_yaml + label: agent.configuration_yaml (Optional) + description: "Custom content for the agent configuration.yaml file in YAML format. Please use the 'Edit as YAML' feature in the Rancher UI for the best editing experience." + type: string + required: false + group: "Advanced Agent Configuration" + - variable: agent.downloadKey + label: agent.downloadKey (Optional) + description: "Your Instana download key" + type: string + required: false + group: "Advanced Agent Configuration" + - variable: agent.endpointPort + label: agent.endpointPort + description: "The Agent backend port number (as a string) of the Instana server your agents will connect to" + type: string + required: true + default: "443" + group: "Advanced Agent Configuration" + - variable: agent.image.name + label: agent.image.name + description: "The name of the Instana Agent container image" + type: string + required: true + default: "instana/agent" + group: "Advanced Agent Configuration" + - variable: agent.image.tag + label: agent.image.tag + description: "The tag name of the Instana Agent container image" + type: string + required: true + default: "latest" + group: "Advanced Agent Configuration" + - variable: agent.image.pullPolicy + label: agent.image.pullPolicy + description: "Specifies when to pull the Instana Agent image container" + type: string + required: true + default: "Always" + group: "Advanced Agent Configuration" + - variable: agent.listenAddress + label: agent.listenAddress (Optional) + description: "The IP address the agent HTTP server will listen to, or '*' for all interfaces" + type: string + required: false + group: "Advanced Agent Configuration" + - variable: agent.mode + label: agent.mode (Optional) + description: "Agent mode. Possible options are: APM, INFRASTRUCTURE or AWS" + type: enum + options: + - "APM" + - "INFRASTRUCTURE" + - "AWS" + required: false + group: "Advanced Agent Configuration" + - variable: agent.pod.annotations + label: agent.pod.annotations (Optional) + description: "Additional annotations to be added to the agent pods in YAML format. Please use the 'Edit as YAML' feature in the Rancher UI for the best editing experience." + type: string + required: false + group: "Advanced Agent Configuration" + - variable: agent.pod.limits.cpu + label: agent.pod.limits.cpu + description: "CPU units allocation limits for the agent pods" + type: string + required: true + default: "1.5" + group: "Advanced Agent Configuration" + - variable: agent.pod.limits.memory + label: agent.pod.limits.memory + description: "Memory allocation limits in MiB for the agent pods" + type: int + required: true + default: 512 + group: "Advanced Agent Configuration" + - variable: agent.pod.proxyHost + label: agent.pod.proxyHost (Optional) + description: "Hostname/address of a proxy. Sets the INSTANA_AGENT_PROXY_HOST environment variable" + type: string + required: false + group: "Advanced Agent Configuration" + - variable: agent.pod.proxyPort + label: agent.pod.proxyPort (Optional) + description: "Port of a proxy. Sets the INSTANA_AGENT_PROXY_PORT environment variable" + type: string + required: false + group: "Advanced Agent Configuration" + - variable: agent.pod.proxyProtocol + label: agent.pod.proxyProtocol (Optional) + description: "Proxy protocol. Sets the INSTANA_AGENT_PROXY_PROTOCOL environment variable. Supported proxy types are http, socks4, socks5" + type: string + required: false + group: "Advanced Agent Configuration" + - variable: agent.pod.proxyUser + label: agent.pod.proxyUser (Optional) + description: "Username of the proxy auth. Sets the INSTANA_AGENT_PROXY_USER environment variable" + type: string + required: false + group: "Advanced Agent Configuration" + - variable: agent.pod.proxyPassword + label: agent.pod.proxyPassword (Optional) + description: "Password of the proxy auth. Sets the INSTANA_AGENT_PROXY_PASSWORD environment variable" + type: string + required: false + group: "Advanced Agent Configuration" + - variable: agent.pod.proxyUseDNS + label: agent.pod.proxyUseDNS. (Optional) + description: "Boolean if proxy also does DNS. Sets the INSTANA_AGENT_PROXY_USE_DNS environment variable" + type: enum + options: + - "true" + - "false" + required: false + group: "Advanced Agent Configuration" + - variable: agent.pod.requests.cpu + label: agent.pod.requests.cpu + description: "Requested CPU units allocation for the agent pods" + type: string + required: true + default: "0.5" + group: "Advanced Agent Configuration" + - variable: agent.pod.requests.memory + label: agent.pod.requests.memory + description: "Requested memory allocation in MiB for the agent pods" + type: int + required: true + default: 512 + group: "Advanced Agent Configuration" + - variable: agent.pod.tolerations + label: agent.pod.tolerations (Optional) + description: "Tolerations to influence agent pod assignment in YAML format. Please use the 'Edit as YAML' feature in the Rancher UI for the best editing experience." + type: string + required: false + group: "Advanced Agent Configuration" + - variable: agent.redactKubernetesSecrets + label: agent.redactKubernetesSecrets (Optional) + description: "Enable additional secrets redaction for selected Kubernetes resources" + type: boolean + required: false + default: false + group: "Advanced Agent Configuration" + - variable: cluster.name + label: cluster.name (Optional) + description: "The name that will be assigned to this cluster in Instana. See the 'Installing the Chart' section in the 'Detailed Descriptions' tab for more details" + type: string + required: false + group: "Advanced Agent Configuration" + - variable: leaderElector.image.name + label: leaderElector.image.name + description: "The name of the leader elector container image" + type: string + required: true + default: "instana/leader-elector" + group: "Advanced Agent Configuration" + - variable: leaderElector.image.tag + label: leaderElector.image.tag + description: "The tag name of the leader elector container image" + type: string + required: true + default: "0.5.4" + group: "Advanced Agent Configuration" + - variable: leaderElector.port + label: leaderElector.port + description: "The port on which the leader elector sidecar is exposed" + type: int + required: true + default: 42655 + group: "Advanced Agent Configuration" +- variable: podSecurityPolicy.enable + label: podSecurityPolicy.enable (Optional) + description: "Specifies whether a PodSecurityPolicy should be authorized for the Instana Agent pods. Requires `rbac.create` to also be `true`" + type: boolean + show_if: "rbac.create=true" + required: false + default: false + group: "Pod Security Policy Configuration" +- variable: podSecurityPolicy.name + label: podSecurityPolicy.name (Optional) + description: "The name of an existing PodSecurityPolicy you would like to authorize for the Instana Agent pods. If not set and `podSecurityPolicy.enable` is `true`, a PodSecurityPolicy will be created with a name generated using the fullname template" + type: string + show_if: "rbac.create=true&&podSecurityPolicy.enable=true" + required: false + group: "Pod Security Policy Configuration" +- variable: rbac.create + label: rbac.create + description: "Specifies whether RBAC resources should be created" + type: boolean + required: true + default: true + group: "RBAC Configuration" +- variable: serviceAccount.create + label: serviceAccount.create + description: "Specifies whether a ServiceAccount should be created" + type: boolean + required: true + default: true + show_subquestion_if: true + group: "RBAC Configuration" + subquestions: + - variable: serviceAccount.name + label: Name of the ServiceAccount (Optional) + description: "The name of the ServiceAccount to use. If not set and `serviceAccount.create` is true, a name is generated using the fullname template." + type: string + required: false + group: "RBAC Configuration" diff --git a/charts/instana/instana-agent/2.0.13/templates/NOTES.txt b/charts/instana/instana-agent/2.0.13/templates/NOTES.txt new file mode 100644 index 0000000000..8243ecd20d --- /dev/null +++ b/charts/instana/instana-agent/2.0.13/templates/NOTES.txt @@ -0,0 +1,73 @@ +{{- if (and (not (or .Values.agent.key .Values.agent.keysSecret )) (and (not .Values.zone.name) (not .Values.cluster.name))) }} +############################################################################## +#### ERROR: You did not specify your secret agent key. #### +#### ERROR: You also did not specify a zone or name for this cluster. #### +############################################################################## + +This agent deployment will be incomplete until you set your agent key and zone or name for this cluster: + + helm upgrade {{ .Release.Name }} --reuse-values \ + --repo https://agents.instana.io/helm \ + --set agent.key=$(YOUR_SECRET_AGENT_KEY) \ + --set zone.name=$(YOUR_ZONE_NAME) instana-agent + +Alternatively, you may specify a cluster name and the zone will be detected from availability zone information on the host: + + helm upgrade {{ .Release.Name }} --reuse-values \ + --repo https://agents.instana.io/helm \ + --set agent.key=$(YOUR_SECRET_AGENT_KEY) \ + --set cluster.name=$(YOUR_CLUSTER_NAME) instana-agent + +- YOUR_SECRET_AGENT_KEY can be obtained from the Management Portal section of your Instana installation. +- YOUR_ZONE_NAME should be the zone that detected technologies will be assigned to. +- YOUR_CLUSTER_NAME should be the custom name of your cluster. + +At least one of zone.name or cluster.name is required. This cluster will be reported with the name of the zone unless you specify a cluster name. + +{{- else if (and (not .Values.zone.name) (not .Values.cluster.name)) }} +############################################################################## +#### ERROR: You did not specify a zone or name for this cluster. #### +############################################################################## + +This agent deployment will be incomplete until you set a zone for this cluster: + + helm upgrade {{ .Release.Name }} --reuse-values \ + --repo https://agents.instana.io/helm \ + --set zone.name=$(YOUR_ZONE_NAME) instana-agent + +Alternatively, you may specify a cluster name and the zone will be detected from availability zone information on the host: + + helm upgrade {{ .Release.Name }} --reuse-values \ + --repo https://agents.instana.io/helm \ + --set cluster.name=$(YOUR_CLUSTER_NAME) instana-agent + +- YOUR_ZONE_NAME should be the zone that detected technologies will be assigned to. +- YOUR_CLUSTER_NAME should be the custom name of your cluster. + +At least one of zone.name or cluster.name is required. This cluster will be reported with the name of the zone unless you specify a cluster name. + +{{- else if not (or .Values.agent.key .Values.agent.keysSecret )}} +############################################################################## +#### ERROR: You did not specify your secret agent key. #### +############################################################################## + +This agent deployment will be incomplete until you set your agent key: + + helm upgrade {{ .Release.Name }} --reuse-values \ + --repo https://agents.instana.io/helm \ + --set agent.key=$(YOUR_SECRET_AGENT_KEY) instana-agent + +- YOUR_SECRET_AGENT_KEY can be obtained from the Management Portal section of your Instana installation. + +{{- else -}} +Ensure to run `oc adm policy add-scc-to-user privileged -z instana-agent -n instana-agent` if running on OCP, otherwise agent pods will not be scheduled correctly. + +It may take a few moments for the agents to fully deploy. You can see what agents are running by listing resources in the {{ .Release.Namespace }} namespace: + + kubectl get all -n {{ .Release.Namespace }} + +You can get the logs for all of the agents with `kubectl logs`: + + kubectl logs -l app.kubernetes.io/name={{ .Release.Name }} -n {{ .Release.Namespace }} -c instana-agent + +{{- end }} diff --git a/charts/instana/instana-agent/2.0.13/templates/agent.yml b/charts/instana/instana-agent/2.0.13/templates/agent.yml new file mode 100644 index 0000000000..fd0412545d --- /dev/null +++ b/charts/instana/instana-agent/2.0.13/templates/agent.yml @@ -0,0 +1,309 @@ +--- +apiVersion: instana.io/v1 +kind: InstanaAgent +metadata: + name: instana-agent + namespace: instana-agent +spec: +{{- if .Values.zone }} + zone: + name: {{ .Values.zone.name }} +{{- end }} +{{- if .Values.zones }} + zones: +{{- toYaml $.Values.zones | nindent 4 }} +{{- end }} + cluster: + name: {{ .Values.cluster.name }} + agent: +{{- if .Values.agent.mode }} + mode: {{ .Values.agent.mode }} +{{- end }} +{{- if .Values.agent.key }} + key: {{ .Values.agent.key }} +{{- end }} +{{- if .Values.agent.downloadKey }} + downloadKey: {{ .Values.agent.downloadKey }} +{{- end }} +{{- if .Values.agent.keysSecret }} + keysSecret: {{ .Values.agent.keysSecret }} +{{- end }} +{{- if .Values.agent.listenAddress }} + listenAddress: {{ .Values.agent.listenAddress }} +{{- end }} + endpointHost: {{ .Values.agent.endpointHost }} + {{- if eq (typeOf .Values.agent.endpointPort) "string" }} + endpointPort: {{ .Values.agent.endpointPort }} + {{- else }} + endpointPort: {{ .Values.agent.endpointPort | quote }} + {{- end }} +{{- if .Values.agent.instanaMvnRepoUrl }} + instanaMvnRepoUrl: {{ .Values.agent.instanaMvnRepoUrl }} +{{- end }} +{{- if .Values.agent.instanaMvnRepoFeaturesPath }} + instanaMvnRepoFeaturesPath: {{ .Values.agent.instanaMvnRepoFeaturesPath }} +{{- end }} +{{- if .Values.agent.instanaMvnRepoSharedPath }} + instanaMvnRepoSharedPath: {{ .Values.agent.instanaMvnRepoSharedPath }} +{{- end }} +{{- if .Values.agent.agentReleaseRepoMirrorUrl }} + agentReleaseRepoMirrorUrl: {{ .Values.agent.agentReleaseRepoMirrorUrl }} +{{- end }} +{{- if .Values.agent.agentReleaseRepoMirrorUsername }} + agentReleaseRepoMirrorUsername: {{ .Values.agent.agentReleaseRepoMirrorUsername }} +{{- end }} +{{- if .Values.agent.agentReleaseRepoMirrorPassword }} + agentReleaseRepoMirrorPassword: {{ .Values.agent.agentReleaseRepoMirrorPassword }} +{{- end }} +{{- if .Values.agent.instanaSharedRepoMirrorUrl }} + instanaSharedRepoMirrorUrl: {{ .Values.agent.instanaSharedRepoMirrorUrl }} +{{- end }} +{{- if .Values.agent.instanaSharedRepoMirrorUsername }} + instanaSharedRepoMirrorUsername: {{ .Values.agent.instanaSharedRepoMirrorUsername }} +{{- end }} +{{- if .Values.agent.instanaSharedRepoMirrorPassword }} + instanaSharedRepoMirrorPassword: {{ .Values.agent.instanaSharedRepoMirrorPassword }} +{{- end }} +{{- if .Values.agent.additionalBackends }} + additionalBackends: +{{- range $.Values.agent.additionalBackends }} + - endpointHost: {{ .endpointHost }} + {{- if eq (typeOf .endpointPort) "string" }} + endpointPort: {{ .endpointPort }} + {{- else }} + endpointPort: {{ .endpointPort | quote }} + {{- end }} + {{- if .key }} + key: {{ .key }} + {{- end }} +{{- end }} +{{- end }} +{{- if .Values.agent.tls }} +{{- if or .Values.agent.tls.secretName (and .Values.agent.tls.certificate .Values.agent.tls.key) }} + tls: +{{- if .Values.agent.tls.secretName }} + secretName: {{ .Values.agent.tls.secretName }} +{{- end }} +{{- if .Values.agent.tls.certificate }} + certificate: {{ .Values.agent.tls.certificate }} +{{- end }} +{{- if .Values.agent.tls.key }} + key: {{ .Values.agent.tls.key }} +{{- end }} +{{- end }} +{{- end }} +{{- if .Values.agent.image }} +{{- if or .Values.agent.image.name .Values.agent.image.digest .Values.agent.image.tag .Values.agent.image.pullPolicy .Values.agent.image.pullSecrets }} + image: +{{- if .Values.agent.image.name }} + name: {{ .Values.agent.image.name }} +{{- end }} +{{- if .Values.agent.image.digest }} + digest: {{ .Values.agent.image.digest }} +{{- end }} +{{- if .Values.agent.image.tag }} + tag: {{ .Values.agent.image.tag }} +{{- end }} +{{- if .Values.agent.image.pullPolicy }} + pullPolicy: {{ .Values.agent.image.pullPolicy }} +{{- end }} +{{- if .Values.agent.image.pullSecrets }} + pullSecrets: +{{- toYaml $.Values.agent.image.pullSecrets | nindent 6 }} +{{- end }} +{{- end }} +{{- end }} +{{- if .Values.agent.minReadySeconds }} + minReadySeconds: {{ .Values.agent.minReadySeconds }} +{{- end }} +{{- if .Values.agent.updateStrategy }} + updateStrategy: +{{- if .Values.agent.updateStrategy.type }} + type: {{ .Values.agent.updateStrategy.type }} +{{- end }} +{{- if .Values.agent.updateStrategy.rollingUpdate }} +{{- if .Values.agent.updateStrategy.rollingUpdate.maxUnavailable }} + rollingUpdate: + maxUnavailable: {{ .Values.agent.updateStrategy.maxUnavailable }} +{{- end }} +{{- end }} +{{- end }} +{{- if .Values.agent.pod }} +{{- if or .Values.agent.pod.annotations .Values.agent.pod.labels .Values.agent.pod.tolerations .Values.agent.pod.affinity .Values.agent.pod.priorityClassName .Values.agent.pod.requests .Values.agent.pod.limits .Values.agent.pod.nodeSelector .Values.agent.pod.volumeMounts .Values.agent.pod.mounts }} + pod: + {{- if .Values.agent.pod.annotations }} + annotations: + {{- toYaml $.Values.agent.pod.annotations | nindent 8 }} + {{- end }} + {{- if .Values.agent.pod.labels }} + labels: + {{- toYaml $.Values.agent.pod.labels | nindent 8 }} + {{- end }} + {{- if .Values.agent.pod.tolerations }} + tolerations: + {{- toYaml $.Values.agent.pod.tolerations | nindent 8 }} + {{- end }} + {{- if .Values.agent.pod.affinity }} + affinity: + {{- toYaml $.Values.agent.pod.affinity | nindent 8 }} + {{- end }} + {{- if .Values.agent.pod.priorityClassName }} + priorityClassName: {{ .Values.agent.pod.priorityClassName }} + {{- end }} + {{- if .Values.agent.pod.requests }} + {{- if or .Values.agent.pod.requests.memory .Values.agent.pod.requests.cpu }} + requests: + {{- if .Values.agent.pod.requests.memory }} + memory: {{ .Values.agent.pod.requests.memory }} + {{- end }} + {{- if .Values.agent.pod.requests.cpu }} + cpu: {{ .Values.agent.pod.requests.cpu | quote }} + {{- end }} + {{- end }} + {{- end }} + {{- if .Values.agent.pod.limits }} + {{- if or .Values.agent.pod.limits.memory .Values.agent.pod.limits.cpu }} + limits: + {{- if .Values.agent.pod.limits.memory }} + memory: {{ .Values.agent.pod.limits.memory }} + {{- end }} + {{- if .Values.agent.pod.limits.cpu }} + cpu: {{ .Values.agent.pod.limits.cpu | quote }} + {{- end }} + {{- if .Values.agent.pod.nodeSelector }} + nodeSelector: + {{- toYaml $.Values.agent.pod.nodeSelector | nindent 8 }} + {{- end }} + {{- end }} + {{- end }} + {{- if .Values.agent.pod.volumeMounts }} + volumeMounts: + {{- toYaml $.Values.agent.pod.volumeMounts | nindent 8 }} + {{- end }} + {{- if .Values.agent.pod.volumes }} + volumes: + {{- toYaml $.Values.agent.pod.volumes | nindent 8 }} + {{- end }} +{{- end }} +{{- end }} +{{- if .Values.agent.proxyHost }} + proxyHost: {{ .Values.agent.proxyHost }} +{{- end }} +{{- if eq (typeOf .Values.agent.proxyPort) "string" }} + proxyPort: {{ .Values.agent.proxyPort }} +{{- else }} + proxyPort: {{ .Values.agent.proxyPort | quote }} +{{- end }} +{{- if .Values.agent.proxyProtocol }} + proxyProtocol: {{ .Values.agent.proxyProtocol }} +{{- end }} +{{- if .Values.agent.proxyUser }} + proxyUser: {{ .Values.agent.proxyUser }} +{{- end }} +{{- if .Values.agent.proxyPassword }} + proxyPassword: {{ .Values.agent.proxyPassword }} +{{- end }} +{{- if .Values.agent.proxyUseDNS }} + proxyUseDNS: {{ .Values.agent.proxyUseDNS }} +{{- end }} +{{- if .Values.agent.env }} + env: +{{- range $key, $value := .Values.agent.env }} + {{- if eq (typeOf $value) "string" }} + {{ $key }}: {{ $value }} + {{- else }} + {{ $key }}: {{ $value | quote }} + {{- end }} +{{- end }} +{{- end }} +{{- if .Values.agent.configuration_yaml }} +{{ $configuration_yaml_string := .Values.agent.configuration_yaml }} + configuration_yaml: |- +{{ $configuration_yaml_string | indent 6}} +{{- end }} +{{- if and .Values.agent.host .Values.agent.host.repository }} + host: + repository: {{ .Values.agent.host.repository }} +{{- end }} +{{- if .Values.agent.serviceMesh}} +{{- if .Values.agent.serviceMesh.enabled }} + serviceMesh: + enabled: {{ .Values.agent.serviceMesh.enabled }} +{{- end }} +{{- end }} +{{- if .Values.opentelemetry }} +{{- if or ( and (hasKey .Values.opentelemetry "grpc") (hasKey .Values.opentelemetry.grpc "enabled")) ( and (hasKey .Values.opentelemetry "http") (hasKey .Values.opentelemetry.http "enabled")) }} + opentelemetry: +{{- if and (hasKey .Values.opentelemetry "grpc") (hasKey .Values.opentelemetry.grpc "enabled") }} + grpc: + enabled: {{ .Values.opentelemetry.grpc.enabled }} +{{- end }} +{{- if and (hasKey .Values.opentelemetry "http") (hasKey .Values.opentelemetry.http "enabled") }} + http: + enabled: {{ .Values.opentelemetry.http.enabled }} +{{- end }} +{{- end }} +{{- end }} +{{- if .Values.prometheus }} +{{- if .Values.prometheus.remoteWrite }} +{{- if .Values.prometheus.remoteWrite.enabled }} + prometheus: + remoteWrite: + enabled: {{ .Values.prometheus.remoteWrite.enabled }} +{{- end }} +{{- end }} +{{- end }} +{{- if .Values.serviceAccount }} +{{- if or .Values.serviceAccount.create .Values.serviceAccount.annotations }} + serviceAccount: +{{- if .Values.serviceAccount.create }} + create: {{ .Values.serviceAccount.create }} +{{- end }} +{{- if .Values.serviceAccount.name }} + name: {{ .Values.serviceAccount.name }} +{{- end }} +{{- if .Values.serviceAccount.annotations }} + annotations: {{ .Values.serviceAccount.annotations }} + {{- toYaml $.Values.serviceAccount.annotations | nindent 6 }} +{{- end }} +{{- end }} +{{- end }} +{{- if .Values.podSecurityPolicy }} +{{- if or .Values.podSecurityPolicy.enable .Values.podSecurityPolicy.name }} + podSecurityPolicy: +{{- if .Values.podSecurityPolicy.enable }} + enable: {{ .Values.podSecurityPolicy.enable }} +{{- end }} +{{- if .Values.podSecurityPolicy.name }} + name: {{ .Values.podSecurityPolicy.name }} +{{- end }} +{{- end }} +{{- end }} +{{- if .Values.k8s_sensor }} +{{- if or .Values.k8s_sensor.image .Values.k8s_sensor.deployment .Values.k8s_sensor.podDisruptionBudget }} + k8s_sensor: +{{- if .Values.k8s_sensor.image }} + image: +{{- if .Values.k8s_sensor.image.name }} + name: {{ .Values.k8s_sensor.image.name }} +{{- end }} +{{- if .Values.k8s_sensor.image.digest }} + digest: {{ .Values.k8s_sensor.image.digest }} +{{- end }} +{{- if .Values.k8s_sensor.image.tag }} + tag: {{ .Values.k8s_sensor.image.tag }} +{{- end }} +{{- if .Values.k8s_sensor.image.pullPolicy }} + pullPolicy: {{ .Values.k8s_sensor.image.pullPolicy }} +{{- end }} +{{- end }} +{{- if .Values.k8s_sensor.deployment }} + deployment: + {{- toYaml $.Values.k8s_sensor.deployment | nindent 6 }} +{{- end }} +{{- if .Values.k8s_sensor.podDisruptionBudget }} + podDisruptionBudget: + {{- toYaml $.Values.k8s_sensor.podDisruptionBudget | nindent 6 }} +{{- end }} +{{- end }} +{{- end }} diff --git a/charts/instana/instana-agent/2.0.13/templates/operator_clusterrole_instana-agent-clusterrole.yml b/charts/instana/instana-agent/2.0.13/templates/operator_clusterrole_instana-agent-clusterrole.yml new file mode 100644 index 0000000000..a12081b7d9 --- /dev/null +++ b/charts/instana/instana-agent/2.0.13/templates/operator_clusterrole_instana-agent-clusterrole.yml @@ -0,0 +1,187 @@ +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: instana-agent-clusterrole +rules: + - nonResourceURLs: + - /healthz + - /metrics + - /metrics/cadvisor + - /stats/summary + - /version + verbs: + - get + - apiGroups: + - apiextensions.k8s.io + resources: + - customresourcedefinitions + verbs: + - get + - list + - watch + - apiGroups: + - apps + resources: + - daemonsets + - deployments + verbs: + - create + - delete + - get + - list + - patch + - update + - watch + - apiGroups: + - apps + resources: + - daemonsets + - deployments + - replicasets + - statefulsets + verbs: + - get + - list + - watch + - apiGroups: + - apps.openshift.io + resources: + - deploymentconfigs + verbs: + - get + - list + - watch + - apiGroups: + - autoscaling + resources: + - horizontalpodautoscalers + verbs: + - get + - list + - watch + - apiGroups: + - batch + resources: + - cronjobs + - jobs + verbs: + - get + - list + - watch + - apiGroups: + - "" + resources: + - configmaps + - endpoints + - events + - namespaces + - nodes + - nodes/metrics + - nodes/stats + - persistentvolumeclaims + - persistentvolumes + - pods + - pods/log + - replicationcontrollers + - resourcequotas + - services + verbs: + - get + - list + - watch + - apiGroups: + - "" + resources: + - configmaps + - secrets + - serviceaccounts + - services + verbs: + - create + - delete + - get + - list + - patch + - update + - watch + - apiGroups: + - extensions + resources: + - deployments + - ingresses + - replicasets + verbs: + - get + - list + - watch + - apiGroups: + - instana.io + resources: + - agents + verbs: + - create + - delete + - get + - list + - patch + - update + - watch + - apiGroups: + - instana.io + resources: + - agents/finalizers + verbs: + - update + - apiGroups: + - instana.io + resources: + - agents/status + verbs: + - get + - patch + - update + - apiGroups: + - networking.k8s.io + resources: + - ingresses + verbs: + - get + - list + - watch + - apiGroups: + - policy + resources: + - podsecuritypolicies + verbs: + - use + - apiGroups: + - policy + resourceNames: + - instana-agent-k8sensor + resources: + - podsecuritypolicies + verbs: + - use + - apiGroups: + - rbac.authorization.k8s.io + resources: + - clusterrolebindings + - clusterroles + verbs: + - bind + - create + - delete + - get + - list + - patch + - update + - watch + - apiGroups: + - security.openshift.io + resourceNames: + - privileged + resources: + - securitycontextconstraints + verbs: + - use diff --git a/charts/instana/instana-agent/2.0.13/templates/operator_clusterrole_leader-election-role.yml b/charts/instana/instana-agent/2.0.13/templates/operator_clusterrole_leader-election-role.yml new file mode 100644 index 0000000000..2a79075c0c --- /dev/null +++ b/charts/instana/instana-agent/2.0.13/templates/operator_clusterrole_leader-election-role.yml @@ -0,0 +1,27 @@ +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: leader-election-role +rules: + - apiGroups: + - "" + - coordination.k8s.io + resources: + - configmaps + - leases + verbs: + - get + - list + - watch + - create + - update + - patch + - delete + - apiGroups: + - "" + resources: + - events + verbs: + - create + - patch diff --git a/charts/instana/instana-agent/2.0.13/templates/operator_clusterrolebinding_instana-agent-clusterrolebinding.yml b/charts/instana/instana-agent/2.0.13/templates/operator_clusterrolebinding_instana-agent-clusterrolebinding.yml new file mode 100644 index 0000000000..7791c4df44 --- /dev/null +++ b/charts/instana/instana-agent/2.0.13/templates/operator_clusterrolebinding_instana-agent-clusterrolebinding.yml @@ -0,0 +1,13 @@ +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: instana-agent-clusterrolebinding +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: instana-agent-clusterrole +subjects: + - kind: ServiceAccount + name: instana-agent-operator + namespace: instana-agent diff --git a/charts/instana/instana-agent/2.0.13/templates/operator_clusterrolebinding_leader-election-rolebinding.yml b/charts/instana/instana-agent/2.0.13/templates/operator_clusterrolebinding_leader-election-rolebinding.yml new file mode 100644 index 0000000000..904203ce6f --- /dev/null +++ b/charts/instana/instana-agent/2.0.13/templates/operator_clusterrolebinding_leader-election-rolebinding.yml @@ -0,0 +1,13 @@ +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: leader-election-rolebinding +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: leader-election-role +subjects: + - kind: ServiceAccount + name: instana-agent-operator + namespace: instana-agent diff --git a/charts/instana/instana-agent/2.0.13/templates/operator_configmap_manager-config.yml b/charts/instana/instana-agent/2.0.13/templates/operator_configmap_manager-config.yml new file mode 100644 index 0000000000..70ceeb82dc --- /dev/null +++ b/charts/instana/instana-agent/2.0.13/templates/operator_configmap_manager-config.yml @@ -0,0 +1,17 @@ +--- +apiVersion: v1 +data: + controller_manager_config.yaml: | + apiVersion: controller-runtime.sigs.k8s.io/v1alpha1 + kind: ControllerManagerConfig + health: + healthProbeBindAddress: :8081 + metrics: + bindAddress: 127.0.0.1:8080 + leaderElection: + leaderElect: true + resourceName: 819a9291.instana.io +kind: ConfigMap +metadata: + name: manager-config + namespace: instana-agent diff --git a/charts/instana/instana-agent/2.0.13/templates/operator_deployment_instana-agent-controller-manager.yml b/charts/instana/instana-agent/2.0.13/templates/operator_deployment_instana-agent-controller-manager.yml new file mode 100644 index 0000000000..b173068706 --- /dev/null +++ b/charts/instana/instana-agent/2.0.13/templates/operator_deployment_instana-agent-controller-manager.yml @@ -0,0 +1,66 @@ +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + labels: + app.kubernetes.io/name: instana-agent-operator + name: instana-agent-controller-manager + namespace: instana-agent +spec: + replicas: 1 + selector: + matchLabels: + app.kubernetes.io/name: instana-agent-operator + template: + metadata: + labels: + app.kubernetes.io/name: instana-agent-operator + spec: + affinity: + nodeAffinity: + requiredDuringSchedulingIgnoredDuringExecution: + nodeSelectorTerms: + - matchExpressions: + - key: kubernetes.io/os + operator: In + values: + - linux + - matchExpressions: + - key: kubernetes.io/arch + operator: In + values: + - amd64 + - ppc64le + - s390x + - arm64 + containers: + - args: + - --leader-elect + command: + - /manager + image: icr.io/instana/instana-agent-operator:2.1.19 + imagePullPolicy: Always + livenessProbe: + httpGet: + path: /healthz + port: 8081 + initialDelaySeconds: 15 + periodSeconds: 20 + name: manager + readinessProbe: + httpGet: + path: /readyz + port: 8081 + initialDelaySeconds: 5 + periodSeconds: 10 + resources: + limits: + cpu: 200m + memory: 600Mi + requests: + cpu: 200m + memory: 200Mi + securityContext: + allowPrivilegeEscalation: true + serviceAccountName: instana-agent-operator + terminationGracePeriodSeconds: 10 diff --git a/charts/instana/instana-agent/2.0.13/templates/operator_serviceaccount_instana-agent-operator.yml b/charts/instana/instana-agent/2.0.13/templates/operator_serviceaccount_instana-agent-operator.yml new file mode 100644 index 0000000000..c23ec50a4a --- /dev/null +++ b/charts/instana/instana-agent/2.0.13/templates/operator_serviceaccount_instana-agent-operator.yml @@ -0,0 +1,6 @@ +--- +apiVersion: v1 +kind: ServiceAccount +metadata: + name: instana-agent-operator + namespace: instana-agent diff --git a/charts/instana/instana-agent/2.0.13/values.yaml b/charts/instana/instana-agent/2.0.13/values.yaml new file mode 100644 index 0000000000..a2bc39963e --- /dev/null +++ b/charts/instana/instana-agent/2.0.13/values.yaml @@ -0,0 +1,282 @@ +# name is the value which will be used as the base resource name for various resources associated with the agent. +# name: instana-agent + +agent: + # agent.mode is used to set agent mode and it can be APM, INFRASTRUCTURE or AWS + # mode: APM + + # agent.key is the secret token which your agent uses to authenticate to Instana's servers. + key: null + # agent.downloadKey is key, sometimes known as "sales key", that allows you to download, + # software from Instana. + # downloadKey: null + + # Rather than specifying the agent key and optionally the download key, you can "bring your + # own secret" creating it in the namespace in which you install the `instana-agent` and + # specify its name in the `keysSecret` field. The secret you create must contains + # a field called `key` and optionally one called `downloadKey`, which contain, respectively, + # the values you'd otherwise set in `.agent.key` and `agent.downloadKey`. + # keysSecret: null + + # agent.listenAddress is the IP address the agent HTTP server will listen to. + # listenAddress: "*" + + # agent.endpointHost is the hostname of the Instana server your agents will connect to. + # endpointHost: ingress-red-saas.instana.io + # agent.endpointPort is the port number (as a String) of the Instana server your agents will connect to. + # endpointPort: 443 + + # These are additional backends the Instana agent will report to besides + # the one configured via the `agent.endpointHost`, `agent.endpointPort` and `agent.key` setting + # additionalBackends: [] + # - endpointHost: ingress.instana.io + # endpointPort: 443 + # key: + + # TLS for end-to-end encryption between Instana agent and clients accessing the agent. + # The Instana agent does not yet allow enforcing TLS encryption. + # TLS is only enabled on a connection when requested by the client. + # tls: + # In order to enable TLS, a secret of type kubernetes.io/tls must be specified. + # secretName is the name of the secret that has the relevant files. + # secretName: null + # Otherwise, the certificate and the private key must be provided as base64 encoded. + # certificate: null + # key: null + + # image: + # agent.image.name is the name of the container image of the Instana agent. + # name: icr.io/instana/agent + # agent.image.digest is the digest (a.k.a. Image ID) of the agent container image; if specified, it has priority over agent.image.tag, which will be ignored. + # digest: + # agent.image.tag is the tag name of the agent container image; if agent.image.digest is specified, this property is ignored. + # tag: latest + # agent.image.pullPolicy specifies when to pull the image container. + # pullPolicy: Always + # agent.image.pullSecrets allows you to override the default pull secret that is created when agent.image.name starts with "containers.instana.io" + # Setting agent.image.pullSecrets prevents the creation of the default "containers-instana-io" secret. + # pullSecrets: + # - name: my_awesome_secret_instead + # If you want no imagePullSecrets to be specified in the agent pod, you can just pass an empty array to agent.image.pullSecrets + # pullSecrets: [] + + # The minimum number of seconds for which a newly created Pod should be ready without any of its containers crashing, for it to be considered available + # minReadySeconds: 0 + + # updateStrategy: + # type: RollingUpdate + # rollingUpdate: + # maxUnavailable: 1 + + # pod: + # agent.pod.annotations are additional annotations to be added to the agent pods. + # annotations: {} + + # agent.pod.labels are additional labels to be added to the agent pods. + # labels: {} + + # agent.pod.tolerations are tolerations to influence agent pod assignment. + # https://kubernetes.io/docs/concepts/configuration/taint-and-toleration/ + # tolerations: [] + + # agent.pod.affinity are affinities to influence agent pod assignment. + # https://kubernetes.io/docs/concepts/configuration/taint-and-toleration/ + # affinity: {} + + # agent.pod.priorityClassName is the name of an existing PriorityClass that should be set on the agent pods + # https://kubernetes.io/docs/concepts/configuration/pod-priority-preemption/ + # priorityClassName: null + + # agent.pod.nodeSelector are selectors to influence where agent pods should be scheduled. + # nodeSelector: + # location: 'us-central1-c' + #nodeSelector: null + + # agent.pod.requests and agent.pod.limits adjusts the resource assignments for the DaemonSet agent + # regardless of the kubernetes.deployment.enabled setting + # requests: + # agent.pod.requests.memory is the requested memory allocation in MiB for the agent pods. + # memory: 768Mi + # agent.pod.requests.cpu are the requested CPU units allocation for the agent pods. + # cpu: 0.5 + # limits: + # agent.pod.limits.memory set the memory allocation limits in MiB for the agent pods. + # memory: 768Mi + # agent.pod.limits.cpu sets the CPU units allocation limits for the agent pods. + # cpu: 1.5 + + # agent.pod.volumes and agent.pod.volumeMounts are additional volumes and volumeMounts for user-specific files. + # For example, a certificate may need to be mounted for an agent sensor to connect to the monitored target. + # https://kubernetes.io/docs/concepts/storage/volumes/ + # volumes: + # - name: my-secret-volume + # secret: + # secretName: instana-agent-key + # volumeMounts: + # - name: my-secret-volume + # mountPath: /secrets + + # agent.proxyHost sets the INSTANA_AGENT_PROXY_HOST environment variable. + # proxyHost: null + # agent.proxyPort sets the INSTANA_AGENT_PROXY_PORT environment variable. + # proxyPort: 80 + # agent.proxyProtocol sets the INSTANA_AGENT_PROXY_PROTOCOL environment variable. + # proxyProtocol: HTTP + # agent.proxyUser sets the INSTANA_AGENT_PROXY_USER environment variable. + # proxyUser: null + # agent.proxyPassword sets the INSTANA_AGENT_PROXY_PASSWORD environment variable. + # proxyPassword: null + # agent.proxyUseDNS sets the INSTANA_AGENT_PROXY_USE_DNS environment variable. + # proxyUseDNS: false + + # use this to set additional environment variables for the instana agent + # for example: + # env: + # INSTANA_AGENT_TAGS: dev + # env: {} + + configuration_yaml: | + # Manual a-priori configuration. Configuration will be only used when the sensor + # is actually installed by the agent. + # The commented out example values represent example configuration and are not + # necessarily defaults. Defaults are usually 'absent' or mentioned separately. + # Changes are hot reloaded unless otherwise mentioned. + + # It is possible to create files called 'configuration-abc.yaml' which are + # merged with this file in file system order. So 'configuration-cde.yaml' comes + # after 'configuration-abc.yaml'. Only nested structures are merged, values are + # overwritten by subsequent configurations. + + # Secrets + # To filter sensitive data from collection by the agent, all sensors respect + # the following secrets configuration. If a key collected by a sensor matches + # an entry from the list, the value is redacted. + #com.instana.secrets: + # matcher: 'contains-ignore-case' # 'contains-ignore-case', 'contains', 'regex' + # list: + # - 'key' + # - 'password' + # - 'secret' + + # Host + #com.instana.plugin.host: + # tags: + # - 'dev' + # - 'app1' + + # agent.redactKubernetesSecrets sets the INSTANA_KUBERNETES_REDACT_SECRETS environment variable. + # redactKubernetesSecrets: null + + # agent.host.repository sets a host path to be mounted as the agent maven repository (for debugging or development purposes) + host: + repository: null + + # agent.serviceMesh.enabled sets the ENABLE_AGENT_SOCKET environment variable. + serviceMesh: + # enabled: true + +cluster: + # cluster.name represents the name that will be assigned to this cluster in Instana + name: null + +# openshift specifies whether the cluster role should include openshift permissions and other tweaks to the YAML. +# The chart will try to auto-detect if the cluster is OpenShift, so you will likely not even need to set this explicitly. +# openshift: true + +# rbac: + # Specifies whether RBAC resources should be created + # create: true + +# opentelemetry: + # enabled: false # legacy setting, will only enable grpc, defaults to false + # grpc: + # enabled: true # takes precedence over legacy settings above, defaults to true if "grpc:" is present + # http: + # enabled: true # allows to enable http endpoints, defaults to true if "http:" is present + +# prometheus: +# remoteWrite: +# enabled: false # If true, it will also apply `service.create=true` + +# serviceAccount: + # Specifies whether a ServiceAccount should be created + # create: true + # The name of the ServiceAccount to use. + # If not set and `create` is true, a name is generated using the fullname template + # name: instana-agent + # Annotations to add to the service account + # annotations: {} + +# podSecurityPolicy: + # Specifies whether a PodSecurityPolicy should be authorized for the Instana Agent pods. + # Requires `rbac.create` to be `true` as well and K8s version below v1.25. + # enable: false + # The name of an existing PodSecurityPolicy you would like to authorize for the Instana Agent pods. + # If not set and `enable` is true, a PodSecurityPolicy will be created with a name generated using the fullname template. + # name: null + +zone: + # zone.name is the custom zone that detected technologies will be assigned to + name: null + +# k8s_sensor: +# image: + # k8s_sensor.image.name is the name of the container image of the Instana agent. + # name: icr.io/instana/k8sensor + # k8s_sensor.image.digest is the digest (a.k.a. Image ID) of the agent container image; if specified, it has priority over agent.image.tag, which will be ignored. + #digest: + # k8s_sensor.image.tag is the tag name of the agent container image; if agent.image.digest is specified, this property is ignored. + # tag: latest + # k8s_sensor.image.pullPolicy specifies when to pull the image container. + # pullPolicy: Always + # deployment: + # Specifies whether or not to enable the Deployment and turn off the Kubernetes sensor in the DaemonSet + # enabled: true + # Use three replicas to ensure the HA by the default. + # replicas: 3 + # k8s_sensor.deployment.pod adjusts the resource assignments for the agent independently of the DaemonSet agent when k8s_sensor.deployment.enabled=true + # pod: + # requests: + # k8s_sensor.deployment.pod.requests.memory is the requested memory allocation in MiB for the agent pods. + # memory: 128Mi + # k8s_sensor.deployment.pod.requests.cpu are the requested CPU units allocation for the agent pods. + # cpu: 120m + # limits: + # k8s_sensor.deployment.pod.limits.memory set the memory allocation limits in MiB for the agent pods. + # memory: 2048Mi + # k8s_sensor.deployment.pod.limits.cpu sets the CPU units allocation limits for the agent pods. + # cpu: 500m + # affinity: + # podAntiAffinity: + # Soft anti-affinity policy: try not to schedule multiple kubernetes-sensor pods on the same node. + # If the policy is set to "requiredDuringSchedulingIgnoredDuringExecution", if the cluster has + # fewer nodes than the amount of desired replicas, `helm install/upgrade --wait` will not return. + # preferredDuringSchedulingIgnoredDuringExecution: + # - weight: 100 + # podAffinityTerm: + # labelSelector: + # matchExpressions: + # - key: instana/agent-mode + # operator: In + # values: [ KUBERNETES ] + # topologyKey: "kubernetes.io/hostname" + # The minimum number of seconds for which a newly created Pod should be ready without any of its containers crashing, for it to be considered available + # minReadySeconds: 0 + # podDisruptionBudget: + # Specifies whether or not to setup a pod disruption budget for the k8sensor deployment + # enabled: false + +# zones: +# # Configure use of zones to use tolerations as the basis to associate a specific daemonset per tainted node pool +# - name: pool-01 +# tolerations: +# - key: "pool" +# operator: "Equal" +# value: "pool-01" +# effect: "NoExecute" +# - name: pool-02 +# tolerations: +# - key: "pool" +# operator: "Equal" +# value: "pool-02" +# effect: "NoExecute" \ No newline at end of file diff --git a/charts/jfrog/artifactory-ha/107.104.10/.helmignore b/charts/jfrog/artifactory-ha/107.104.10/.helmignore new file mode 100644 index 0000000000..b6e97f07fb --- /dev/null +++ b/charts/jfrog/artifactory-ha/107.104.10/.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 +OWNERS + +tests/ \ No newline at end of file diff --git a/charts/jfrog/artifactory-ha/107.104.10/CHANGELOG.md b/charts/jfrog/artifactory-ha/107.104.10/CHANGELOG.md new file mode 100644 index 0000000000..2f477c5f4e --- /dev/null +++ b/charts/jfrog/artifactory-ha/107.104.10/CHANGELOG.md @@ -0,0 +1,1515 @@ +# JFrog Artifactory-ha Chart Changelog +All changes to this chart will be documented in this file + +## [107.104.10] - Feb 07, 2025 +* Added new RTFS service +* Added new Topology service +* Added new onemodel service +* Added customVolumeMounts support for frontend,event,evidence,jfconnect,topology,observability containers +* Added ingress and nginx routing support for rtfs service context +* Added recommended sizing extraEnvironmentVariables for access container +* Added default extra javaOpts support in system yaml for topology +* Modified the RTFS chart and the topology probe values +* Fixing secret based annotations for RTFS deployment +* Fixed `shared` block in system.yaml to include all properties +* Fixed RTFS jfrogUrl issue for platform chart +* Fixed disabling onemodel using `onemodel.enabled=false` +* Removed unwanted database support from rtfs +* Added hpa support for RTFS service + +## [107.102.0] - Nov 26, 2024 +* Remove the Xms and Xmx with InitialRAMPercentage and MaxRAMPercentage if they are available in extra_java_options + +## [107.98.0] - Nov 06, 2024 +* Add support for `extraEnvironmentVariables` on filebeat Sidecar [GH-1377](https://github.com/jfrog/charts/pull/1377) +* Support for SSL offload HTTPS proto override in Nginx service (ClusterIP, LoadBalancer) layer. Introduced `nginx.service.ssloffloadForceHttps` field with boolean type. [GH-1906](https://github.com/jfrog/charts/pull/1906) +* Enable Access workers integration when artifactory.worker.enabled is true +* Added `signedUrlExpirySeconds` option to artifactory.persistence.type of `google-storage`, `google-storage-v2`, and `google-storage-v2-direct` [GH-1858](https://github.com/jfrog/charts/pull/1858) +* Added support to bootstrap jfconnect custom certs to jfconnect trusted directory +* Fixed the type of `.Values.artifactory.persistence.googleStorage.signedUrlExpirySeconds` in binarystore.xml from boolean to integer +* Added support for modifying `pathType` in ingress +* Added fix for database credentials secret in non-unified secret installations + +## [107.96.0] - Sep 18, 2024 +* Merged Artifactory sizing templates to a single file per size +* Added metadata and observability standalone image support + +## [107.95.0] - Aug 26, 2024 +* Adding missing annotations to primary Artifactory Service [GH-1862](https://github.com/jfrog/charts/pull/1862) + +## [107.94.0] - Aug 14, 2024 +* Fixed #Expose rtfs port only when it is enabled + +## [107.93.0] - Aug 9, 2024 +* Added support for worker via `artifactory.worker.enabled` flag +* Fixed creation of duplicate objects in statefulSet + +## [107.92.0] - July 31, 2024 +* Updating the example link for downloading the DB driver +* Adding dedicated ingress and service path for GRPC protocol + +## [107.91.0] - July 18, 2024 +* Remove X-JFrog-Override-Base-Url port when using default `443/80` ports + +## [107.90.0] - July 18, 2024 +* Fixed #adding colon in image registry which breaks deployment [GH-1892](https://github.com/jfrog/charts/pull/1892) +* Added new `nginx.hosts` to use Nginx server_name directive instead of `ingress.hosts` +* Added a deprecation notice of ingress.hosts when `ngnix.enabled` is true +* Added new evidence service +* Corrected database connection values based on sizing +* **IMPORTANT** +* Separate access from artifactory tomcat to run on its own dedicated tomcat + * With this change access will be running in its own dedicated container + * This will give the ability to control resources and java options specific to access + Can be done by passing the following, + `access.javaOpts.other` + `access.resources` + `access.extraEnvironmentVariables` +* Added Binary Provider recommendations + +## [107.89.0] - May 30, 2024 +* Fix the indentation of the commented-out sections in the values.yaml file + +## [107.88.0] - May 29, 2024 +* **IMPORTANT** +* Refactored `nginx.artifactoryConf` and `nginx.mainConf` configuration (moved to files/nginx-artifactory-conf.yaml and files/nginx-main-conf.yaml instead of keys in values.yaml) + +## [107.87.0] - May 29, 2024 +* Renamed `.Values.artifactory.openMetrics` to `.Values.artifactory.metrics` +* Align all liveness and readiness probes (Removed hard-coded values) + +## [107.85.0] - May 29, 2024 +* Changed `migration.enabled` to false by default. For 6.x to 7.x migration, this flag needs to be set to `true` + +## [107.84.0] - May 29, 2024 +* Added image section for `initContainers` instead of `initContainerImage` +* Renamed `router.image.imagePullPolicy` to `router.image.pullPolicy` +* Removed loggers.image section +* Added support for `global.verisons.initContainers` to override `initContainers.image.tag` +* Fixed an issue with extraSystemYaml merge +* **IMPORTANT** +* Renamed `artifactory.setSecurityContext` to `artifactory.podSecurityContext` +* Renamed `artifactory.uid` to `artifactory.podSecurityContext.runAsUser` +* Renamed `artifactory.gid` to `artifactory.podSecurityContext.runAsGroup` and `artifactory.podSecurityContext.fsGroup` +* Renamed `artifactory.fsGroupChangePolicy` to `artifactory.podSecurityContext.fsGroupChangePolicy` +* Renamed `artifactory.seLinuxOptions` to `artifactory.podSecurityContext.seLinuxOptions` +* Added flag `allowNonPostgresql` defaults to false +* Update postgresql tag version to `15.6.0-debian-12-r5` +* Added a check if `initContainerImage` exists +* Fixed a wrong imagePullPolicy configuration +* Fixed an issue to generate unified secret to support artifactory fullname [GH-1882](https://github.com/jfrog/charts/issues/1882) +* Fixed an issue template render on loggers [GH-1883](https://github.com/jfrog/charts/issues/1883) +* Override metadata and observability image tag with `global.verisons.artifactory` value +* Fixed resource constraints for "setup" initContainer of nginx deployment [GH-962] (https://github.com/jfrog/charts/issues/962) +* Added .Values.artifactory.unifiedSecretsPrependReleaseName` for unified secret to prepend release name +* Fixed maxCacheSize and cacheProviderDir mix up under azure-blob-storage-v2-direct template in binarystore.xml + +## [107.83.0] - Mar 12, 2024 +* Added image section for `metadata` and `observability` + +## [107.82.0] - Mar 04, 2024 +* Added `disableRouterBypass` flag as experimental feature, to disable the artifactoryPath /artifactory/ and route all traffic through the Router. +* Removed Replicator Service + +## [107.81.0] - Feb 20, 2024 +* **IMPORTANT** +* Refactored systemYaml configuration (moved to files/system.yaml instead of key in values.yaml) +* Added ability to provide `extraSystemYaml` configuration in values.yaml which will merge with the existing system yaml when `systemYamlOverride` is not given [GH-1848](https://github.com/jfrog/charts/pull/1848) +* Added option to modify the new cache configs, maxFileSizeLimit and skipDuringUpload +* Added IPV4/IPV6 Dualstack flag support for Artifactory and nginx service +* Added `singleStackIPv6Cluster` flag, which manages the Nginx configuration to enable listening on IPv6 and proxying +* Fixing broken link for creating additional kubernetes resources. Refer [here](https://github.com/jfrog/log-analytics-prometheus/blob/master/helm/artifactory-ha-values.yaml) +* Refactored installerInfo configuration (moved to files/installer-info.json instead of key in values.yaml) + +## [107.80.0] - Feb 20, 2024 +* Updated README.md to create a namespace using `--create-namespace` as part of helm install + +## [107.79.0] - Feb 20, 2024 +* **IMPORTANT** +* Added `unifiedSecretInstallation` flag which enables single unified secret holding all internal (chart) secrets to `true` by default +* Added support for azure-blob-storage-v2-direct config +* Added option to set Nginx to write access_log to container STDOUT +* **Important change:** +* Update postgresql tag version to `15.2.0-debian-11-r23` +* If this is a new deployment or you already use an external database (`postgresql.enabled=false`), these changes **do not affect you**! +* If this is an upgrade and you are using the default bundles PostgreSQL (`postgresql.enabled=true`), you need to pass previous 9.x/10.x/12.x/13.x's postgresql.image.tag, previous postgresql.persistence.size and databaseUpgradeReady=true + +## [107.77.0] - April 22, 2024 +* Removed integration service +* Added recommended postgresql sizing configurations under sizing directory +* Updated artifactory-federation (probes, port, embedded mode) +* **IMPORTANT** +* setSecurityContext has been renamed to podSecurityContext. +* Moved podSecurityContext to values.yaml +* Fixing broken nginx port [GH-1860](https://github.com/jfrog/charts/issues/1860) +* Added nginx.customCommand to use custom commands for the nginx container + +## [107.76.0] - Dec 13, 2023 +* Added connectionTimeout and socketTimeout paramaters under AWSS3 binarystore section +* Reduced nginx startupProbe initialDelaySeconds + +## [107.74.0] - Nov 30, 2023 +* Added recommended sizing configurations under sizing directory, please refer [here](README.md/#apply-sizing-configurations-to-the-chart) +* **IMPORTANT** +* Added min kubeVersion ">= 1.19.0-0" in chart.yaml + +## [107.70.0] - Nov 30, 2023 +* Fixed - StatefulSet pod annotations changed from range to toYaml [GH-1828](https://github.com/jfrog/charts/issues/1828) +* Fixed - Invalid format for awsS3V3 `multiPartLimit,multipartElementSize` in binarystore.xml +* Fixed - Artifactory primary service condition +* Fixed - SecurityContext with runAsGroup in artifactory-ha [GH-1838](https://github.com/jfrog/charts/issues/1838) +* Added support for custom labels in the Nginx pods [GH-1836](https://github.com/jfrog/charts/pull/1836) +* Added podSecurityContext and containerSecurityContext for nginx +* Added support for nginx on openshift, set `podSecurityContext` and `containerSecurityContext` to false +* Renamed nginx internalPort 80,443 to 8080,8443 to support openshift + +## [107.69.0] - Sep 18, 2023 +* Adjust rtfs context +* Fixed - Metadata service does not respect customVolumeMounts for DB CAs [GH-1815](https://github.com/jfrog/charts/issues/1815) + +## [107.68.8] - Sep 18, 2023 +* Reverted - Enabled `unifiedSecretInstallation` by default [GH-1819](https://github.com/jfrog/charts/issues/1819) +* Removed unused `artifactory.javaOpts` from values.yaml +* Removed openshift condition check from NOTES.txt +* Fixed an issue with artifactory node replicaCount [GH-1808](https://github.com/jfrog/charts/issues/1808) + +## [107.68.7] - Aug 28, 2023 +* Enabled `unifiedSecretInstallation` by default +* Removed unused `artifactory.javaOpts` from values.yaml + +## [107.67.0] - Aug 28, 2023 +* Add 'extraJavaOpts' and 'port' values to federation service + +## [107.66.0] - Aug 28, 2023 +* Added federation service container in artifactory +* Add rtfs service to ingress in artifactory + +## [107.64.0] - Aug 28,2023 +* Added support to configure event.webhooks within generated system.yaml +* Fixed an issue to generate ssl certificate should support artifactory-ha fullname +* Added 'multiPartLimit' and 'multipartElementSize' parameters to awsS3V3 binary providers. +* Increased default Artifactory Tomcat acceptCount config to 400 +* Fixed Illegal Strict-Transport-Security header in nginx config + +## [107.63.0] - Aug 28, 2023 +* Added support for Openshift by adding the securityContext in container level. +* **IMPORTANT** +* Disable securityContext in container and pod level to deploy postgres on openshift. +* Fixed support for fsGroup in non openshift environment and runAsGroup in openshift environment. +* Fixed - Helm Template Error when using artifactory.loggers [GH-1791](https://github.com/jfrog/charts/issues/1791) +* Removed the nginx disable condition for openshift +* Fixed jfconnect disabling as micro-service on splitcontainers [GH-1806](https://github.com/jfrog/charts/issues/1806) + +## [107.62.0] - Jun 5, 2023 +* Added support for 'port' and 'useHttp' parameters for s3-storage-v3 binary provider [GH-1767](https://github.com/jfrog/charts/issues/1767) + +## [107.61.0] - May 31, 2023 +* Added new binary provider `google-storage-v2-direct` + +## [107.60.0] - May 31, 2023 +* Enabled `splitServicesToContainers` to true by default +* Updated the recommended values for small, medium and large installations to support the 'splitServicesToContainers' + +## [107.59.0] - May 31, 2023 +* Fixed reference of `terminationGracePeriodSeconds` +* **Breaking change** +* Updated the defaults of replicaCount (Values.artifactory.primary.replicaCount and Values.artifactory.node.replicaCount) to support Cloud-Native High Availability. Refer [Cloud-Native High Availability](https://jfrog.com/help/r/jfrog-installation-setup-documentation/cloud-native-high-availability) +* Updated the values of the recommended resources - values-small, values-medium and values-large according to the Cloud-Native HA support. +* **IMPORTANT** +* In the absence of custom parameters for primary.replicaCount and node.replicaCount on your deployment, it is recommended to specify the current values explicitly to prevent any undesired changes to the deployment structure. +* Please be advised that the configuration for resources allocation (requests, limits, javaOpts, affinity rules, etc) will now be applied solely under Values.artifactory.primary when using the new defaults. +* **Upgrade** +* Upgrade from primary-members to primary-only is recommended, and can be done by deploy the chart with the new values. +* During the upgrade, members pods should be deleted and new primary pods should be created. This might trigger the creation of new PVCs. +* Added Support for Cold Artifact Storage as part of the systemYaml configuration (disabled by default) +* Added new binary provider `s3-storage-v3-archive` +* Fixed jfconnect disabling as micro-service on non-splitcontainers +* Fixed an issue whereby, Artifactory failed to start when using persistence storage type `nfs` due to missing binarystore.xml + + +## [107.58.0] - Mar 23, 2023 +* Updated postgresql multi-arch tag version to `13.10.0-debian-11-r14` +* Removed obselete remove-lost-found initContainer` +* Added env JF_SHARED_NODE_HAENABLED under frontend when running in the container split mode + +## [107.57.0] - Mar 02, 2023 +* Updated initContainerImage and logger image to `ubi9/ubi-minimal:9.1.0.1793` + +## [107.55.0] - Feb 21, 2023 +* Updated initContainerImage and logger image to `ubi9/ubi-minimal:9.1.0.1760` +* Adding a custom preStop to Artifactory router for allowing graceful termination to complete +* Fixed an invalid reference of node selector on artifactory-ha chart + +## [107.53.0] - Jan 20, 2023 +* Updated initContainerImage and logger image to `ubi8/ubi-minimal:8.7.1049` + +## [107.50.0] - Jan 20, 2023 +* Updated postgresql tag version to `13.9.0-debian-11-r11` +* Fixed make lint issue on artifactory-ha chart [GH-1714](https://github.com/jfrog/charts/issues/1714) +* Fixed an issue for capabilities check of ingress +* Updated jfrogUrl text path in migrate.sh file +* Added a note that from 107.46.x chart versions, `copyOnEveryStartup` is not needed for binarystore.xml, it is always copied via initContainers. For more Info, Refer [GH-1723](https://github.com/jfrog/charts/issues/1723) + +## [107.49.0] - Jan 16, 2023 +* Changed logic in wait-for-primary container to use /dev/tcp instead of curl +* Added support for setting `seLinuxOptions` in `securityContext` [GH-1700](https://github.com/jfrog/charts/pull/1700) +* Added option to enable/disable proxy_request_buffering and proxy_buffering_off [GH-1686](https://github.com/jfrog/charts/pull/1686) +* Updated initContainerImage and logger image to `ubi8/ubi-minimal:8.7.1049` + +## [107.48.0] - Oct 27, 2022 +* Updated router version to `7.51.0` + +## [107.47.0] - Sep 29, 2022 +* Updated initContainerImage to `ubi8/ubi-minimal:8.6-941` +* Added support for annotations for artifactory statefulset and nginx deployment [GH-1665](https://github.com/jfrog/charts/pull/1665) +* Updated router version to `7.49.0` + +## [107.46.0] - Sep 14, 2022 +* **IMPORTANT** +* Added support for lifecycle hooks for all containers, changed `artifactory.postStartCommand` to `.Values.artifactory.lifecycle.postStart.exec.command` +* Updated initContainerImage and logger image to `ubi8/ubi-minimal:8.6-902` +* Update nginx configuration to allow websocket requests when using pipelines +* Fixed an issue to allow artifactory to make direct API calls to store instead via jfconnect service when `splitServicesToContainers=true` +* Refactor binarystore.xml configuration (moved to `files/binarystore.xml` instead of key in values.yaml) +* Added new binary providers `s3-storage-v3-direct`, `azure-blob-storage-direct`, `google-storage-v2` +* Deprecated (removed) `aws-s3` binary provider [JetS3t library](https://www.jfrog.com/confluence/display/JFROG/Configuring+the+Filestore#ConfiguringtheFilestore-BinaryProvider) +* Deprecated (removed) `google-storage` binary provider and force persistence storage type `google-storage` to work with `google-storage-v2` only +* Copy binarystore.xml in init Container to fix existing persistence on file system in clear text +* Removed obselete `.Values.artifactory.binarystore.enabled` key +* Removed `newProbes.enabled`, default to new probes +* Added nginx.customCommand using inotifyd to reload nginx's config upon ssl secret or configmap changes [GH-1640](https://github.com/jfrog/charts/pull/1640) + +## [107.43.0] - Aug 25, 2022 +* Added flag `artifactory.replicator.ingress.enabled` to enable/disable ingress for replicator +* Updated initContainerImage and logger image to `ubi8/ubi-minimal:8.6-854` +* Updated router version to `7.45.0` +* Added flag `artifactory.schedulerName` to set for the pods the value of schedulerName field [GH-1606](https://github.com/jfrog/charts/issues/1606) +* Enabled TLS based on access or router in values.yaml + +## [107.42.0] - Aug 25, 2022 +* Enabled database creds secret to use from unified secret +* Updated router version to `7.42.0` +* Added support to truncate (> 63 chars) for unifiedCustomSecretVolumeName + +## [107.41.0] - June 27, 2022 +* Added support for nginx.terminationGracePeriodSeconds [GH-1645](https://github.com/jfrog/charts/issues/1645) +* Fix nginx lifecycle values [GH-1646](https://github.com/jfrog/charts/pull/1646) +* Use an alternate command for `find` to copy custom certificates +* Added support for circle of trust using `circleOfTrustCertificatesSecret` secret name [GH-1623](https://github.com/jfrog/charts/pull/1623) + +## [107.40.0] - Jun 16, 2022 +* Deprecated k8s PodDisruptionBudget api policy/v1beta1 [GH-1618](https://github.com/jfrog/charts/issues/1618) +* Disabled node PodDisruptionBudget, statefulset and artifactory-primary service from artifactory-ha chart when member nodes are 0 +* From artifactory 7.38.x, joinKey can be retrived from Admin > User Management > Settings in UI +* Fixed template name for artifactory-ha database creds [GH-1602](https://github.com/jfrog/charts/pull/1602) +* Allow templating for pod annotations [GH-1634](https://github.com/jfrog/charts/pull/1634) +* Added flags to control enable/disable infra services in splitServicesToContainers + +## [107.39.0] - May 16, 2022 +* Fix default `artifactory.async.corePoolSize` [GH-1612](https://github.com/jfrog/charts/issues/1612) +* Added support of nginx annotations +* Reduce startupProbe `initialDelaySeconds` +* Align all liveness and readiness probes failureThreshold to `5` seconds +* Added new flag `unifiedSecretInstallation` to enables single unified secret holding all the artifactory-ha secrets +* Updated router version to `7.38.0` + +## [107.38.0] - May 04, 2022 +* Added support for `global.nodeSelector` to artifactory and nginx pods +* Updated router version to `7.36.1` +* Added support for custom global probes timeout +* Updated frontend container command +* Added topologySpreadConstraints to artifactory and nginx, and add lifecycle hooks to nginx [GH-1596](https://github.com/jfrog/charts/pull/1596) +* Added support of extraEnvironmentVariables for all infra services containers +* Enabled the consumption (jfconnect) flag by default +* Fix jfconnect disabling on non-splitcontainers + +## [107.37.0] - Mar 08, 2022 +* Added support for customPorts in nginx deployment +* Bugfix - Wrong proxy_pass configurations for /artifactory/ in the default artifactory.conf +* Added signedUrlExpirySeconds option to artifactory.persistence.type aws-S3-V3 +* Updated router version to `7.35.0` +* Added useInstanceCredentials,enableSignedUrlRedirect option to google-storage-v2 +* Changed dependency charts repo to `charts.jfrog.io` + +## [107.36.0] - Mar 03, 2022 +* Remove pdn tracker which starts replicator service +* Added silent option for curl probes +* Added readiness health check for the artifactory container for k8s version < 1.20 +* Fix property file migration issue to system.yaml 6.x to 7.x + +## [107.35.0] - Feb 08, 2022 +* Updated router version to `7.32.1` + +## [107.33.0] - Jan 11, 2022 +* Make default value of anti-affinity to soft +* Readme fixes +* Added support for setting `fsGroupChangePolicy` +* Added nginx customInitContainers, customVolumes, customSidecarContainers [GH-1565](https://github.com/jfrog/charts/pull/1565) +* Updated router version to `7.30.0` + +## [107.32.0] - Dec 23, 2021 +* Updated logger image to `jfrog/ubi-minimal:8.5-204` +* Added default `8091` as `artifactory.tomcat.maintenanceConnector.port` for probes check +* Refactored probes to replace httpGet probes with basic exec + curl +* Refactored `database-creds` secret to create only when database values are passed +* Added new endpoints for probes `/artifactory/api/v1/system/liveness` and `/artifactory/api/v1/system/readiness` +* Enabled `newProbes:true` by default to use these endpoints +* Fix filebeat sidecar spool file permissions +* Updated filebeat sidecar container to `7.16.2` + +## [107.31.0] - Dec 17, 2021 +* Remove integration service feature flag to make it mandatory service +* Update postgresql tag version to `13.4.0-debian-10-r39` +* Refactored `router.requiredServiceTypes` to support platform chart + +## [107.30.0] - Nov 30, 2021 +* Fixed incorrect permission for filebeat.yaml +* Updated healthcheck (liveness/readiness) api for integration service +* Disable readiness health check for the artifactory container when running in the container split mode +* Ability to start replicator on enabling pdn tracker + +## [107.29.0] - Nov 30, 2021 +* Added integration service container in artifactory +* Add support for Ingress Class Name in Ingress Spec [GH-1516](https://github.com/jfrog/charts/pull/1516) +* Fixed chart values to use curl instead of wget [GH-1529](https://github.com/jfrog/charts/issues/1529) +* Updated nginx config to allow websockets when pipelines is enabled +* Moved router.topology.local.requireqservicetypes from system.yaml to router as environment variable +* Added jfconnect in system.yaml +* Updated artifactory container’s health probes to use artifactory api on rt-split +* Updated initContainerImage to `jfrog/ubi-minimal:8.5-204` +* Updated router version to `7.28.2` +* Set Jfconnect enabled to `false` in the artifactory container when running in the container split mode + +## [107.28.0] - Nov 11, 2021 +* Added default values cpu and memeory in initContainers +* Updated router version to `7.26.0` +* Bug fix - jmx port not exposed in artifactory service +* Updated (`rbac.create` and `serviceAccount.create` to false by default) for least privileges +* Fixed incorrect data type for `Values.router.serviceRegistry.insecure` in default values.yaml [GH-1514](https://github.com/jfrog/charts/pull/1514/files) +* **IMPORTANT** +* Changed init-container images from `alpine` to `ubi8/ubi-minimal` +* Added support for AWS License Manager using `.Values.aws.licenseConfigSecretName` + +## [107.27.0] - Oct 6, 2021 +* **Breaking change** +* Aligned probe structure (moved probes variables under config block) +* Added support for new probes(set to false by default) +* Bugfix - Invalid format for `multiPartLimit,multipartElementSize,maxCacheSize` in binarystore.xml [GH-1466](https://github.com/jfrog/charts/issues/1466) +* Added missioncontrol container in artifactory +* Dropped NET_RAW capability for the containers +* Added resources to migration-artifactory init container +* Added resources to all rt split containers +* Updated router version to `7.25.1` +* Added support for Ingress networking.k8s.io/v1/Ingress for k8s >=1.22 [GH-1487](https://github.com/jfrog/charts/pull/1487) +* Added min kubeVersion ">= 1.14.0-0" in chart.yaml +* Update alpine tag version to `3.14.2` +* Update busybox tag version to `1.33.1` +* Update postgresql tag version to `13.4.0-debian-10-r39` + +## [107.26.0] - Aug 20, 2021 +* Added Observability container (only when `splitServicesToContainers` is enabled) +* Added min kubeVersion ">= 1.12.0-0" in chart.yaml + +## [107.25.0] - Aug 13, 2021 +* Updated readme of chart to point to wiki. Refer [Installing Artifactory](https://www.jfrog.com/confluence/display/JFROG/Installing+Artifactory) +* Added startupProbe and livenessProbe for RT-split containers +* Updated router version to 7.24.1 +* Added security hardening fixes +* Enabled startup probes for k8s >= 1.20.x +* Changed network policy to allow all ingress and egress traffic +* Added Observability changes +* Added support for global.versions.router (only when `splitServicesToContainers` is enabled) + +## [107.24.0] - July 27, 2021 +* Support global and product specific tags at the same time +* Added support for artifactory containers split + +## [107.23.0] - July 8, 2021 +* Bug fix - logger sideCar picks up Wrong File in helm +* Allow filebeat metrics configuration in values.yaml + +## [107.22.0] - July 6, 2021 +* Update alpine tag version to `3.14.0` +* Added `nodePort` support to artifactory-service and nginx-service templates +* Removed redundant `terminationGracePeriodSeconds` in statefulset +* Increased `startupProbe.failureThreshold` time + +## [107.21.3] - July 2, 2021 +* Added ability to change sendreasonphrase value in server.xml via system yaml + +## [107.19.3] - May 20, 2021 +* Fix broken support for startupProbe for k8s < 1.18.x +* Removed an extraneous resources block from the prepare-custom-persistent-volume container in the primary statefulset +* Added support for `nameOverride` and `fullnameOverride` in values.yaml + +## [107.18.6] - May 4, 2021 +* Removed `JF_SHARED_NODE_PRIMARY` env to support for Cloud Native HA +* Bumping chart version to align with app version +* Add `securityContext` option on nginx container + +## [5.0.0] - April 22, 2021 +* **Breaking change:** +* Increased default postgresql persistence size to `200Gi` +* Update postgresql tag version to `13.2.0-debian-10-r55` +* Update postgresql chart version to `10.3.18` in chart.yaml - [10.x Upgrade Notes](https://github.com/bitnami/charts/tree/master/bitnami/postgresql#to-1000) +* If this is a new deployment or you already use an external database (`postgresql.enabled=false`), these changes **do not affect you**! +* If this is an upgrade and you are using the default PostgreSQL (`postgresql.enabled=true`), you need to pass previous 9.x/10.x/12.x's postgresql.image.tag, previous postgresql.persistence.size and databaseUpgradeReady=true +* **IMPORTANT** +* This chart is only helm v3 compatible +* Fix support for Cloud Native HA +* Fixed filebeat-configmap naming +* Explicitly set ServiceAccount `automountServiceAccountToken` to 'true' +* Update alpine tag version to `3.13.5` + +## [4.13.2] - April 15, 2021 +* Updated Artifactory version to 7.17.9 - [Release Notes](https://www.jfrog.com/confluence/display/JFROG/Artifactory+Release+Notes#ArtifactoryReleaseNotes-Artifactory7.17.9) + +## [4.13.1] - April 6, 2021 +* Updated Artifactory version to 7.17.6 - [Release Notes](https://www.jfrog.com/confluence/display/JFROG/Artifactory+Release+Notes#ArtifactoryReleaseNotes-Artifactory7.17.6) +* Update alpine tag version to `3.13.4` + +## [4.13.0] - April 5, 2021 +* **IMPORTANT** +* Added `charts.jfrog.io` as default JFrog Helm repository +* Updated Artifactory version to 7.17.5 - [Release Notes](https://www.jfrog.com/confluence/display/JFROG/Artifactory+Release+Notes#ArtifactoryReleaseNotes-Artifactory7.17.5) + +## [4.12.2] - Mar 31, 2021 +* Updated Artifactory version to 7.17.4 - [Release Notes](https://www.jfrog.com/confluence/display/JFROG/Artifactory+Release+Notes#ArtifactoryReleaseNotes-Artifactory7.17.4) + +## [4.12.1] - Mar 30, 2021 +* Updated Artifactory version to 7.17.3 +* Add `timeoutSeconds` to all exec probes - Please refer [here](https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/#configure-probes) + +## [4.12.0] - Mar 24, 2021 +* Updated Artifactory version to 7.17.2 +* Optimized startupProbe time + +## [4.11.0] - Mar 18, 2021 +* Add support to startupProbe + +## [4.10.0] - Mar 15, 2021 +* Updated Artifactory version to 7.16.3 + +## [4.9.5] - Mar 09, 2021 +* Added HSTS header to nginx conf + +## [4.9.4] - Mar 9, 2021 +* Removed bintray URL references in the chart + +## [4.9.3] - Mar 04, 2021 +* Updated Artifactory version to 7.15.4 - [Release Notes](https://www.jfrog.com/confluence/display/JFROG/Artifactory+Release+Notes#ArtifactoryReleaseNotes-Artifactory7.15.4) + +## [4.9.2] - Mar 04, 2021 +* Fixed creation of nginx-certificate-secret when Nginx is disabled + +## [4.9.1] - Feb 19, 2021 +* Update busybox tag version to `1.32.1` + +## [4.9.0] - Feb 18, 2021 +* Updated Artifactory version to 7.15.3 - [Release Notes](https://www.jfrog.com/confluence/display/JFROG/Artifactory+Release+Notes#ArtifactoryReleaseNotes-Artifactory7.15.3) +* Add option to specify update strategy for Artifactory statefulset + +## [4.8.1] - Feb 11, 2021 +* Exposed "multiPartLimit" and "multipartElementSize" for the Azure Blob Storage Binary Provider + +## [4.8.0] - Feb 08, 2021 +* Updated Artifactory version to 7.12.8 - [Release Notes](https://www.jfrog.com/confluence/display/JFROG/Artifactory+Release+Notes#ArtifactoryReleaseNotes-Artifactory7.12.8) +* Support for custom certificates using secrets +* **Important:** Switched docker images download from `docker.bintray.io` to `releases-docker.jfrog.io` +* Update alpine tag version to `3.13.1` + +## [4.7.9] - Feb 3, 2021 +* Fix copyOnEveryStartup for HA cluster license + +## [4.7.8] - Jan 25, 2021 +* Add support for hostAliases + +## [4.7.7] - Jan 11, 2021 +* Fix failures when using creds file for configurating google storage + +## [4.7.6] - Jan 11, 2021 +* Updated Artifactory version to 7.12.6 - [Release Notes](https://www.jfrog.com/confluence/display/JFROG/Artifactory+Release+Notes#ArtifactoryReleaseNotes-Artifactory7.12.6) + +## [4.7.5] - Jan 07, 2021 +* Added support for optional tracker dedicated ingress `.Values.artifactory.replicator.trackerIngress.enabled` (defaults to false) + +## [4.7.4] - Jan 04, 2021 +* Fixed gid support for statefulset + +## [4.7.3] - Dec 31, 2020 +* Added gid support for statefulset +* Add setSecurityContext flag to allow securityContext block to be removed from artifactory statefulset + +## [4.7.2] - Dec 29, 2020 +* **Important:** Removed `.Values.metrics` and `.Values.fluentd` (Fluentd and Prometheus integrations) +* Add support for creating additional kubernetes resources - [refer here](https://github.com/jfrog/log-analytics-prometheus/blob/master/artifactory-ha-values.yaml) +* Updated Artifactory version to 7.12.5 + +## [4.7.1] - Dec 21, 2020 +* Updated Artifactory version to 7.12.3 + +## [4.7.0] - Dec 18, 2020 +* Updated Artifactory version to 7.12.2 +* Added `.Values.artifactory.openMetrics.enabled` + +## [4.6.1] - Dec 11, 2020 +* Added configurable `.Values.global.versions.artifactory` in values.yaml + +## [4.6.0] - Dec 10, 2020 +* Update postgresql tag version to `12.5.0-debian-10-r25` +* Fixed `artifactory.persistence.googleStorage.endpoint` from `storage.googleapis.com` to `commondatastorage.googleapis.com` +* Updated chart maintainers email + +## [4.5.5] - Dec 4, 2020 +* **Important:** Renamed `.Values.systemYaml` to `.Values.systemYamlOverride` + +## [4.5.4] - Dec 1, 2020 +* Improve error message returned when attempting helm upgrade command + +## [4.5.3] - Nov 30, 2020 +* Updated Artifactory version to 7.11.5 - [Release Notes](https://www.jfrog.com/confluence/display/JFROG/Artifactory+Release+Notes#ArtifactoryReleaseNotes-Artifactory7.11) + +# [4.5.2] - Nov 23, 2020 +* Updated Artifactory version to 7.11.2 - [Release Notes](https://www.jfrog.com/confluence/display/JFROG/Artifactory+Release+Notes#ArtifactoryReleaseNotes-Artifactory7.11) +* Updated port namings on services and pods to allow for istio protocol discovery +* Change semverCompare checks to support hosted Kubernetes +* Add flag to disable creation of ServiceMonitor when enabling prometheus metrics +* Prevent the PostHook command to be executed if the user did not specify a command in the values file +* Fix issue with tls file generation when nginx.https.enabled is false + +## [4.5.1] - Nov 19, 2020 +* Updated Artifactory version to 7.11.2 +* Bugfix - access.config.import.xml override Access Federation configurations + +## [4.5.0] - Nov 17, 2020 +* Updated Artifactory version to 7.11.1 +* Update alpine tag version to `3.12.1` + +## [4.4.6] - Nov 10, 2020 +* Pass system.yaml via external secret for advanced usecases +* Added support for custom ingress +* Bugfix - stateful set not picking up changes to database secrets + +## [4.4.5] - Nov 9, 2020 +* Updated Artifactory version to 7.10.6 - [Release Notes](https://www.jfrog.com/confluence/display/JFROG/Artifactory+Release+Notes#ArtifactoryReleaseNotes-Artifactory7.10.6) + +## [4.4.4] - Nov 2, 2020 +* Add enablePathStyleAccess property for aws-s3-v3 binary provider template + +## [4.4.3] - Nov 2, 2020 +* Updated Artifactory version to 7.10.5 - [Release Notes](https://www.jfrog.com/confluence/display/JFROG/Artifactory+Release+Notes#ArtifactoryReleaseNotes-Artifactory7.10.5) + +## [4.4.2] - Oct 22, 2020 +* Chown bug fix where Linux capability cannot chown all files causing log line warnings +* Fix Frontend timeout linting issue + +## [4.4.1] - Oct 20, 2020 +* Add flag to disable prepare-custom-persistent-volume init container + +## [4.4.0] - Oct 19, 2020 +* Updated Artifactory version to 7.10.2 - [Release Notes](https://www.jfrog.com/confluence/display/JFROG/Artifactory+Release+Notes#ArtifactoryReleaseNotes-Artifactory7.10.2) + +## [4.3.4] - Oct 19, 2020 +* Add support to specify priorityClassName for nginx deployment + +## [4.3.3] - Oct 15, 2020 +* Fixed issue with node PodDisruptionBudget which also getting applied on the primary +* Fix mandatory masterKey check issue when upgrading from 6.x to 7.x + +## [4.3.2] - Oct 14, 2020 +* Add support to allow more than 1 Primary in Artifactory-ha STS + +## [4.3.1] - Oct 9, 2020 +* Add global support for customInitContainersBegin + +## [4.3.0] - Oct 07, 2020 +* Updated Artifactory version to 7.9.1 +* **Breaking change:** Fix `storageClass` to correct `storageClassName` in values.yaml + +## [4.2.0] - Oct 5, 2020 +* Expose Prometheus metrics via a ServiceMonitor +* Parse log files for metric data with Fluentd + +## [4.1.0] - Sep 30, 2020 +* Updated Artifactory version to 7.9.0 - [Release Notes](https://www.jfrog.com/confluence/display/JFROG/Artifactory+Release+Notes#ArtifactoryReleaseNotes-Artifactory7.9) + +## [4.0.12] - Sep 25, 2020 +* Update to use linux capability CAP_CHOWN instead of root base init container to avoid any use of root containers to pass Redhat security requirements + +## [4.0.11] - Sep 28, 2020 +* Setting chart coordinates in migitation yaml + +## [4.0.10] - Sep 25, 2020 +* Update filebeat version to `7.9.2` + +## [4.0.9] - Sep 24, 2020 +* Fixed broken issue - when setting `waitForDatabase:false` container startup still waits for DB + +## [4.0.8] - Sep 22, 2020 +* Updated readme + +## [4.0.7] - Sep 22, 2020 +* Fix lint issue in migitation yaml + +## [4.0.6] - Sep 22, 2020 +* Fix broken migitation yaml + +## [4.0.5] - Sep 21, 2020 +* Added mitigation yaml for Artifactory - [More info](https://github.com/jfrog/chartcenter/blob/master/docs/securitymitigationspec.md) + +## [4.0.4] - Sep 17, 2020 +* Added configurable session(UI) timeout in frontend microservice + +## [4.0.3] - Sep 17, 2020 +* Fix small typo in README and added proper required text to be shown while postgres upgrades + +## [4.0.2] - Sep 14, 2020 +* Updated Artifactory version to 7.7.8 - [Release Notes](https://www.jfrog.com/confluence/display/JFROG/Artifactory+Release+Notes#ArtifactoryReleaseNotes-Artifactory7.7.8) + +## [4.0.1] - Sep 8, 2020 +* Added support for artifactory pro license (single node) installation. + +## [4.0.0] - Sep 2, 2020 +* **Breaking change:** Changed `imagePullSecrets` value from string to list +* **Breaking change:** Added `image.registry` and changed `image.version` to `image.tag` for docker images +* Added support for global values +* Updated maintainers in chart.yaml +* Update postgresql tag version to `12.3.0-debian-10-r71` +* Update postgresqlsub chart version to `9.3.4` - [9.x Upgrade Notes](https://github.com/bitnami/charts/tree/master/bitnami/postgresql#900) +* **IMPORTANT** +* If this is a new deployment or you already use an external database (`postgresql.enabled=false`), these changes **do not affect you**! +* If this is an upgrade and you are using the default PostgreSQL (`postgresql.enabled=true`), you need to pass previous 9.x/10.x's postgresql.image.tag and databaseUpgradeReady=true. + +## [3.1.0] - Aug 13, 2020 +* Updated Artifactory version to 7.7.3 - [Release Notes](https://www.jfrog.com/confluence/display/JFROG/Artifactory+Release+Notes#ArtifactoryReleaseNotes-Artifactory7.7) + +## [3.0.15] - Aug 10, 2020 +* Added enableSignedUrlRedirect for persistent storage type aws-s3-v3. + +## [3.0.14] - Jul 31, 2020 +* Update the README section on Nginx SSL termination to reflect the actual YAML structure. + +## [3.0.13] - Jul 30, 2020 +* Added condition to disable the migration scripts. + +## [3.0.12] - Jul 29, 2020 +* Document Artifactory node affinity. + +## [3.0.11] - Jul 28, 2020 +* Added maxConnections for persistent storage type aws-s3-v3. + +## [3.0.10] - Jul 28, 2020 +Bugfix / support for userPluginSecrets with Artifactory 7 + +## [3.0.9] - Jul 27, 2020 +* Add tpl to external database secrets. +* Modified `scheme` to `artifactory-ha.scheme` + +## [3.0.8] - Jul 23, 2020 +* Added condition to disable the migration init container. + +## [3.0.7] - Jul 21, 2020 +* Updated Artifactory-ha Chart to add node and primary labels to pods and service objects. + +## [3.0.6] - Jul 20, 2020 +* Support custom CA and certificates + +## [3.0.5] - Jul 13, 2020 +* Updated Artifactory version to 7.6.3 - https://www.jfrog.com/confluence/display/JFROG/Artifactory+Release+Notes#ArtifactoryReleaseNotes-Artifactory7.6.3 +* Fixed Mysql database jar path in `preStartCommand` in README + +## [3.0.4] - Jul 8, 2020 +* Move some postgresql values to where they should be according to the subchart + +## [3.0.3] - Jul 8, 2020 +* Set Artifactory access client connections to the same value as the access threads. + +## [3.0.2] - Jul 6, 2020 +* Updated Artifactory version to 7.6.2 +* **IMPORTANT** +* Added ChartCenter Helm repository in README + +## [3.0.1] - Jul 01, 2020 +* Add dedicated ingress object for Replicator service when enabled + +## [3.0.0] - Jun 30, 2020 +* Update postgresql tag version to `10.13.0-debian-10-r38` +* Update alpine tag version to `3.12` +* Update busybox tag version to `1.31.1` +* **IMPORTANT** +* If this is a new deployment or you already use an external database (`postgresql.enabled=false`), these changes **do not affect you**! +* If this is an upgrade and you are using the default PostgreSQL (`postgresql.enabled=true`), you need to pass postgresql.image.tag=9.6.18-debian-10-r7 and databaseUpgradeReady=true + +## [2.6.0] - Jun 29, 2020 +* Updated Artifactory version to 7.6.1 - https://www.jfrog.com/confluence/display/JFROG/Artifactory+Release+Notes#ArtifactoryReleaseNotes-Artifactory7.6.1 +* Add tpl for external database secrets + +## [2.5.8] - Jun 25, 2020 +* Stop loading the Nginx stream module because it is now a core module + +## [2.5.7] - Jun 18, 2020 +* Fixes bootstrap configMap issue on member node + +## [2.5.6] - Jun 11, 2020 +* Support list of custom secrets + +## [2.5.5] - Jun 11, 2020 +* NOTES.txt fixed incorrect information + +## [2.5.4] - Jun 12, 2020 +* Updated Artifactory version to 7.5.7 - https://www.jfrog.com/confluence/display/JFROG/Artifactory+Release+Notes#ArtifactoryReleaseNotes-Artifactory7.5.7 + +## [2.5.3] - Jun 8, 2020 +* Statically setting primary service type to ClusterIP. +* Prevents primary service from being exposed publicly when using LoadBalancer type on cloud providers. + +## [2.5.2] - Jun 8, 2020 +* Readme update - configuring Artifactory with oracledb + +## [2.5.1] - Jun 5, 2020 +* Fixes broken PDB issue upgrading from 6.x to 7.x + +## [2.5.0] - Jun 1, 2020 +* Updated Artifactory version to 7.5.5 - https://www.jfrog.com/confluence/display/JFROG/Artifactory+Release+Notes#ArtifactoryReleaseNotes-Artifactory7.5 +* Fixes bootstrap configMap permission issue +* Update postgresql tag version to `9.6.18-debian-10-r7` + +## [2.4.10] - May 27, 2020 +* Added Tomcat maxThreads & acceptCount + +## [2.4.9] - May 25, 2020 +* Fixed postgresql README `image` Parameters + +## [2.4.8] - May 24, 2020 +* Fixed typo in README regarding migration timeout + +## [2.4.7] - May 19, 2020 +* Added metadata maxOpenConnections + +## [2.4.6] - May 07, 2020 +* Fix `installerInfo` string format + +## [2.4.5] - Apr 27, 2020 +* Updated Artifactory version to 7.4.3 + +## [2.4.4] - Apr 27, 2020 +* Change customInitContainers order to run before the "migration-ha-artifactory" initContainer + +## [2.4.3] - Apr 24, 2020 +* Fix `artifactory.persistence.awsS3V3.useInstanceCredentials` incorrect conditional logic +* Bump postgresql tag version to `9.6.17-debian-10-r72` in values.yaml + +## [2.4.2] - Apr 16, 2020 +* Custom volume mounts in migration init container. + +## [2.4.1] - Apr 16, 2020 +* Fix broken support for gcpServiceAccount for googleStorage + +## [2.4.0] - Apr 14, 2020 +* Updated Artifactory version to 7.4.1 + +## [2.3.1] - April 13, 2020 +* Update README with helm v3 commands + +## [2.3.0] - April 10, 2020 +* Use dependency charts from `https://charts.bitnami.com/bitnami` +* Bump postgresql chart version to `8.7.3` in requirements.yaml +* Bump postgresql tag version to `9.6.17-debian-10-r21` in values.yaml + +## [2.2.11] - Apr 8, 2020 +* Added recommended ingress annotation to avoid 413 errors + +## [2.2.10] - Apr 8, 2020 +* Moved migration scripts under `files` directory +* Support preStartCommand in migration Init container as `artifactory.migration.preStartCommand` + +## [2.2.9] - Apr 01, 2020 +* Support masterKey and joinKey as secrets + +## [2.2.8] - Apr 01, 2020 +* Ensure that the join key is also copied when provided by an external secret +* Migration container in primary and node statefulset now respects custom versions and the specified node/primary resources + +## [2.2.7] - Apr 01, 2020 +* Added cache-layer in chain definition of Google Cloud Storage template +* Fix readme use to `-hex 32` instead of `-hex 16` + +## [2.2.6] - Mar 31, 2020 +* Change the way the artifactory `command:` is set so it will properly pass a SIGTERM to java + +## [2.2.5] - Mar 31, 2020 +* Removed duplicate `artifactory-license` volume from primary node + +## [2.2.4] - Mar 31, 2020 +* Restore `artifactory-license` volume for the primary node + +## [2.2.3] - Mar 29, 2020 +* Add Nginx log options: stderr as logfile and log level + +## [2.2.2] - Mar 30, 2020 +* Apply initContainers.resources to `copy-system-yaml`, `prepare-custom-persistent-volume`, and `migration-artifactory-ha` containers +* Use the same defaulting mechanism used for the artifactory version used elsewhere in the chart +* Removed duplicate `artifactory-license` volume that prevented using an external secret + +## [2.2.1] - Mar 29, 2020 +* Fix loggers sidecars configurations to support new file system layout and new log names + +## [2.2.0] - Mar 29, 2020 +* Fix broken admin user bootstrap configuration +* **Breaking change:** renamed `artifactory.accessAdmin` to `artifactory.admin` + +## [2.1.3] - Mar 24, 2020 +* Use `postgresqlExtendedConf` for setting custom PostgreSQL configuration (instead of `postgresqlConfiguration`) + +## [2.1.2] - Mar 21, 2020 +* Support for SSL offload in Nginx service(LoadBalancer) layer. Introduced `nginx.service.ssloffload` field with boolean type. + +## [2.1.1] - Mar 23, 2020 +* Moved installer info to values.yaml so it is fully customizable + +## [2.1.0] - Mar 23, 2020 +* Updated Artifactory version to 7.3.2 + +## [2.0.36] - Mar 20, 2020 +* Add support GCP credentials.json authentication + +## [2.0.35] - Mar 20, 2020 +* Add support for masterKey trim during 6.x to 7.x migration if 6.x masterKey is 32 hex (64 characters) + +## [2.0.34] - Mar 19, 2020 +* Add support for NFS directories `haBackupDir` and `haDataDir` + +## [2.0.33] - Mar 18, 2020 +* Increased Nginx proxy_buffers size + +## [2.0.32] - Mar 17, 2020 +* Changed all single quotes to double quotes in values files +* useInstanceCredentials variable was declared in S3 settings but not used in chart. Now it is being used. + +## [2.0.31] - Mar 17, 2020 +* Fix rendering of Service Account annotations + +## [2.0.30] - Mar 16, 2020 +* Add Unsupported message from 6.18 to 7.2.x (migration) + +## [2.0.29] - Mar 11, 2020 +* Upgrade Docs update + +## [2.0.28] - Mar 11, 2020 +* Unified charts public release + +## [2.0.27] - Mar 8, 2020 +* Add an optional wait for primary node to be ready with a proper test for http status + +## [2.0.23] - Mar 6, 2020 +* Fix path to `/artifactory_bootstrap` +* Add support for controlling the name of the ingress and allow to set more than one cname + +## [2.0.22] - Mar 4, 2020 +* Add support for disabling `consoleLog` in `system.yaml` file + +## [2.0.21] - Feb 28, 2020 +* Add support to process `valueFrom` for extraEnvironmentVariables + +## [2.0.20] - Feb 26, 2020 +* Store join key to secret + +## [2.0.19] - Feb 26, 2020 +* Updated Artifactory version to 7.2.1 + +## [2.0.12] - Feb 07, 2020 +* Remove protection flag `databaseUpgradeReady` which was added to check internal postgres upgrade + +## [2.0.0] - Feb 07, 2020 +* Updated Artifactory version to 7.0.0 + +## [1.4.10] - Feb 13, 2020 +* Add support for SSH authentication to Artifactory + +## [1.4.9] - Feb 10, 2020 +* Fix custom DB password indention + +## [1.4.8] - Feb 9, 2020 +* Add support for `tpl` in the `postStartCommand` + +## [1.4.7] - Feb 4, 2020 +* Support customisable Nginx kind + +## [1.4.6] - Feb 2, 2020 +* Add a comment stating that it is recommended to use an external PostgreSQL with a static password for production installations + +## [1.4.5] - Feb 2, 2020 +* Add support for primary or member node specific preStartCommand + +## [1.4.4] - Jan 30, 2020 +* Add the option to configure resources for the logger containers + +## [1.4.3] - Jan 26, 2020 +* Improve `database.user` and `database.password` logic in order to support more use cases and make the configuration less repetitive + +## [1.4.2] - Jan 22, 2020 +* Refined pod disruption budgets to separate nginx and Artifactory pods + +## [1.4.1] - Jan 19, 2020 +* Fix replicator port config in nginx replicator configmap + +## [1.4.0] - Jan 19, 2020 +* Updated Artifactory version to 6.17.0 + +## [1.3.8] - Jan 16, 2020 +* Added example for external nginx-ingress + +## [1.3.7] - Jan 07, 2020 +* Add support for customizable `mountOptions` of NFS PVs + +## [1.3.6] - Dec 30, 2019 +* Fix for nginx probes failing when launched with http disabled + +## [1.3.5] - Dec 24, 2019 +* Better support for custom `artifactory.internalPort` + +## [1.3.4] - Dec 23, 2019 +* Mark empty map values with `{}` + +## [1.3.3] - Dec 16, 2019 +* Another fix for toggling nginx service ports + +## [1.3.2] - Dec 12, 2019 +* Fix for toggling nginx service ports + +## [1.3.1] - Dec 10, 2019 +* Add support for toggling nginx service ports + +## [1.3.0] - Dec 1, 2019 +* Updated Artifactory version to 6.16.0 + +## [1.2.4] - Nov 28, 2019 +* Add support for using existing PriorityClass + +## [1.2.3] - Nov 27, 2019 +* Add support for PriorityClass + +## [1.2.2] - Nov 20, 2019 +* Update Artifactory logo + +## [1.2.1] - Nov 18, 2019 +* Add the option to provide service account annotations (in order to support stuff like https://docs.aws.amazon.com/eks/latest/userguide/specify-service-account-role.html) + +## [1.2.0] - Nov 18, 2019 +* Updated Artifactory version to 6.15.0 + +## [1.1.12] - Nov 17, 2019 +* Fix `README.md` format (broken table) + +## [1.1.11] - Nov 17, 2019 +* Update comment on Artifactory master key + +## [1.1.10] - Nov 17, 2019 +* Fix creation of double slash in nginx artifactory configuration + +## [1.1.9] - Nov 14, 2019 +* Set explicit `postgresql.postgresqlPassword=""` to avoid helm v3 error + +## [1.1.8] - Nov 12, 2019 +* Updated Artifactory version to 6.14.1 + +## [1.1.7] - Nov 11, 2019 +* Additional documentation for masterKey + +## [1.1.6] - Nov 10, 2019 +* Update PostgreSQL chart version to 7.0.1 +* Use formal PostgreSQL configuration format + +## [1.1.5] - Nov 8, 2019 +* Add support `artifactory.service.loadBalancerSourceRanges` for whitelisting when setting `artifactory.service.type=LoadBalancer` + +## [1.1.4] - Nov 6, 2019 +* Add support for any type of environment variable by using `extraEnvironmentVariables` as-is + +## [1.1.3] - Nov 6, 2019 +* Add nodeselector support for Postgresql + +## [1.1.2] - Nov 5, 2019 +* Add support for the aws-s3-v3 filestore, which adds support for pod IAM roles + +## [1.1.1] - Nov 4, 2019 +* When using `copyOnEveryStartup`, make sure that the target base directories are created before copying the files + +## [1.1.0] - Nov 3, 2019 +* Updated Artifactory version to 6.14.0 + +## [1.0.1] - Nov 3, 2019 +* Make sure the artifactory pod exits when one of the pre-start stages fail + +## [1.0.0] - Oct 27, 2019 +**IMPORTANT - BREAKING CHANGES!**
    +**DOWNTIME MIGHT BE REQUIRED FOR AN UPGRADE!** +* If this is a new deployment or you already use an external database (`postgresql.enabled=false`), these changes **do not affect you**! +* If this is an upgrade and you are using the default PostgreSQL (`postgresql.enabled=true`), must use the upgrade instructions in [UPGRADE_NOTES.md](UPGRADE_NOTES.md)! +* PostgreSQL sub chart was upgraded to version `6.5.x`. This version is **not backward compatible** with the old version (`0.9.5`)! +* Note the following **PostgreSQL** Helm chart changes + * The chart configuration has changed! See [values.yaml](values.yaml) for the new keys used + * **PostgreSQL** is deployed as a StatefulSet + * See [PostgreSQL helm chart](https://hub.helm.sh/charts/stable/postgresql) for all available configurations + +## [0.17.3] - Oct 24, 2019 +* Change the preStartCommand to support templating + +## [0.17.2] - Oct 21, 2019 +* Add support for setting `artifactory.primary.labels` +* Add support for setting `artifactory.node.labels` +* Add support for setting `nginx.labels` + +## [0.17.1] - Oct 10, 2019 +* Updated Artifactory version to 6.13.1 + +## [0.17.0] - Oct 7, 2019 +* Updated Artifactory version to 6.13.0 + +## [0.16.7] - Sep 24, 2019 +* Option to skip wait-for-db init container with '--set waitForDatabase=false' + +## [0.16.6] - Sep 24, 2019 +* Add support for setting `nginx.service.labels` + +## [0.16.5] - Sep 23, 2019 +* Add support for setting `artifactory.customInitContainersBegin` + +## [0.16.4] - Sep 20, 2019 +* Add support for setting `initContainers.resources` + +## [0.16.3] - Sep 11, 2019 +* Updated Artifactory version to 6.12.2 + +## [0.16.2] - Sep 9, 2019 +* Updated Artifactory version to 6.12.1 + +## [0.16.1] - Aug 22, 2019 +* Fix the nginx server_name directive used with ingress.hosts + +## [0.16.0] - Aug 21, 2019 +* Updated Artifactory version to 6.12.0 + +## [0.15.15] - Aug 18, 2019 +* Fix existingSharedClaim permissions issue and example + +## [0.15.14] - Aug 14, 2019 +* Updated Artifactory version to 6.11.6 + +## [0.15.13] - Aug 11, 2019 +* Fix Ingress routing and add an example + +## [0.15.12] - Aug 6, 2019 +* Do not mount `access/etc/bootstrap.creds` unless user specifies a custom password or secret (Access already generates a random password if not provided one) +* If custom `bootstrap.creds` is provided (using keys or custom secret), prepare it with an init container so the temp file does not persist + +## [0.15.11] - Aug 5, 2019 +* Improve binarystore config + 1. Convert to a secret + 2. Move config to values.yaml + 3. Support an external secret + +## [0.15.10] - Aug 5, 2019 +* Don't create the nginx configmaps when nginx.enabled is false + +## [0.15.9] - Aug 1, 2019 +* Fix masterkey/masterKeySecretName not specified warning render logic in NOTES.txt + +## [0.15.8] - Jul 28, 2019 +* Simplify nginx setup and shorten initial wait for probes + +## [0.15.7] - Jul 25, 2019 +* Updated README about how to apply Artifactory licenses + +## [0.15.6] - Jul 22, 2019 +* Change Ingress API to be compatible with recent kubernetes versions + +## [0.15.5] - Jul 22, 2019 +* Updated Artifactory version to 6.11.3 + +## [0.15.4] - Jul 11, 2019 +* Add `artifactory.customVolumeMounts` support to member node statefulset template + +## [0.15.3] - Jul 11, 2019 +* Add ingress.hosts to the Nginx server_name directive when ingress is enabled to help with Docker repository sub domain configuration + +## [0.15.2] - Jul 3, 2019 +* Add the option for changing nginx config using values.yaml and remove outdated reverse proxy documentation + +## [0.15.1] - Jul 1, 2019 +* Updated Artifactory version to 6.11.1 + +## [0.15.0] - Jun 27, 2019 +* Updated Artifactory version to 6.11.0 and Restart Primary node when bootstrap.creds file has been modified in artifactory-ha + +## [0.14.4] - Jun 24, 2019 +* Add the option to provide an IP for the access-admin endpoints + +## [0.14.3] - Jun 24, 2019 +* Update chart maintainers + +## [0.14.2] - Jun 24, 2019 +* Change Nginx to point to the artifactory externalPort + +## [0.14.1] - Jun 23, 2019 +* Add values files for small, medium and large installations + +## [0.14.0] - Jun 20, 2019 +* Use ConfigMaps for nginx configuration and remove nginx postStart command + +## [0.13.10] - Jun 19, 2019 +* Updated Artifactory version to 6.10.4 + +## [0.13.9] - Jun 18, 2019 +* Add the option to provide additional ingress rules + +## [0.13.8] - Jun 14, 2019 +* Updated readme with improved external database setup example + +## [0.13.7] - Jun 6, 2019 +* Updated Artifactory version to 6.10.3 +* Updated installer-info template + +## [0.13.6] - Jun 6, 2019 +* Updated Google Cloud Storage API URL and https settings + +## [0.13.5] - Jun 5, 2019 +* Delete the db.properties file on Artifactory startup + +## [0.13.4] - Jun 3, 2019 +* Updated Artifactory version to 6.10.2 + +## [0.13.3] - May 21, 2019 +* Updated Artifactory version to 6.10.1 + +## [0.13.2] - May 19, 2019 +* Fix missing logger image tag + +## [0.13.1] - May 15, 2019 +* Support `artifactory.persistence.cacheProviderDir` for on-premise cluster + +## [0.13.0] - May 7, 2019 +* Updated Artifactory version to 6.10.0 + +## [0.12.23] - May 5, 2019 +* Add support for setting `artifactory.async.corePoolSize` + +## [0.12.22] - May 2, 2019 +* Remove unused property `artifactory.releasebundle.feature.enabled` + +## [0.12.21] - Apr 30, 2019 +* Add support for JMX monitoring + +## [0.12.20] - Apr29, 2019 +* Added support for headless services + +## [0.12.19] - Apr 28, 2019 +* Added support for `cacheProviderDir` + +## [0.12.18] - Apr 18, 2019 +* Changing API StatefulSet version to `v1` and permission fix for custom `artifactory.conf` for Nginx + +## [0.12.17] - Apr 16, 2019 +* Updated documentation for Reverse Proxy Configuration + +## [0.12.16] - Apr 12, 2019 +* Added support for `customVolumeMounts` + +## [0.12.15] - Aprl 12, 2019 +* Added support for `bucketExists` flag for googleStorage + +## [0.12.14] - Apr 11, 2019 +* Replace `curl` examples with `wget` due to the new base image + +## [0.12.13] - Aprl 07, 2019 +* Add support for providing the Artifactory license as a parameter + +## [0.12.12] - Apr 10, 2019 +* Updated Artifactory version to 6.9.1 + +## [0.12.11] - Aprl 04, 2019 +* Add support for templated extraEnvironmentVariables + +## [0.12.10] - Aprl 07, 2019 +* Change network policy API group + +## [0.12.9] - Aprl 04, 2019 +* Apply the existing PVC for members (in addition to primary) + +## [0.12.8] - Aprl 03, 2019 +* Bugfix for userPluginSecrets + +## [0.12.7] - Apr 4, 2019 +* Add information about upgrading Artifactory with auto-generated postgres password + +## [0.12.6] - Aprl 03, 2019 +* Added installer info + +## [0.12.5] - Aprl 03, 2019 +* Allow secret names for user plugins to contain template language + +## [0.12.4] - Apr 02, 2019 +* Fix issue #253 (use existing PVC for data and backup storage) + +## [0.12.3] - Apr 02, 2019 +* Allow NetworkPolicy configurations (defaults to allow all) + +## [0.12.2] - Aprl 01, 2019 +* Add support for user plugin secret + +## [0.12.1] - Mar 26, 2019 +* Add the option to copy a list of files to ARTIFACTORY_HOME on startup + +## [0.12.0] - Mar 26, 2019 +* Updated Artifactory version to 6.9.0 + +## [0.11.18] - Mar 25, 2019 +* Add CI tests for persistence, ingress support and nginx + +## [0.11.17] - Mar 22, 2019 +* Add the option to change the default access-admin password + +## [0.11.16] - Mar 22, 2019 +* Added support for `.Probe.path` to customise the paths used for health probes + +## [0.11.15] - Mar 21, 2019 +* Added support for `artifactory.customSidecarContainers` to create custom sidecar containers +* Added support for `artifactory.customVolumes` to create custom volumes + +## [0.11.14] - Mar 21, 2019 +* Make ingress path configurable + +## [0.11.13] - Mar 19, 2019 +* Move the copy of bootstrap config from postStart to preStart for Primary + +## [0.11.12] - Mar 19, 2019 +* Fix existingClaim example + +## [0.11.11] - Mar 18, 2019 +* Disable the option to use nginx PVC with more than one replica + +## [0.11.10] - Mar 15, 2019 +* Wait for nginx configuration file before using it + +## [0.11.9] - Mar 15, 2019 +* Revert securityContext changes since they were causing issues + +## [0.11.8] - Mar 15, 2019 +* Fix issue #247 (init container failing to run) + +## [0.11.7] - Mar 14, 2019 +* Updated Artifactory version to 6.8.7 + +## [0.11.6] - Mar 13, 2019 +* Move securityContext to container level + +## [0.11.5] - Mar 11, 2019 +* Add the option to use existing volume claims for Artifactory storage + +## [0.11.4] - Mar 11, 2019 +* Updated Artifactory version to 6.8.6 + +## [0.11.3] - Mar 5, 2019 +* Updated Artifactory version to 6.8.4 + +## [0.11.2] - Mar 4, 2019 +* Add support for catalina logs sidecars + +## [0.11.1] - Feb 27, 2019 +* Updated Artifactory version to 6.8.3 + +## [0.11.0] - Feb 25, 2019 +* Add nginx support for tail sidecars + +## [0.10.3] - Feb 21, 2019 +* Add s3AwsVersion option to awsS3 configuration for use with IAM roles + +## [0.10.2] - Feb 19, 2019 +* Updated Artifactory version to 6.8.2 + +## [0.10.1] - Feb 17, 2019 +* Updated Artifactory version to 6.8.1 +* Add example of `SERVER_XML_EXTRA_CONNECTOR` usage + +## [0.10.0] - Feb 15, 2019 +* Updated Artifactory version to 6.8.0 + +## [0.9.7] - Feb 13, 2019 +* Updated Artifactory version to 6.7.3 + +## [0.9.6] - Feb 7, 2019 +* Add support for tail sidecars to view logs from k8s api + +## [0.9.5] - Feb 6, 2019 +* Fix support for customizing statefulset `terminationGracePeriodSeconds` + +## [0.9.4] - Feb 5, 2019 +* Add support for customizing statefulset `terminationGracePeriodSeconds` + +## [0.9.3] - Feb 5, 2019 +* Remove the inactive server remove plugin + +## [0.9.2] - Feb 3, 2019 +* Updated Artifactory version to 6.7.2 + +## [0.9.1] - Jan 27, 2019 +* Fix support for Azure Blob Storage Binary provider + +## [0.9.0] - Jan 23, 2019 +* Updated Artifactory version to 6.7.0 + +## [0.8.10] - Jan 22, 2019 +* Added support for `artifactory.customInitContainers` to create custom init containers + +## [0.8.9] - Jan 18, 2019 +* Added support of values ingress.labels + +## [0.8.8] - Jan 16, 2019 +* Mount replicator.yaml (config) directly to /replicator_extra_conf + +## [0.8.7] - Jan 15, 2018 +* Add support for Azure Blob Storage Binary provider + +## [0.8.6] - Jan 13, 2019 +* Fix documentation about nginx group id + +## [0.8.5] - Jan 13, 2019 +* Updated Artifactory version to 6.6.5 + +## [0.8.4] - Jan 8, 2019 +* Make artifactory.replicator.publicUrl required when the replicator is enabled + +## [0.8.3] - Jan 1, 2019 +* Updated Artifactory version to 6.6.3 +* Add support for `artifactory.extraEnvironmentVariables` to pass more environment variables to Artifactory + +## [0.8.2] - Dec 28, 2018 +* Fix location `replicator.yaml` is copied to + +## [0.8.1] - Dec 27, 2018 +* Updated Artifactory version to 6.6.1 + +## [0.8.0] - Dec 20, 2018 +* Updated Artifactory version to 6.6.0 + +## [0.7.17] - Dec 17, 2018 +* Updated Artifactory version to 6.5.13 + +## [0.7.16] - Dec 12, 2018 +* Fix documentation about Artifactory license setup using secret + +## [0.7.15] - Dec 9, 2018 +* AWS S3 add `roleName` for using IAM role + +## [0.7.14] - Dec 6, 2018 +* AWS S3 `identity` and `credential` are now added only if have a value to allow using IAM role + +## [0.7.13] - Dec 5, 2018 +* Remove Distribution certificates creation. + +## [0.7.12] - Dec 2, 2018 +* Remove Java option "-Dartifactory.locking.provider.type=db". This is already the default setting. + +## [0.7.11] - Nov 30, 2018 +* Updated Artifactory version to 6.5.9 + +## [0.7.10] - Nov 29, 2018 +* Fixed the volumeMount for the replicator.yaml + +## [0.7.9] - Nov 29, 2018 +* Optionally include primary node into poddisruptionbudget + +## [0.7.8] - Nov 29, 2018 +* Updated postgresql version to 9.6.11 + +## [0.7.7] - Nov 27, 2018 +* Updated Artifactory version to 6.5.8 + +## [0.7.6] - Nov 18, 2018 +* Added support for configMap to use custom Reverse Proxy Configuration with Nginx + +## [0.7.5] - Nov 14, 2018 +* Updated Artifactory version to 6.5.3 + +## [0.7.4] - Nov 13, 2018 +* Allow pod anti-affinity settings to include primary node + +## [0.7.3] - Nov 12, 2018 +* Support artifactory.preStartCommand for running command before entrypoint starts + +## [0.7.2] - Nov 7, 2018 +* Support database.url parameter (DB_URL) + +## [0.7.1] - Oct 29, 2018 +* Change probes port to 8040 (so they will not be blocked when all tomcat threads on 8081 are exhausted) + +## [0.7.0] - Oct 28, 2018 +* Update postgresql chart to version 0.9.5 to be able and use `postgresConfig` options + +## [0.6.9] - Oct 23, 2018 +* Fix providing external secret for database credentials + +## [0.6.8] - Oct 22, 2018 +* Allow user to configure externalTrafficPolicy for Loadbalancer + +## [0.6.7] - Oct 22, 2018 +* Updated ingress annotation support (with examples) to support docker registry v2 + +## [0.6.6] - Oct 21, 2018 +* Updated Artifactory version to 6.5.2 + +## [0.6.5] - Oct 19, 2018 +* Allow providing pre-existing secret containing master key +* Allow arbitrary annotations on primary and member node pods +* Enforce size limits when using local storage with `emptyDir` +* Allow `soft` or `hard` specification of member node anti-affinity +* Allow providing pre-existing secrets containing external database credentials +* Fix `s3` binary store provider to properly use the `cache-fs` provider +* Allow arbitrary properties when using the `s3` binary store provider + +## [0.6.4] - Oct 18, 2018 +* Updated Artifactory version to 6.5.1 + +## [0.6.3] - Oct 17, 2018 +* Add Apache 2.0 license + +## [0.6.2] - Oct 14, 2018 +* Make S3 endpoint configurable (was hardcoded with `s3.amazonaws.com`) + +## [0.6.1] - Oct 11, 2018 +* Allows ingress default `backend` to be enabled or disabled (defaults to enabled) + +## [0.6.0] - Oct 11, 2018 +* Updated Artifactory version to 6.5.0 + +## [0.5.3] - Oct 9, 2018 +* Quote ingress hosts to support wildcard names + +## [0.5.2] - Oct 2, 2018 +* Add `helm repo add jfrog https://charts.jfrog.io` to README + +## [0.5.1] - Oct 2, 2018 +* Set Artifactory to 6.4.1 + +## [0.5.0] - Sep 27, 2018 +* Set Artifactory to 6.4.0 + +## [0.4.7] - Sep 26, 2018 +* Add ci/test-values.yaml + +## [0.4.6] - Sep 25, 2018 +* Add PodDisruptionBudget for member nodes, defaulting to minAvailable of 1 + +## [0.4.4] - Sep 2, 2018 +* Updated Artifactory version to 6.3.2 + +## [0.4.0] - Aug 22, 2018 +* Added support to run as non root +* Updated Artifactory version to 6.2.0 + +## [0.3.0] - Aug 22, 2018 +* Enabled RBAC Support +* Added support for PostStartCommand (To download Database JDBC connector) +* Increased postgresql max_connections +* Added support for `nginx.conf` ConfigMap +* Updated Artifactory version to 6.1.0 diff --git a/charts/jfrog/artifactory-ha/107.104.10/Chart.lock b/charts/jfrog/artifactory-ha/107.104.10/Chart.lock new file mode 100644 index 0000000000..eb94099719 --- /dev/null +++ b/charts/jfrog/artifactory-ha/107.104.10/Chart.lock @@ -0,0 +1,6 @@ +dependencies: +- name: postgresql + repository: https://charts.jfrog.io/ + version: 10.3.18 +digest: sha256:404ce007353baaf92a6c5f24b249d5b336c232e5fd2c29f8a0e4d0095a09fd53 +generated: "2022-03-08T08:54:51.805126+05:30" diff --git a/charts/jfrog/artifactory-ha/107.104.10/Chart.yaml b/charts/jfrog/artifactory-ha/107.104.10/Chart.yaml new file mode 100644 index 0000000000..acda1e3b31 --- /dev/null +++ b/charts/jfrog/artifactory-ha/107.104.10/Chart.yaml @@ -0,0 +1,33 @@ +annotations: + artifactoryServiceVersion: 7.104.18 + catalog.cattle.io/certified: partner + catalog.cattle.io/display-name: JFrog Artifactory HA + catalog.cattle.io/featured: "2" + catalog.cattle.io/kube-version: '>= 1.19.0-0' + catalog.cattle.io/release-name: artifactory-ha + metadataVersion: 7.118.2 + observabilityVersion: 1.31.11 +apiVersion: v2 +appVersion: 7.104.10 +dependencies: +- condition: postgresql.enabled + name: postgresql + repository: https://charts.jfrog.io/ + version: 10.3.18 +description: Universal Repository Manager supporting all major packaging formats, + build tools and CI servers. +home: https://www.jfrog.com/artifactory/ +icon: file://assets/icons/artifactory-ha.png +keywords: +- artifactory +- jfrog +- devops +kubeVersion: '>= 1.19.0-0' +maintainers: +- email: installers@jfrog.com + name: Chart Maintainers at JFrog +name: artifactory-ha +sources: +- https://github.com/jfrog/charts +type: application +version: 107.104.10 diff --git a/charts/jfrog/artifactory-ha/107.104.10/LICENSE b/charts/jfrog/artifactory-ha/107.104.10/LICENSE new file mode 100644 index 0000000000..8dada3edaf --- /dev/null +++ b/charts/jfrog/artifactory-ha/107.104.10/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright {yyyy} {name of copyright owner} + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/charts/jfrog/artifactory-ha/107.104.10/README.md b/charts/jfrog/artifactory-ha/107.104.10/README.md new file mode 100644 index 0000000000..e0ef540164 --- /dev/null +++ b/charts/jfrog/artifactory-ha/107.104.10/README.md @@ -0,0 +1,69 @@ +# JFrog Artifactory High Availability Helm Chart + +**IMPORTANT!** Our Helm Chart docs have moved to our main documentation site. Below you will find the basic instructions for installing, uninstalling, and deleting Artifactory. For all other information, refer to [Installing Artifactory - Helm HA Installation](https://www.jfrog.com/confluence/display/JFROG/Installing+Artifactory#InstallingArtifactory-HelmHAInstallation). + +**Note:** From Artifactory 7.17.4 and above, the Helm HA installation can be installed so that each node you install can run all tasks in the cluster. + +Below you will find the basic instructions for installing, uninstalling, and deleting Artifactory. For all other information, refer to the documentation site. + +## Prerequisites Details + +* Kubernetes 1.19+ +* Artifactory HA license + +## Chart Details +This chart will do the following: + +* Deploy Artifactory highly available cluster. 3 primary nodes. +* Deploy a PostgreSQL database **NOTE:** For production grade installations it is recommended to use an external PostgreSQL +* Deploy an Nginx server + +## Installing the Chart + +### Add JFrog Helm repository + +Before installing JFrog helm charts, you need to add the [JFrog helm repository](https://charts.jfrog.io) to your helm client + +```bash +helm repo add jfrog https://charts.jfrog.io +``` +2. Next, create a unique Master Key (Artifactory requires a unique master key) and pass it to the template during installation. +3. Now, update the repository. + +```bash +helm repo update +``` + +### Install Chart +To install the chart with the release name `artifactory`: +```bash +helm upgrade --install artifactory-ha jfrog/artifactory-ha --namespace artifactory-ha --create-namespace +``` + +### Apply Sizing configurations to the Chart +To apply the chart with recommended sizing configurations : +For small configurations : +```bash +helm upgrade --install artifactory-ha jfrog/artifactory-ha -f sizing/artifactory-small.yaml --namespace artifactory-ha --create-namespace +``` + +## Uninstalling Artifactory + +Uninstall is supported only on Helm v3 and on. + +Uninstall Artifactory using the following command. + +```bash +helm uninstall artifactory-ha && sleep 90 && kubectl delete pvc -l app=artifactory-ha +``` + +## Deleting Artifactory + +**IMPORTANT:** Deleting Artifactory will also delete your data volumes and you will lose all of your data. You must back up all this information before deletion. You do not need to uninstall Artifactory before deleting it. + +To delete Artifactory use the following command. + +```bash +helm delete artifactory-ha --namespace artifactory-ha +``` + diff --git a/charts/jfrog/artifactory-ha/107.104.10/app-readme.md b/charts/jfrog/artifactory-ha/107.104.10/app-readme.md new file mode 100644 index 0000000000..a5aa5fd478 --- /dev/null +++ b/charts/jfrog/artifactory-ha/107.104.10/app-readme.md @@ -0,0 +1,16 @@ +# JFrog Artifactory High Availability Helm Chart + +Universal Repository Manager supporting all major packaging formats, build tools and CI servers. + +## Chart Details +This chart will do the following: + +* Deploy Artifactory highly available cluster. 1 primary node and 2 member nodes. +* Deploy a PostgreSQL database +* Deploy an Nginx server(optional) + +## Useful links +Blog: [Herd Trust Into Your Rancher Labs Multi-Cloud Strategy with Artifactory](https://jfrog.com/blog/herd-trust-into-your-rancher-labs-multi-cloud-strategy-with-artifactory/) + +## Activate Your Artifactory Instance +Don't have a license? Please send an email to [rancher-jfrog-licenses@jfrog.com](mailto:rancher-jfrog-licenses@jfrog.com) to get it. diff --git a/charts/jfrog/artifactory-ha/107.104.10/charts/postgresql/.helmignore b/charts/jfrog/artifactory-ha/107.104.10/charts/postgresql/.helmignore new file mode 100644 index 0000000000..f0c1319444 --- /dev/null +++ b/charts/jfrog/artifactory-ha/107.104.10/charts/postgresql/.helmignore @@ -0,0 +1,21 @@ +# 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 diff --git a/charts/jfrog/artifactory-ha/107.104.10/charts/postgresql/Chart.lock b/charts/jfrog/artifactory-ha/107.104.10/charts/postgresql/Chart.lock new file mode 100644 index 0000000000..3687f52df5 --- /dev/null +++ b/charts/jfrog/artifactory-ha/107.104.10/charts/postgresql/Chart.lock @@ -0,0 +1,6 @@ +dependencies: +- name: common + repository: https://charts.bitnami.com/bitnami + version: 1.4.2 +digest: sha256:dce0349883107e3ff103f4f17d3af4ad1ea3c7993551b1c28865867d3e53d37c +generated: "2021-03-30T09:13:28.360322819Z" diff --git a/charts/jfrog/artifactory-ha/107.104.10/charts/postgresql/Chart.yaml b/charts/jfrog/artifactory-ha/107.104.10/charts/postgresql/Chart.yaml new file mode 100644 index 0000000000..4b197b2071 --- /dev/null +++ b/charts/jfrog/artifactory-ha/107.104.10/charts/postgresql/Chart.yaml @@ -0,0 +1,29 @@ +annotations: + category: Database +apiVersion: v2 +appVersion: 11.11.0 +dependencies: +- name: common + repository: https://charts.bitnami.com/bitnami + version: 1.x.x +description: Chart for PostgreSQL, an object-relational database management system + (ORDBMS) with an emphasis on extensibility and on standards-compliance. +home: https://github.com/bitnami/charts/tree/master/bitnami/postgresql +icon: https://bitnami.com/assets/stacks/postgresql/img/postgresql-stack-220x234.png +keywords: +- postgresql +- postgres +- database +- sql +- replication +- cluster +maintainers: +- email: containers@bitnami.com + name: Bitnami +- email: cedric@desaintmartin.fr + name: desaintmartin +name: postgresql +sources: +- https://github.com/bitnami/bitnami-docker-postgresql +- https://www.postgresql.org/ +version: 10.3.18 diff --git a/charts/jfrog/artifactory-ha/107.104.10/charts/postgresql/README.md b/charts/jfrog/artifactory-ha/107.104.10/charts/postgresql/README.md new file mode 100644 index 0000000000..63d3605bb8 --- /dev/null +++ b/charts/jfrog/artifactory-ha/107.104.10/charts/postgresql/README.md @@ -0,0 +1,770 @@ +# PostgreSQL + +[PostgreSQL](https://www.postgresql.org/) is an object-relational database management system (ORDBMS) with an emphasis on extensibility and on standards-compliance. + +For HA, please see [this repo](https://github.com/bitnami/charts/tree/master/bitnami/postgresql-ha) + +## TL;DR + +```console +$ helm repo add bitnami https://charts.bitnami.com/bitnami +$ helm install my-release bitnami/postgresql +``` + +## Introduction + +This chart bootstraps a [PostgreSQL](https://github.com/bitnami/bitnami-docker-postgresql) deployment on a [Kubernetes](http://kubernetes.io) cluster using the [Helm](https://helm.sh) package manager. + +Bitnami charts can be used with [Kubeapps](https://kubeapps.com/) for deployment and management of Helm Charts in clusters. This chart has been tested to work with NGINX Ingress, cert-manager, fluentd and Prometheus on top of the [BKPR](https://kubeprod.io/). + +## Prerequisites + +- Kubernetes 1.12+ +- Helm 3.1.0 +- PV provisioner support in the underlying infrastructure + +## Installing the Chart +To install the chart with the release name `my-release`: + +```console +$ helm install my-release bitnami/postgresql +``` + +The command deploys PostgreSQL on the Kubernetes cluster in the default configuration. The [Parameters](#parameters) section lists the parameters that can be configured during installation. + +> **Tip**: List all releases using `helm list` + +## Uninstalling the Chart + +To uninstall/delete the `my-release` deployment: + +```console +$ helm delete my-release +``` + +The command removes all the Kubernetes components but PVC's associated with the chart and deletes the release. + +To delete the PVC's associated with `my-release`: + +```console +$ kubectl delete pvc -l release=my-release +``` + +> **Note**: Deleting the PVC's will delete postgresql data as well. Please be cautious before doing it. + +## Parameters + +The following tables lists the configurable parameters of the PostgreSQL chart and their default values. + +| Parameter | Description | Default | +|-----------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------------------------------------------------------------| +| `global.imageRegistry` | Global Docker Image registry | `nil` | +| `global.postgresql.postgresqlDatabase` | PostgreSQL database (overrides `postgresqlDatabase`) | `nil` | +| `global.postgresql.postgresqlUsername` | PostgreSQL username (overrides `postgresqlUsername`) | `nil` | +| `global.postgresql.existingSecret` | Name of existing secret to use for PostgreSQL passwords (overrides `existingSecret`) | `nil` | +| `global.postgresql.postgresqlPassword` | PostgreSQL admin password (overrides `postgresqlPassword`) | `nil` | +| `global.postgresql.servicePort` | PostgreSQL port (overrides `service.port`) | `nil` | +| `global.postgresql.replicationPassword` | Replication user password (overrides `replication.password`) | `nil` | +| `global.imagePullSecrets` | Global Docker registry secret names as an array | `[]` (does not add image pull secrets to deployed pods) | +| `global.storageClass` | Global storage class for dynamic provisioning | `nil` | +| `image.registry` | PostgreSQL Image registry | `docker.io` | +| `image.repository` | PostgreSQL Image name | `bitnami/postgresql` | +| `image.tag` | PostgreSQL Image tag | `{TAG_NAME}` | +| `image.pullPolicy` | PostgreSQL Image pull policy | `IfNotPresent` | +| `image.pullSecrets` | Specify Image pull secrets | `nil` (does not add image pull secrets to deployed pods) | +| `image.debug` | Specify if debug values should be set | `false` | +| `nameOverride` | String to partially override common.names.fullname template with a string (will prepend the release name) | `nil` | +| `fullnameOverride` | String to fully override common.names.fullname template with a string | `nil` | +| `volumePermissions.enabled` | Enable init container that changes volume permissions in the data directory (for cases where the default k8s `runAsUser` and `fsUser` values do not work) | `false` | +| `volumePermissions.image.registry` | Init container volume-permissions image registry | `docker.io` | +| `volumePermissions.image.repository` | Init container volume-permissions image name | `bitnami/bitnami-shell` | +| `volumePermissions.image.tag` | Init container volume-permissions image tag | `"10"` | +| `volumePermissions.image.pullPolicy` | Init container volume-permissions image pull policy | `Always` | +| `volumePermissions.securityContext.*` | Other container security context to be included as-is in the container spec | `{}` | +| `volumePermissions.securityContext.runAsUser` | User ID for the init container (when facing issues in OpenShift or uid unknown, try value "auto") | `0` | +| `usePasswordFile` | Have the secrets mounted as a file instead of env vars | `false` | +| `ldap.enabled` | Enable LDAP support | `false` | +| `ldap.existingSecret` | Name of existing secret to use for LDAP passwords | `nil` | +| `ldap.url` | LDAP URL beginning in the form `ldap[s]://host[:port]/basedn[?[attribute][?[scope][?[filter]]]]` | `nil` | +| `ldap.server` | IP address or name of the LDAP server. | `nil` | +| `ldap.port` | Port number on the LDAP server to connect to | `nil` | +| `ldap.scheme` | Set to `ldaps` to use LDAPS. | `nil` | +| `ldap.tls` | Set to `1` to use TLS encryption | `nil` | +| `ldap.prefix` | String to prepend to the user name when forming the DN to bind | `nil` | +| `ldap.suffix` | String to append to the user name when forming the DN to bind | `nil` | +| `ldap.search_attr` | Attribute to match against the user name in the search | `nil` | +| `ldap.search_filter` | The search filter to use when doing search+bind authentication | `nil` | +| `ldap.baseDN` | Root DN to begin the search for the user in | `nil` | +| `ldap.bindDN` | DN of user to bind to LDAP | `nil` | +| `ldap.bind_password` | Password for the user to bind to LDAP | `nil` | +| `replication.enabled` | Enable replication | `false` | +| `replication.user` | Replication user | `repl_user` | +| `replication.password` | Replication user password | `repl_password` | +| `replication.readReplicas` | Number of read replicas replicas | `1` | +| `replication.synchronousCommit` | Set synchronous commit mode. Allowed values: `on`, `remote_apply`, `remote_write`, `local` and `off` | `off` | +| `replication.numSynchronousReplicas` | Number of replicas that will have synchronous replication. Note: Cannot be greater than `replication.readReplicas`. | `0` | +| `replication.applicationName` | Cluster application name. Useful for advanced replication settings | `my_application` | +| `existingSecret` | Name of existing secret to use for PostgreSQL passwords. The secret has to contain the keys `postgresql-password` which is the password for `postgresqlUsername` when it is different of `postgres`, `postgresql-postgres-password` which will override `postgresqlPassword`, `postgresql-replication-password` which will override `replication.password` and `postgresql-ldap-password` which will be used to authenticate on LDAP. The value is evaluated as a template. | `nil` | +| `postgresqlPostgresPassword` | PostgreSQL admin password (used when `postgresqlUsername` is not `postgres`, in which case`postgres` is the admin username). | _random 10 character alphanumeric string_ | +| `postgresqlUsername` | PostgreSQL user (creates a non-admin user when `postgresqlUsername` is not `postgres`) | `postgres` | +| `postgresqlPassword` | PostgreSQL user password | _random 10 character alphanumeric string_ | +| `postgresqlDatabase` | PostgreSQL database | `nil` | +| `postgresqlDataDir` | PostgreSQL data dir folder | `/bitnami/postgresql` (same value as persistence.mountPath) | +| `extraEnv` | Any extra environment variables you would like to pass on to the pod. The value is evaluated as a template. | `[]` | +| `extraEnvVarsCM` | Name of a Config Map containing extra environment variables you would like to pass on to the pod. The value is evaluated as a template. | `nil` | +| `postgresqlInitdbArgs` | PostgreSQL initdb extra arguments | `nil` | +| `postgresqlInitdbWalDir` | PostgreSQL location for transaction log | `nil` | +| `postgresqlConfiguration` | Runtime Config Parameters | `nil` | +| `postgresqlExtendedConf` | Extended Runtime Config Parameters (appended to main or default configuration) | `nil` | +| `pgHbaConfiguration` | Content of pg_hba.conf | `nil (do not create pg_hba.conf)` | +| `postgresqlSharedPreloadLibraries` | Shared preload libraries (comma-separated list) | `pgaudit` | +| `postgresqlMaxConnections` | Maximum total connections | `nil` | +| `postgresqlPostgresConnectionLimit` | Maximum total connections for the postgres user | `nil` | +| `postgresqlDbUserConnectionLimit` | Maximum total connections for the non-admin user | `nil` | +| `postgresqlTcpKeepalivesInterval` | TCP keepalives interval | `nil` | +| `postgresqlTcpKeepalivesIdle` | TCP keepalives idle | `nil` | +| `postgresqlTcpKeepalivesCount` | TCP keepalives count | `nil` | +| `postgresqlStatementTimeout` | Statement timeout | `nil` | +| `postgresqlPghbaRemoveFilters` | Comma-separated list of patterns to remove from the pg_hba.conf file | `nil` | +| `customStartupProbe` | Override default startup probe | `nil` | +| `customLivenessProbe` | Override default liveness probe | `nil` | +| `customReadinessProbe` | Override default readiness probe | `nil` | +| `audit.logHostname` | Add client hostnames to the log file | `false` | +| `audit.logConnections` | Add client log-in operations to the log file | `false` | +| `audit.logDisconnections` | Add client log-outs operations to the log file | `false` | +| `audit.pgAuditLog` | Add operations to log using the pgAudit extension | `nil` | +| `audit.clientMinMessages` | Message log level to share with the user | `nil` | +| `audit.logLinePrefix` | Template string for the log line prefix | `nil` | +| `audit.logTimezone` | Timezone for the log timestamps | `nil` | +| `configurationConfigMap` | ConfigMap with the PostgreSQL configuration files (Note: Overrides `postgresqlConfiguration` and `pgHbaConfiguration`). The value is evaluated as a template. | `nil` | +| `extendedConfConfigMap` | ConfigMap with the extended PostgreSQL configuration files. The value is evaluated as a template. | `nil` | +| `initdbScripts` | Dictionary of initdb scripts | `nil` | +| `initdbUser` | PostgreSQL user to execute the .sql and sql.gz scripts | `nil` | +| `initdbPassword` | Password for the user specified in `initdbUser` | `nil` | +| `initdbScriptsConfigMap` | ConfigMap with the initdb scripts (Note: Overrides `initdbScripts`). The value is evaluated as a template. | `nil` | +| `initdbScriptsSecret` | Secret with initdb scripts that contain sensitive information (Note: can be used with `initdbScriptsConfigMap` or `initdbScripts`). The value is evaluated as a template. | `nil` | +| `service.type` | Kubernetes Service type | `ClusterIP` | +| `service.port` | PostgreSQL port | `5432` | +| `service.nodePort` | Kubernetes Service nodePort | `nil` | +| `service.annotations` | Annotations for PostgreSQL service | `{}` (evaluated as a template) | +| `service.loadBalancerIP` | loadBalancerIP if service type is `LoadBalancer` | `nil` | +| `service.loadBalancerSourceRanges` | Address that are allowed when svc is LoadBalancer | `[]` (evaluated as a template) | +| `schedulerName` | Name of the k8s scheduler (other than default) | `nil` | +| `shmVolume.enabled` | Enable emptyDir volume for /dev/shm for primary and read replica(s) Pod(s) | `true` | +| `shmVolume.chmod.enabled` | Run at init chmod 777 of the /dev/shm (ignored if `volumePermissions.enabled` is `false`) | `true` | +| `persistence.enabled` | Enable persistence using PVC | `true` | +| `persistence.existingClaim` | Provide an existing `PersistentVolumeClaim`, the value is evaluated as a template. | `nil` | +| `persistence.mountPath` | Path to mount the volume at | `/bitnami/postgresql` | +| `persistence.subPath` | Subdirectory of the volume to mount at | `""` | +| `persistence.storageClass` | PVC Storage Class for PostgreSQL volume | `nil` | +| `persistence.accessModes` | PVC Access Mode for PostgreSQL volume | `[ReadWriteOnce]` | +| `persistence.size` | PVC Storage Request for PostgreSQL volume | `8Gi` | +| `persistence.annotations` | Annotations for the PVC | `{}` | +| `persistence.selector` | Selector to match an existing Persistent Volume (this value is evaluated as a template) | `{}` | +| `commonAnnotations` | Annotations to be added to all deployed resources (rendered as a template) | `{}` | +| `primary.podAffinityPreset` | PostgreSQL primary pod affinity preset. Ignored if `primary.affinity` is set. Allowed values: `soft` or `hard` | `""` | +| `primary.podAntiAffinityPreset` | PostgreSQL primary pod anti-affinity preset. Ignored if `primary.affinity` is set. Allowed values: `soft` or `hard` | `soft` | +| `primary.nodeAffinityPreset.type` | PostgreSQL primary node affinity preset type. Ignored if `primary.affinity` is set. Allowed values: `soft` or `hard` | `""` | +| `primary.nodeAffinityPreset.key` | PostgreSQL primary node label key to match Ignored if `primary.affinity` is set. | `""` | +| `primary.nodeAffinityPreset.values` | PostgreSQL primary node label values to match. Ignored if `primary.affinity` is set. | `[]` | +| `primary.affinity` | Affinity for PostgreSQL primary pods assignment | `{}` (evaluated as a template) | +| `primary.nodeSelector` | Node labels for PostgreSQL primary pods assignment | `{}` (evaluated as a template) | +| `primary.tolerations` | Tolerations for PostgreSQL primary pods assignment | `[]` (evaluated as a template) | +| `primary.anotations` | Map of annotations to add to the statefulset (postgresql primary) | `{}` | +| `primary.labels` | Map of labels to add to the statefulset (postgresql primary) | `{}` | +| `primary.podAnnotations` | Map of annotations to add to the pods (postgresql primary) | `{}` | +| `primary.podLabels` | Map of labels to add to the pods (postgresql primary) | `{}` | +| `primary.priorityClassName` | Priority Class to use for each pod (postgresql primary) | `nil` | +| `primary.extraInitContainers` | Additional init containers to add to the pods (postgresql primary) | `[]` | +| `primary.extraVolumeMounts` | Additional volume mounts to add to the pods (postgresql primary) | `[]` | +| `primary.extraVolumes` | Additional volumes to add to the pods (postgresql primary) | `[]` | +| `primary.sidecars` | Add additional containers to the pod | `[]` | +| `primary.service.type` | Allows using a different service type for primary | `nil` | +| `primary.service.nodePort` | Allows using a different nodePort for primary | `nil` | +| `primary.service.clusterIP` | Allows using a different clusterIP for primary | `nil` | +| `primaryAsStandBy.enabled` | Whether to enable current cluster's primary as standby server of another cluster or not. | `false` | +| `primaryAsStandBy.primaryHost` | The Host of replication primary in the other cluster. | `nil` | +| `primaryAsStandBy.primaryPort ` | The Port of replication primary in the other cluster. | `nil` | +| `readReplicas.podAffinityPreset` | PostgreSQL read only pod affinity preset. Ignored if `primary.affinity` is set. Allowed values: `soft` or `hard` | `""` | +| `readReplicas.podAntiAffinityPreset` | PostgreSQL read only pod anti-affinity preset. Ignored if `primary.affinity` is set. Allowed values: `soft` or `hard` | `soft` | +| `readReplicas.nodeAffinityPreset.type` | PostgreSQL read only node affinity preset type. Ignored if `primary.affinity` is set. Allowed values: `soft` or `hard` | `""` | +| `readReplicas.nodeAffinityPreset.key` | PostgreSQL read only node label key to match Ignored if `primary.affinity` is set. | `""` | +| `readReplicas.nodeAffinityPreset.values` | PostgreSQL read only node label values to match. Ignored if `primary.affinity` is set. | `[]` | +| `readReplicas.affinity` | Affinity for PostgreSQL read only pods assignment | `{}` (evaluated as a template) | +| `readReplicas.nodeSelector` | Node labels for PostgreSQL read only pods assignment | `{}` (evaluated as a template) | +| `readReplicas.anotations` | Map of annotations to add to the statefulsets (postgresql readReplicas) | `{}` | +| `readReplicas.resources` | CPU/Memory resource requests/limits override for readReplicass. Will fallback to `values.resources` if not defined. | `{}` | +| `readReplicas.labels` | Map of labels to add to the statefulsets (postgresql readReplicas) | `{}` | +| `readReplicas.podAnnotations` | Map of annotations to add to the pods (postgresql readReplicas) | `{}` | +| `readReplicas.podLabels` | Map of labels to add to the pods (postgresql readReplicas) | `{}` | +| `readReplicas.priorityClassName` | Priority Class to use for each pod (postgresql readReplicas) | `nil` | +| `readReplicas.extraInitContainers` | Additional init containers to add to the pods (postgresql readReplicas) | `[]` | +| `readReplicas.extraVolumeMounts` | Additional volume mounts to add to the pods (postgresql readReplicas) | `[]` | +| `readReplicas.extraVolumes` | Additional volumes to add to the pods (postgresql readReplicas) | `[]` | +| `readReplicas.sidecars` | Add additional containers to the pod | `[]` | +| `readReplicas.service.type` | Allows using a different service type for readReplicas | `nil` | +| `readReplicas.service.nodePort` | Allows using a different nodePort for readReplicas | `nil` | +| `readReplicas.service.clusterIP` | Allows using a different clusterIP for readReplicas | `nil` | +| `readReplicas.persistence.enabled` | Whether to enable readReplicas replicas persistence | `true` | +| `terminationGracePeriodSeconds` | Seconds the pod needs to terminate gracefully | `nil` | +| `resources` | CPU/Memory resource requests/limits | Memory: `256Mi`, CPU: `250m` | +| `securityContext.*` | Other pod security context to be included as-is in the pod spec | `{}` | +| `securityContext.enabled` | Enable security context | `true` | +| `securityContext.fsGroup` | Group ID for the pod | `1001` | +| `containerSecurityContext.*` | Other container security context to be included as-is in the container spec | `{}` | +| `containerSecurityContext.enabled` | Enable container security context | `true` | +| `containerSecurityContext.runAsUser` | User ID for the container | `1001` | +| `serviceAccount.enabled` | Enable service account (Note: Service Account will only be automatically created if `serviceAccount.name` is not set) | `false` | +| `serviceAccount.name` | Name of existing service account | `nil` | +| `networkPolicy.enabled` | Enable NetworkPolicy | `false` | +| `networkPolicy.allowExternal` | Don't require client label for connections | `true` | +| `networkPolicy.explicitNamespacesSelector` | A Kubernetes LabelSelector to explicitly select namespaces from which ingress traffic could be allowed | `{}` | +| `startupProbe.enabled` | Enable startupProbe | `false` | +| `startupProbe.initialDelaySeconds` | Delay before liveness probe is initiated | 30 | +| `startupProbe.periodSeconds` | How often to perform the probe | 15 | +| `startupProbe.timeoutSeconds` | When the probe times | 5 | +| `startupProbe.failureThreshold` | Minimum consecutive failures for the probe to be considered failed after having succeeded. | 10 | +| `startupProbe.successThreshold` | Minimum consecutive successes for the probe to be considered successful after having failed. | 1 | +| `livenessProbe.enabled` | Enable livenessProbe | `true` | +| `livenessProbe.initialDelaySeconds` | Delay before liveness probe is initiated | 30 | +| `livenessProbe.periodSeconds` | How often to perform the probe | 10 | +| `livenessProbe.timeoutSeconds` | When the probe times out | 5 | +| `livenessProbe.failureThreshold` | Minimum consecutive failures for the probe to be considered failed after having succeeded. | 6 | +| `livenessProbe.successThreshold` | Minimum consecutive successes for the probe to be considered successful after having failed | 1 | +| `readinessProbe.enabled` | Enable readinessProbe | `true` | +| `readinessProbe.initialDelaySeconds` | Delay before readiness probe is initiated | 5 | +| `readinessProbe.periodSeconds` | How often to perform the probe | 10 | +| `readinessProbe.timeoutSeconds` | When the probe times out | 5 | +| `readinessProbe.failureThreshold` | Minimum consecutive failures for the probe to be considered failed after having succeeded. | 6 | +| `readinessProbe.successThreshold` | Minimum consecutive successes for the probe to be considered successful after having failed | 1 | +| `tls.enabled` | Enable TLS traffic support | `false` | +| `tls.preferServerCiphers` | Whether to use the server's TLS cipher preferences rather than the client's | `true` | +| `tls.certificatesSecret` | Name of an existing secret that contains the certificates | `nil` | +| `tls.certFilename` | Certificate filename | `""` | +| `tls.certKeyFilename` | Certificate key filename | `""` | +| `tls.certCAFilename` | CA Certificate filename. If provided, PostgreSQL will authenticate TLS/SSL clients by requesting them a certificate. | `nil` | +| `tls.crlFilename` | File containing a Certificate Revocation List | `nil` | +| `metrics.enabled` | Start a prometheus exporter | `false` | +| `metrics.service.type` | Kubernetes Service type | `ClusterIP` | +| `service.clusterIP` | Static clusterIP or None for headless services | `nil` | +| `metrics.service.annotations` | Additional annotations for metrics exporter pod | `{ prometheus.io/scrape: "true", prometheus.io/port: "9187"}` | +| `metrics.service.loadBalancerIP` | loadBalancerIP if redis metrics service type is `LoadBalancer` | `nil` | +| `metrics.serviceMonitor.enabled` | Set this to `true` to create ServiceMonitor for Prometheus operator | `false` | +| `metrics.serviceMonitor.additionalLabels` | Additional labels that can be used so ServiceMonitor will be discovered by Prometheus | `{}` | +| `metrics.serviceMonitor.namespace` | Optional namespace in which to create ServiceMonitor | `nil` | +| `metrics.serviceMonitor.interval` | Scrape interval. If not set, the Prometheus default scrape interval is used | `nil` | +| `metrics.serviceMonitor.scrapeTimeout` | Scrape timeout. If not set, the Prometheus default scrape timeout is used | `nil` | +| `metrics.prometheusRule.enabled` | Set this to true to create prometheusRules for Prometheus operator | `false` | +| `metrics.prometheusRule.additionalLabels` | Additional labels that can be used so prometheusRules will be discovered by Prometheus | `{}` | +| `metrics.prometheusRule.namespace` | namespace where prometheusRules resource should be created | the same namespace as postgresql | +| `metrics.prometheusRule.rules` | [rules](https://prometheus.io/docs/prometheus/latest/configuration/alerting_rules/) to be created, check values for an example. | `[]` | +| `metrics.image.registry` | PostgreSQL Exporter Image registry | `docker.io` | +| `metrics.image.repository` | PostgreSQL Exporter Image name | `bitnami/postgres-exporter` | +| `metrics.image.tag` | PostgreSQL Exporter Image tag | `{TAG_NAME}` | +| `metrics.image.pullPolicy` | PostgreSQL Exporter Image pull policy | `IfNotPresent` | +| `metrics.image.pullSecrets` | Specify Image pull secrets | `nil` (does not add image pull secrets to deployed pods) | +| `metrics.customMetrics` | Additional custom metrics | `nil` | +| `metrics.extraEnvVars` | Extra environment variables to add to exporter | `{}` (evaluated as a template) | +| `metrics.securityContext.*` | Other container security context to be included as-is in the container spec | `{}` | +| `metrics.securityContext.enabled` | Enable security context for metrics | `false` | +| `metrics.securityContext.runAsUser` | User ID for the container for metrics | `1001` | +| `metrics.livenessProbe.initialDelaySeconds` | Delay before liveness probe is initiated | 30 | +| `metrics.livenessProbe.periodSeconds` | How often to perform the probe | 10 | +| `metrics.livenessProbe.timeoutSeconds` | When the probe times out | 5 | +| `metrics.livenessProbe.failureThreshold` | Minimum consecutive failures for the probe to be considered failed after having succeeded. | 6 | +| `metrics.livenessProbe.successThreshold` | Minimum consecutive successes for the probe to be considered successful after having failed | 1 | +| `metrics.readinessProbe.enabled` | would you like a readinessProbe to be enabled | `true` | +| `metrics.readinessProbe.initialDelaySeconds` | Delay before liveness probe is initiated | 5 | +| `metrics.readinessProbe.periodSeconds` | How often to perform the probe | 10 | +| `metrics.readinessProbe.timeoutSeconds` | When the probe times out | 5 | +| `metrics.readinessProbe.failureThreshold` | Minimum consecutive failures for the probe to be considered failed after having succeeded. | 6 | +| `metrics.readinessProbe.successThreshold` | Minimum consecutive successes for the probe to be considered successful after having failed | 1 | +| `updateStrategy` | Update strategy policy | `{type: "RollingUpdate"}` | +| `psp.create` | Create Pod Security Policy | `false` | +| `rbac.create` | Create Role and RoleBinding (required for PSP to work) | `false` | +| `extraDeploy` | Array of extra objects to deploy with the release (evaluated as a template). | `nil` | + +Specify each parameter using the `--set key=value[,key=value]` argument to `helm install`. For example, + +```console +$ helm install my-release \ + --set postgresqlPassword=secretpassword,postgresqlDatabase=my-database \ + bitnami/postgresql +``` + +The above command sets the PostgreSQL `postgres` account password to `secretpassword`. Additionally it creates a database named `my-database`. + +> NOTE: Once this chart is deployed, it is not possible to change the application's access credentials, such as usernames or passwords, using Helm. To change these application credentials after deployment, delete any persistent volumes (PVs) used by the chart and re-deploy it, or use the application's built-in administrative tools if available. + +Alternatively, a YAML file that specifies the values for the parameters can be provided while installing the chart. For example, + +```console +$ helm install my-release -f values.yaml bitnami/postgresql +``` + +> **Tip**: You can use the default [values.yaml](values.yaml) + +## Configuration and installation details + +### [Rolling VS Immutable tags](https://docs.bitnami.com/containers/how-to/understand-rolling-tags-containers/) + +It is strongly recommended to use immutable tags in a production environment. This ensures your deployment does not change automatically if the same tag is updated with a different image. + +Bitnami will release a new chart updating its containers if a new version of the main container, significant changes, or critical vulnerabilities exist. + +### Customizing primary and read replica services in a replicated configuration + +At the top level, there is a service object which defines the services for both primary and readReplicas. For deeper customization, there are service objects for both the primary and read types individually. This allows you to override the values in the top level service object so that the primary and read can be of different service types and with different clusterIPs / nodePorts. Also in the case you want the primary and read to be of type nodePort, you will need to set the nodePorts to different values to prevent a collision. The values that are deeper in the primary.service or readReplicas.service objects will take precedence over the top level service object. + +### Change PostgreSQL version + +To modify the PostgreSQL version used in this chart you can specify a [valid image tag](https://hub.docker.com/r/bitnami/postgresql/tags/) using the `image.tag` parameter. For example, `image.tag=X.Y.Z`. This approach is also applicable to other images like exporters. + +### postgresql.conf / pg_hba.conf files as configMap + +This helm chart also supports to customize the whole configuration file. + +Add your custom file to "files/postgresql.conf" in your working directory. This file will be mounted as configMap to the containers and it will be used for configuring the PostgreSQL server. + +Alternatively, you can add additional PostgreSQL configuration parameters using the `postgresqlExtendedConf` parameter as a dict, using camelCase, e.g. {"sharedBuffers": "500MB"}. Alternatively, to replace the entire default configuration use `postgresqlConfiguration`. + +In addition to these options, you can also set an external ConfigMap with all the configuration files. This is done by setting the `configurationConfigMap` parameter. Note that this will override the two previous options. + +### Allow settings to be loaded from files other than the default `postgresql.conf` + +If you don't want to provide the whole PostgreSQL configuration file and only specify certain parameters, you can add your extended `.conf` files to "files/conf.d/" in your working directory. +Those files will be mounted as configMap to the containers adding/overwriting the default configuration using the `include_dir` directive that allows settings to be loaded from files other than the default `postgresql.conf`. + +Alternatively, you can also set an external ConfigMap with all the extra configuration files. This is done by setting the `extendedConfConfigMap` parameter. Note that this will override the previous option. + +### Initialize a fresh instance + +The [Bitnami PostgreSQL](https://github.com/bitnami/bitnami-docker-postgresql) image allows you to use your custom scripts to initialize a fresh instance. In order to execute the scripts, they must be located inside the chart folder `files/docker-entrypoint-initdb.d` so they can be consumed as a ConfigMap. + +Alternatively, you can specify custom scripts using the `initdbScripts` parameter as dict. + +In addition to these options, you can also set an external ConfigMap with all the initialization scripts. This is done by setting the `initdbScriptsConfigMap` parameter. Note that this will override the two previous options. If your initialization scripts contain sensitive information such as credentials or passwords, you can use the `initdbScriptsSecret` parameter. + +The allowed extensions are `.sh`, `.sql` and `.sql.gz`. + +### Securing traffic using TLS + +TLS support can be enabled in the chart by specifying the `tls.` parameters while creating a release. The following parameters should be configured to properly enable the TLS support in the chart: + +- `tls.enabled`: Enable TLS support. Defaults to `false` +- `tls.certificatesSecret`: Name of an existing secret that contains the certificates. No defaults. +- `tls.certFilename`: Certificate filename. No defaults. +- `tls.certKeyFilename`: Certificate key filename. No defaults. + +For example: + +* First, create the secret with the cetificates files: + + ```console + kubectl create secret generic certificates-tls-secret --from-file=./cert.crt --from-file=./cert.key --from-file=./ca.crt + ``` + +* Then, use the following parameters: + + ```console + volumePermissions.enabled=true + tls.enabled=true + tls.certificatesSecret="certificates-tls-secret" + tls.certFilename="cert.crt" + tls.certKeyFilename="cert.key" + ``` + + > Note TLS and VolumePermissions: PostgreSQL requires certain permissions on sensitive files (such as certificate keys) to start up. Due to an on-going [issue](https://github.com/kubernetes/kubernetes/issues/57923) regarding kubernetes permissions and the use of `containerSecurityContext.runAsUser`, you must enable `volumePermissions` to ensure everything works as expected. + +### Sidecars + +If you need additional containers to run within the same pod as PostgreSQL (e.g. an additional metrics or logging exporter), you can do so via the `sidecars` config parameter. Simply define your container according to the Kubernetes container spec. + +```yaml +# For the PostgreSQL primary +primary: + sidecars: + - name: your-image-name + image: your-image + imagePullPolicy: Always + ports: + - name: portname + containerPort: 1234 +# For the PostgreSQL replicas +readReplicas: + sidecars: + - name: your-image-name + image: your-image + imagePullPolicy: Always + ports: + - name: portname + containerPort: 1234 +``` + +### Metrics + +The chart optionally can start a metrics exporter for [prometheus](https://prometheus.io). The metrics endpoint (port 9187) is not exposed and it is expected that the metrics are collected from inside the k8s cluster using something similar as the described in the [example Prometheus scrape configuration](https://github.com/prometheus/prometheus/blob/master/documentation/examples/prometheus-kubernetes.yml). + +The exporter allows to create custom metrics from additional SQL queries. See the Chart's `values.yaml` for an example and consult the [exporters documentation](https://github.com/wrouesnel/postgres_exporter#adding-new-metrics-via-a-config-file) for more details. + +### Use of global variables + +In more complex scenarios, we may have the following tree of dependencies + +``` + +--------------+ + | | + +------------+ Chart 1 +-----------+ + | | | | + | --------+------+ | + | | | + | | | + | | | + | | | + v v v ++-------+------+ +--------+------+ +--------+------+ +| | | | | | +| PostgreSQL | | Sub-chart 1 | | Sub-chart 2 | +| | | | | | ++--------------+ +---------------+ +---------------+ +``` + +The three charts below depend on the parent chart Chart 1. However, subcharts 1 and 2 may need to connect to PostgreSQL as well. In order to do so, subcharts 1 and 2 need to know the PostgreSQL credentials, so one option for deploying could be deploy Chart 1 with the following parameters: + +``` +postgresql.postgresqlPassword=testtest +subchart1.postgresql.postgresqlPassword=testtest +subchart2.postgresql.postgresqlPassword=testtest +postgresql.postgresqlDatabase=db1 +subchart1.postgresql.postgresqlDatabase=db1 +subchart2.postgresql.postgresqlDatabase=db1 +``` + +If the number of dependent sub-charts increases, installing the chart with parameters can become increasingly difficult. An alternative would be to set the credentials using global variables as follows: + +``` +global.postgresql.postgresqlPassword=testtest +global.postgresql.postgresqlDatabase=db1 +``` + +This way, the credentials will be available in all of the subcharts. + +## Persistence + +The [Bitnami PostgreSQL](https://github.com/bitnami/bitnami-docker-postgresql) image stores the PostgreSQL data and configurations at the `/bitnami/postgresql` path of the container. + +Persistent Volume Claims are used to keep the data across deployments. This is known to work in GCE, AWS, and minikube. +See the [Parameters](#parameters) section to configure the PVC or to disable persistence. + +If you already have data in it, you will fail to sync to standby nodes for all commits, details can refer to [code](https://github.com/bitnami/bitnami-docker-postgresql/blob/8725fe1d7d30ebe8d9a16e9175d05f7ad9260c93/9.6/debian-9/rootfs/libpostgresql.sh#L518-L556). If you need to use those data, please covert them to sql and import after `helm install` finished. + +## NetworkPolicy + +To enable network policy for PostgreSQL, install [a networking plugin that implements the Kubernetes NetworkPolicy spec](https://kubernetes.io/docs/tasks/administer-cluster/declare-network-policy#before-you-begin), and set `networkPolicy.enabled` to `true`. + +For Kubernetes v1.5 & v1.6, you must also turn on NetworkPolicy by setting the DefaultDeny namespace annotation. Note: this will enforce policy for _all_ pods in the namespace: + +```console +$ kubectl annotate namespace default "net.beta.kubernetes.io/network-policy={\"ingress\":{\"isolation\":\"DefaultDeny\"}}" +``` + +With NetworkPolicy enabled, traffic will be limited to just port 5432. + +For more precise policy, set `networkPolicy.allowExternal=false`. This will only allow pods with the generated client label to connect to PostgreSQL. +This label will be displayed in the output of a successful install. + +## Differences between Bitnami PostgreSQL image and [Docker Official](https://hub.docker.com/_/postgres) image + +- The Docker Official PostgreSQL image does not support replication. If you pass any replication environment variable, this would be ignored. The only environment variables supported by the Docker Official image are POSTGRES_USER, POSTGRES_DB, POSTGRES_PASSWORD, POSTGRES_INITDB_ARGS, POSTGRES_INITDB_WALDIR and PGDATA. All the remaining environment variables are specific to the Bitnami PostgreSQL image. +- The Bitnami PostgreSQL image is non-root by default. This requires that you run the pod with `securityContext` and updates the permissions of the volume with an `initContainer`. A key benefit of this configuration is that the pod follows security best practices and is prepared to run on Kubernetes distributions with hard security constraints like OpenShift. +- For OpenShift, one may either define the runAsUser and fsGroup accordingly, or try this more dynamic option: volumePermissions.securityContext.runAsUser="auto",securityContext.enabled=false,containerSecurityContext.enabled=false,shmVolume.chmod.enabled=false + +### Deploy chart using Docker Official PostgreSQL Image + +From chart version 4.0.0, it is possible to use this chart with the Docker Official PostgreSQL image. +Besides specifying the new Docker repository and tag, it is important to modify the PostgreSQL data directory and volume mount point. Basically, the PostgreSQL data dir cannot be the mount point directly, it has to be a subdirectory. + +``` +image.repository=postgres +image.tag=10.6 +postgresqlDataDir=/data/pgdata +persistence.mountPath=/data/ +``` + +### Setting Pod's affinity + +This chart allows you to set your custom affinity using the `XXX.affinity` paremeter(s). Find more infomation about Pod's affinity in the [kubernetes documentation](https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#affinity-and-anti-affinity). + +As an alternative, you can use of the preset configurations for pod affinity, pod anti-affinity, and node affinity available at the [bitnami/common](https://github.com/bitnami/charts/tree/master/bitnami/common#affinities) chart. To do so, set the `XXX.podAffinityPreset`, `XXX.podAntiAffinityPreset`, or `XXX.nodeAffinityPreset` parameters. + +## Troubleshooting + +Find more information about how to deal with common errors related to Bitnami’s Helm charts in [this troubleshooting guide](https://docs.bitnami.com/general/how-to/troubleshoot-helm-chart-issues). + +## Upgrading + +It's necessary to specify the existing passwords while performing an upgrade to ensure the secrets are not updated with invalid randomly generated passwords. Remember to specify the existing values of the `postgresqlPassword` and `replication.password` parameters when upgrading the chart: + +```bash +$ helm upgrade my-release bitnami/postgresql \ + --set postgresqlPassword=[POSTGRESQL_PASSWORD] \ + --set replication.password=[REPLICATION_PASSWORD] +``` + +> Note: you need to substitute the placeholders _[POSTGRESQL_PASSWORD]_, and _[REPLICATION_PASSWORD]_ with the values obtained from instructions in the installation notes. + +### To 10.0.0 + +[On November 13, 2020, Helm v2 support was formally finished](https://github.com/helm/charts#status-of-the-project), this major version is the result of the required changes applied to the Helm Chart to be able to incorporate the different features added in Helm v3 and to be consistent with the Helm project itself regarding the Helm v2 EOL. + +**What changes were introduced in this major version?** + +- Previous versions of this Helm Chart use `apiVersion: v1` (installable by both Helm 2 and 3), this Helm Chart was updated to `apiVersion: v2` (installable by Helm 3 only). [Here](https://helm.sh/docs/topics/charts/#the-apiversion-field) you can find more information about the `apiVersion` field. +- Move dependency information from the *requirements.yaml* to the *Chart.yaml* +- After running `helm dependency update`, a *Chart.lock* file is generated containing the same structure used in the previous *requirements.lock* +- The different fields present in the *Chart.yaml* file has been ordered alphabetically in a homogeneous way for all the Bitnami Helm Chart. + +**Considerations when upgrading to this version** + +- If you want to upgrade to this version using Helm v2, this scenario is not supported as this version doesn't support Helm v2 anymore +- If you installed the previous version with Helm v2 and wants to upgrade to this version with Helm v3, please refer to the [official Helm documentation](https://helm.sh/docs/topics/v2_v3_migration/#migration-use-cases) about migrating from Helm v2 to v3 + +**Useful links** + +- https://docs.bitnami.com/tutorials/resolve-helm2-helm3-post-migration-issues/ +- https://helm.sh/docs/topics/v2_v3_migration/ +- https://helm.sh/blog/migrate-from-helm-v2-to-helm-v3/ + +#### Breaking changes + +- The term `master` has been replaced with `primary` and `slave` with `readReplicas` throughout the chart. Role names have changed from `master` and `slave` to `primary` and `read`. + +To upgrade to `10.0.0`, it should be done reusing the PVCs used to hold the PostgreSQL data on your previous release. To do so, follow the instructions below (the following example assumes that the release name is `postgresql`): + +> NOTE: Please, create a backup of your database before running any of those actions. + +Obtain the credentials and the names of the PVCs used to hold the PostgreSQL data on your current release: + +```console +$ export POSTGRESQL_PASSWORD=$(kubectl get secret --namespace default postgresql -o jsonpath="{.data.postgresql-password}" | base64 --decode) +$ export POSTGRESQL_PVC=$(kubectl get pvc -l app.kubernetes.io/instance=postgresql,role=master -o jsonpath="{.items[0].metadata.name}") +``` + +Delete the PostgreSQL statefulset. Notice the option `--cascade=false`: + +```console +$ kubectl delete statefulsets.apps postgresql-postgresql --cascade=false +``` + +Now the upgrade works: + +```console +$ helm upgrade postgresql bitnami/postgresql --set postgresqlPassword=$POSTGRESQL_PASSWORD --set persistence.existingClaim=$POSTGRESQL_PVC +``` + +You will have to delete the existing PostgreSQL pod and the new statefulset is going to create a new one + +```console +$ kubectl delete pod postgresql-postgresql-0 +``` + +Finally, you should see the lines below in PostgreSQL container logs: + +```console +$ kubectl logs $(kubectl get pods -l app.kubernetes.io/instance=postgresql,app.kubernetes.io/name=postgresql,role=primary -o jsonpath="{.items[0].metadata.name}") +... +postgresql 08:05:12.59 INFO ==> Deploying PostgreSQL with persisted data... +... +``` + +### To 9.0.0 + +In this version the chart was adapted to follow the Helm label best practices, see [PR 3021](https://github.com/bitnami/charts/pull/3021). That means the backward compatibility is not guarantee when upgrading the chart to this major version. + +As a workaround, you can delete the existing statefulset (using the `--cascade=false` flag pods are not deleted) before upgrade the chart. For example, this can be a valid workflow: + +- Deploy an old version (8.X.X) + +```console +$ helm install postgresql bitnami/postgresql --version 8.10.14 +``` + +- Old version is up and running + +```console +$ helm ls +NAME NAMESPACE REVISION UPDATED STATUS CHART APP VERSION +postgresql default 1 2020-08-04 13:39:54.783480286 +0000 UTC deployed postgresql-8.10.14 11.8.0 + +$ kubectl get pods +NAME READY STATUS RESTARTS AGE +postgresql-postgresql-0 1/1 Running 0 76s +``` + +- The upgrade to the latest one (9.X.X) is going to fail + +```console +$ helm upgrade postgresql bitnami/postgresql +Error: UPGRADE FAILED: cannot patch "postgresql-postgresql" with kind StatefulSet: StatefulSet.apps "postgresql-postgresql" is invalid: spec: Forbidden: updates to statefulset spec for fields other than 'replicas', 'template', and 'updateStrategy' are forbidden +``` + +- Delete the statefulset + +```console +$ kubectl delete statefulsets.apps --cascade=false postgresql-postgresql +statefulset.apps "postgresql-postgresql" deleted +``` + +- Now the upgrade works + +```console +$ helm upgrade postgresql bitnami/postgresql +$ helm ls +NAME NAMESPACE REVISION UPDATED STATUS CHART APP VERSION +postgresql default 3 2020-08-04 13:42:08.020385884 +0000 UTC deployed postgresql-9.1.2 11.8.0 +``` + +- We can kill the existing pod and the new statefulset is going to create a new one: + +```console +$ kubectl delete pod postgresql-postgresql-0 +pod "postgresql-postgresql-0" deleted + +$ kubectl get pods +NAME READY STATUS RESTARTS AGE +postgresql-postgresql-0 1/1 Running 0 19s +``` + +Please, note that without the `--cascade=false` both objects (statefulset and pod) are going to be removed and both objects will be deployed again with the `helm upgrade` command + +### To 8.0.0 + +Prefixes the port names with their protocols to comply with Istio conventions. + +If you depend on the port names in your setup, make sure to update them to reflect this change. + +### To 7.1.0 + +Adds support for LDAP configuration. + +### To 7.0.0 + +Helm performs a lookup for the object based on its group (apps), version (v1), and kind (Deployment). Also known as its GroupVersionKind, or GVK. Changing the GVK is considered a compatibility breaker from Kubernetes' point of view, so you cannot "upgrade" those objects to the new GVK in-place. Earlier versions of Helm 3 did not perform the lookup correctly which has since been fixed to match the spec. + +In https://github.com/helm/charts/pull/17281 the `apiVersion` of the statefulset resources was updated to `apps/v1` in tune with the api's deprecated, resulting in compatibility breakage. + +This major version bump signifies this change. + +### To 6.5.7 + +In this version, the chart will use PostgreSQL with the Postgis extension included. The version used with Postgresql version 10, 11 and 12 is Postgis 2.5. It has been compiled with the following dependencies: + +- protobuf +- protobuf-c +- json-c +- geos +- proj + +### To 5.0.0 + +In this version, the **chart is using PostgreSQL 11 instead of PostgreSQL 10**. You can find the main difference and notable changes in the following links: [https://www.postgresql.org/about/news/1894/](https://www.postgresql.org/about/news/1894/) and [https://www.postgresql.org/about/featurematrix/](https://www.postgresql.org/about/featurematrix/). + +For major releases of PostgreSQL, the internal data storage format is subject to change, thus complicating upgrades, you can see some errors like the following one in the logs: + +```console +Welcome to the Bitnami postgresql container +Subscribe to project updates by watching https://github.com/bitnami/bitnami-docker-postgresql +Submit issues and feature requests at https://github.com/bitnami/bitnami-docker-postgresql/issues +Send us your feedback at containers@bitnami.com + +INFO ==> ** Starting PostgreSQL setup ** +NFO ==> Validating settings in POSTGRESQL_* env vars.. +INFO ==> Initializing PostgreSQL database... +INFO ==> postgresql.conf file not detected. Generating it... +INFO ==> pg_hba.conf file not detected. Generating it... +INFO ==> Deploying PostgreSQL with persisted data... +INFO ==> Configuring replication parameters +INFO ==> Loading custom scripts... +INFO ==> Enabling remote connections +INFO ==> Stopping PostgreSQL... +INFO ==> ** PostgreSQL setup finished! ** + +INFO ==> ** Starting PostgreSQL ** + [1] FATAL: database files are incompatible with server + [1] DETAIL: The data directory was initialized by PostgreSQL version 10, which is not compatible with this version 11.3. +``` + +In this case, you should migrate the data from the old chart to the new one following an approach similar to that described in [this section](https://www.postgresql.org/docs/current/upgrading.html#UPGRADING-VIA-PGDUMPALL) from the official documentation. Basically, create a database dump in the old chart, move and restore it in the new one. + +### To 4.0.0 + +This chart will use by default the Bitnami PostgreSQL container starting from version `10.7.0-r68`. This version moves the initialization logic from node.js to bash. This new version of the chart requires setting the `POSTGRES_PASSWORD` in the slaves as well, in order to properly configure the `pg_hba.conf` file. Users from previous versions of the chart are advised to upgrade immediately. + +IMPORTANT: If you do not want to upgrade the chart version then make sure you use the `10.7.0-r68` version of the container. Otherwise, you will get this error + +``` +The POSTGRESQL_PASSWORD environment variable is empty or not set. Set the environment variable ALLOW_EMPTY_PASSWORD=yes to allow the container to be started with blank passwords. This is recommended only for development +``` + +### To 3.0.0 + +This releases make it possible to specify different nodeSelector, affinity and tolerations for master and slave pods. +It also fixes an issue with `postgresql.master.fullname` helper template not obeying fullnameOverride. + +#### Breaking changes + +- `affinty` has been renamed to `master.affinity` and `slave.affinity`. +- `tolerations` has been renamed to `master.tolerations` and `slave.tolerations`. +- `nodeSelector` has been renamed to `master.nodeSelector` and `slave.nodeSelector`. + +### To 2.0.0 + +In order to upgrade from the `0.X.X` branch to `1.X.X`, you should follow the below steps: + +- Obtain the service name (`SERVICE_NAME`) and password (`OLD_PASSWORD`) of the existing postgresql chart. You can find the instructions to obtain the password in the NOTES.txt, the service name can be obtained by running + +```console +$ kubectl get svc +``` + +- Install (not upgrade) the new version + +```console +$ helm repo update +$ helm install my-release bitnami/postgresql +``` + +- Connect to the new pod (you can obtain the name by running `kubectl get pods`): + +```console +$ kubectl exec -it NAME bash +``` + +- Once logged in, create a dump file from the previous database using `pg_dump`, for that we should connect to the previous postgresql chart: + +```console +$ pg_dump -h SERVICE_NAME -U postgres DATABASE_NAME > /tmp/backup.sql +``` + +After run above command you should be prompted for a password, this password is the previous chart password (`OLD_PASSWORD`). +This operation could take some time depending on the database size. + +- Once you have the backup file, you can restore it with a command like the one below: + +```console +$ psql -U postgres DATABASE_NAME < /tmp/backup.sql +``` + +In this case, you are accessing to the local postgresql, so the password should be the new one (you can find it in NOTES.txt). + +If you want to restore the database and the database schema does not exist, it is necessary to first follow the steps described below. + +```console +$ psql -U postgres +postgres=# drop database DATABASE_NAME; +postgres=# create database DATABASE_NAME; +postgres=# create user USER_NAME; +postgres=# alter role USER_NAME with password 'BITNAMI_USER_PASSWORD'; +postgres=# grant all privileges on database DATABASE_NAME to USER_NAME; +postgres=# alter database DATABASE_NAME owner to USER_NAME; +``` diff --git a/charts/jfrog/artifactory-ha/107.104.10/charts/postgresql/charts/common/.helmignore b/charts/jfrog/artifactory-ha/107.104.10/charts/postgresql/charts/common/.helmignore new file mode 100644 index 0000000000..50af031725 --- /dev/null +++ b/charts/jfrog/artifactory-ha/107.104.10/charts/postgresql/charts/common/.helmignore @@ -0,0 +1,22 @@ +# 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/ diff --git a/charts/jfrog/artifactory-ha/107.104.10/charts/postgresql/charts/common/Chart.yaml b/charts/jfrog/artifactory-ha/107.104.10/charts/postgresql/charts/common/Chart.yaml new file mode 100644 index 0000000000..bcc3808d08 --- /dev/null +++ b/charts/jfrog/artifactory-ha/107.104.10/charts/postgresql/charts/common/Chart.yaml @@ -0,0 +1,23 @@ +annotations: + category: Infrastructure +apiVersion: v2 +appVersion: 1.4.2 +description: A Library Helm Chart for grouping common logic between bitnami charts. + This chart is not deployable by itself. +home: https://github.com/bitnami/charts/tree/master/bitnami/common +icon: https://bitnami.com/downloads/logos/bitnami-mark.png +keywords: +- common +- helper +- template +- function +- bitnami +maintainers: +- email: containers@bitnami.com + name: Bitnami +name: common +sources: +- https://github.com/bitnami/charts +- http://www.bitnami.com/ +type: library +version: 1.4.2 diff --git a/charts/jfrog/artifactory-ha/107.104.10/charts/postgresql/charts/common/README.md b/charts/jfrog/artifactory-ha/107.104.10/charts/postgresql/charts/common/README.md new file mode 100644 index 0000000000..7287cbb5fc --- /dev/null +++ b/charts/jfrog/artifactory-ha/107.104.10/charts/postgresql/charts/common/README.md @@ -0,0 +1,322 @@ +# Bitnami Common Library Chart + +A [Helm Library Chart](https://helm.sh/docs/topics/library_charts/#helm) for grouping common logic between bitnami charts. + +## TL;DR + +```yaml +dependencies: + - name: common + version: 0.x.x + repository: https://charts.bitnami.com/bitnami +``` + +```bash +$ helm dependency update +``` + +```yaml +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ include "common.names.fullname" . }} +data: + myvalue: "Hello World" +``` + +## Introduction + +This chart provides a common template helpers which can be used to develop new charts using [Helm](https://helm.sh) package manager. + +Bitnami charts can be used with [Kubeapps](https://kubeapps.com/) for deployment and management of Helm Charts in clusters. This Helm chart has been tested on top of [Bitnami Kubernetes Production Runtime](https://kubeprod.io/) (BKPR). Deploy BKPR to get automated TLS certificates, logging and monitoring for your applications. + +## Prerequisites + +- Kubernetes 1.12+ +- Helm 3.1.0 + +## Parameters + +The following table lists the helpers available in the library which are scoped in different sections. + +### Affinities + +| Helper identifier | Description | Expected Input | +|-------------------------------|------------------------------------------------------|------------------------------------------------| +| `common.affinities.node.soft` | Return a soft nodeAffinity definition | `dict "key" "FOO" "values" (list "BAR" "BAZ")` | +| `common.affinities.node.hard` | Return a hard nodeAffinity definition | `dict "key" "FOO" "values" (list "BAR" "BAZ")` | +| `common.affinities.pod.soft` | Return a soft podAffinity/podAntiAffinity definition | `dict "component" "FOO" "context" $` | +| `common.affinities.pod.hard` | Return a hard podAffinity/podAntiAffinity definition | `dict "component" "FOO" "context" $` | + +### Capabilities + +| Helper identifier | Description | Expected Input | +|----------------------------------------------|------------------------------------------------------------------------------------------------|-------------------| +| `common.capabilities.kubeVersion` | Return the target Kubernetes version (using client default if .Values.kubeVersion is not set). | `.` Chart context | +| `common.capabilities.deployment.apiVersion` | Return the appropriate apiVersion for deployment. | `.` Chart context | +| `common.capabilities.statefulset.apiVersion` | Return the appropriate apiVersion for statefulset. | `.` Chart context | +| `common.capabilities.ingress.apiVersion` | Return the appropriate apiVersion for ingress. | `.` Chart context | +| `common.capabilities.rbac.apiVersion` | Return the appropriate apiVersion for RBAC resources. | `.` Chart context | +| `common.capabilities.crd.apiVersion` | Return the appropriate apiVersion for CRDs. | `.` Chart context | +| `common.capabilities.supportsHelmVersion` | Returns true if the used Helm version is 3.3+ | `.` Chart context | + +### Errors + +| Helper identifier | Description | Expected Input | +|-----------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------| +| `common.errors.upgrade.passwords.empty` | It will ensure required passwords are given when we are upgrading a chart. If `validationErrors` is not empty it will throw an error and will stop the upgrade action. | `dict "validationErrors" (list $validationError00 $validationError01) "context" $` | + +### Images + +| Helper identifier | Description | Expected Input | +|-----------------------------|------------------------------------------------------|---------------------------------------------------------------------------------------------------------| +| `common.images.image` | Return the proper and full image name | `dict "imageRoot" .Values.path.to.the.image "global" $`, see [ImageRoot](#imageroot) for the structure. | +| `common.images.pullSecrets` | Return the proper Docker Image Registry Secret Names | `dict "images" (list .Values.path.to.the.image1, .Values.path.to.the.image2) "global" .Values.global` | + +### Ingress + +| Helper identifier | Description | Expected Input | +|--------------------------|----------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `common.ingress.backend` | Generate a proper Ingress backend entry depending on the API version | `dict "serviceName" "foo" "servicePort" "bar"`, see the [Ingress deprecation notice](https://kubernetes.io/blog/2019/07/18/api-deprecations-in-1-16/) for the syntax differences | + +### Labels + +| Helper identifier | Description | Expected Input | +|-----------------------------|------------------------------------------------------|-------------------| +| `common.labels.standard` | Return Kubernetes standard labels | `.` Chart context | +| `common.labels.matchLabels` | Return the proper Docker Image Registry Secret Names | `.` Chart context | + +### Names + +| Helper identifier | Description | Expected Inpput | +|-------------------------|------------------------------------------------------------|-------------------| +| `common.names.name` | Expand the name of the chart or use `.Values.nameOverride` | `.` Chart context | +| `common.names.fullname` | Create a default fully qualified app name. | `.` Chart context | +| `common.names.chart` | Chart name plus version | `.` Chart context | + +### Secrets + +| Helper identifier | Description | Expected Input | +|---------------------------|--------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `common.secrets.name` | Generate the name of the secret. | `dict "existingSecret" .Values.path.to.the.existingSecret "defaultNameSuffix" "mySuffix" "context" $` see [ExistingSecret](#existingsecret) for the structure. | +| `common.secrets.key` | Generate secret key. | `dict "existingSecret" .Values.path.to.the.existingSecret "key" "keyName"` see [ExistingSecret](#existingsecret) for the structure. | +| `common.passwords.manage` | Generate secret password or retrieve one if already created. | `dict "secret" "secret-name" "key" "keyName" "providedValues" (list "path.to.password1" "path.to.password2") "length" 10 "strong" false "chartName" "chartName" "context" $`, length, strong and chartNAme fields are optional. | +| `common.secrets.exists` | Returns whether a previous generated secret already exists. | `dict "secret" "secret-name" "context" $` | + +### Storage + +| Helper identifier | Description | Expected Input | +|-------------------------------|---------------------------------------|---------------------------------------------------------------------------------------------------------------------| +| `common.affinities.node.soft` | Return a soft nodeAffinity definition | `dict "persistence" .Values.path.to.the.persistence "global" $`, see [Persistence](#persistence) for the structure. | + +### TplValues + +| Helper identifier | Description | Expected Input | +|---------------------------|----------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------| +| `common.tplvalues.render` | Renders a value that contains template | `dict "value" .Values.path.to.the.Value "context" $`, value is the value should rendered as template, context frequently is the chart context `$` or `.` | + +### Utils + +| Helper identifier | Description | Expected Input | +|--------------------------------|------------------------------------------------------------------------------------------|------------------------------------------------------------------------| +| `common.utils.fieldToEnvVar` | Build environment variable name given a field. | `dict "field" "my-password"` | +| `common.utils.secret.getvalue` | Print instructions to get a secret value. | `dict "secret" "secret-name" "field" "secret-value-field" "context" $` | +| `common.utils.getValueFromKey` | Gets a value from `.Values` object given its key path | `dict "key" "path.to.key" "context" $` | +| `common.utils.getKeyFromList` | Returns first `.Values` key with a defined value or first of the list if all non-defined | `dict "keys" (list "path.to.key1" "path.to.key2") "context" $` | + +### Validations + +| Helper identifier | Description | Expected Input | +|--------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `common.validations.values.single.empty` | Validate a value must not be empty. | `dict "valueKey" "path.to.value" "secret" "secret.name" "field" "my-password" "subchart" "subchart" "context" $` secret, field and subchart are optional. In case they are given, the helper will generate a how to get instruction. See [ValidateValue](#validatevalue) | +| `common.validations.values.multiple.empty` | Validate a multiple values must not be empty. It returns a shared error for all the values. | `dict "required" (list $validateValueConf00 $validateValueConf01) "context" $`. See [ValidateValue](#validatevalue) | +| `common.validations.values.mariadb.passwords` | This helper will ensure required password for MariaDB are not empty. It returns a shared error for all the values. | `dict "secret" "mariadb-secret" "subchart" "true" "context" $` subchart field is optional and could be true or false it depends on where you will use mariadb chart and the helper. | +| `common.validations.values.postgresql.passwords` | This helper will ensure required password for PostgreSQL are not empty. It returns a shared error for all the values. | `dict "secret" "postgresql-secret" "subchart" "true" "context" $` subchart field is optional and could be true or false it depends on where you will use postgresql chart and the helper. | +| `common.validations.values.redis.passwords` | This helper will ensure required password for RedisTM are not empty. It returns a shared error for all the values. | `dict "secret" "redis-secret" "subchart" "true" "context" $` subchart field is optional and could be true or false it depends on where you will use redis chart and the helper. | +| `common.validations.values.cassandra.passwords` | This helper will ensure required password for Cassandra are not empty. It returns a shared error for all the values. | `dict "secret" "cassandra-secret" "subchart" "true" "context" $` subchart field is optional and could be true or false it depends on where you will use cassandra chart and the helper. | +| `common.validations.values.mongodb.passwords` | This helper will ensure required password for MongoDB® are not empty. It returns a shared error for all the values. | `dict "secret" "mongodb-secret" "subchart" "true" "context" $` subchart field is optional and could be true or false it depends on where you will use mongodb chart and the helper. | + +### Warnings + +| Helper identifier | Description | Expected Input | +|------------------------------|----------------------------------|------------------------------------------------------------| +| `common.warnings.rollingTag` | Warning about using rolling tag. | `ImageRoot` see [ImageRoot](#imageroot) for the structure. | + +## Special input schemas + +### ImageRoot + +```yaml +registry: + type: string + description: Docker registry where the image is located + example: docker.io + +repository: + type: string + description: Repository and image name + example: bitnami/nginx + +tag: + type: string + description: image tag + example: 1.16.1-debian-10-r63 + +pullPolicy: + type: string + description: Specify a imagePullPolicy. Defaults to 'Always' if image tag is 'latest', else set to 'IfNotPresent' + +pullSecrets: + type: array + items: + type: string + description: Optionally specify an array of imagePullSecrets. + +debug: + type: boolean + description: Set to true if you would like to see extra information on logs + example: false + +## An instance would be: +# registry: docker.io +# repository: bitnami/nginx +# tag: 1.16.1-debian-10-r63 +# pullPolicy: IfNotPresent +# debug: false +``` + +### Persistence + +```yaml +enabled: + type: boolean + description: Whether enable persistence. + example: true + +storageClass: + type: string + description: Ghost data Persistent Volume Storage Class, If set to "-", storageClassName: "" which disables dynamic provisioning. + example: "-" + +accessMode: + type: string + description: Access mode for the Persistent Volume Storage. + example: ReadWriteOnce + +size: + type: string + description: Size the Persistent Volume Storage. + example: 8Gi + +path: + type: string + description: Path to be persisted. + example: /bitnami + +## An instance would be: +# enabled: true +# storageClass: "-" +# accessMode: ReadWriteOnce +# size: 8Gi +# path: /bitnami +``` + +### ExistingSecret + +```yaml +name: + type: string + description: Name of the existing secret. + example: mySecret +keyMapping: + description: Mapping between the expected key name and the name of the key in the existing secret. + type: object + +## An instance would be: +# name: mySecret +# keyMapping: +# password: myPasswordKey +``` + +#### Example of use + +When we store sensitive data for a deployment in a secret, some times we want to give to users the possibility of using theirs existing secrets. + +```yaml +# templates/secret.yaml +--- +apiVersion: v1 +kind: Secret +metadata: + name: {{ include "common.names.fullname" . }} + labels: + app: {{ include "common.names.fullname" . }} +type: Opaque +data: + password: {{ .Values.password | b64enc | quote }} + +# templates/dpl.yaml +--- +... + env: + - name: PASSWORD + valueFrom: + secretKeyRef: + name: {{ include "common.secrets.name" (dict "existingSecret" .Values.existingSecret "context" $) }} + key: {{ include "common.secrets.key" (dict "existingSecret" .Values.existingSecret "key" "password") }} +... + +# values.yaml +--- +name: mySecret +keyMapping: + password: myPasswordKey +``` + +### ValidateValue + +#### NOTES.txt + +```console +{{- $validateValueConf00 := (dict "valueKey" "path.to.value00" "secret" "secretName" "field" "password-00") -}} +{{- $validateValueConf01 := (dict "valueKey" "path.to.value01" "secret" "secretName" "field" "password-01") -}} + +{{ include "common.validations.values.multiple.empty" (dict "required" (list $validateValueConf00 $validateValueConf01) "context" $) }} +``` + +If we force those values to be empty we will see some alerts + +```console +$ helm install test mychart --set path.to.value00="",path.to.value01="" + 'path.to.value00' must not be empty, please add '--set path.to.value00=$PASSWORD_00' to the command. To get the current value: + + export PASSWORD_00=$(kubectl get secret --namespace default secretName -o jsonpath="{.data.password-00}" | base64 --decode) + + 'path.to.value01' must not be empty, please add '--set path.to.value01=$PASSWORD_01' to the command. To get the current value: + + export PASSWORD_01=$(kubectl get secret --namespace default secretName -o jsonpath="{.data.password-01}" | base64 --decode) +``` + +## Upgrading + +### To 1.0.0 + +[On November 13, 2020, Helm v2 support was formally finished](https://github.com/helm/charts#status-of-the-project), this major version is the result of the required changes applied to the Helm Chart to be able to incorporate the different features added in Helm v3 and to be consistent with the Helm project itself regarding the Helm v2 EOL. + +**What changes were introduced in this major version?** + +- Previous versions of this Helm Chart use `apiVersion: v1` (installable by both Helm 2 and 3), this Helm Chart was updated to `apiVersion: v2` (installable by Helm 3 only). [Here](https://helm.sh/docs/topics/charts/#the-apiversion-field) you can find more information about the `apiVersion` field. +- Use `type: library`. [Here](https://v3.helm.sh/docs/faq/#library-chart-support) you can find more information. +- The different fields present in the *Chart.yaml* file has been ordered alphabetically in a homogeneous way for all the Bitnami Helm Charts + +**Considerations when upgrading to this version** + +- If you want to upgrade to this version from a previous one installed with Helm v3, you shouldn't face any issues +- If you want to upgrade to this version using Helm v2, this scenario is not supported as this version doesn't support Helm v2 anymore +- If you installed the previous version with Helm v2 and wants to upgrade to this version with Helm v3, please refer to the [official Helm documentation](https://helm.sh/docs/topics/v2_v3_migration/#migration-use-cases) about migrating from Helm v2 to v3 + +**Useful links** + +- https://docs.bitnami.com/tutorials/resolve-helm2-helm3-post-migration-issues/ +- https://helm.sh/docs/topics/v2_v3_migration/ +- https://helm.sh/blog/migrate-from-helm-v2-to-helm-v3/ diff --git a/charts/jfrog/artifactory-ha/107.104.10/charts/postgresql/charts/common/templates/_affinities.tpl b/charts/jfrog/artifactory-ha/107.104.10/charts/postgresql/charts/common/templates/_affinities.tpl new file mode 100644 index 0000000000..493a6dc7e4 --- /dev/null +++ b/charts/jfrog/artifactory-ha/107.104.10/charts/postgresql/charts/common/templates/_affinities.tpl @@ -0,0 +1,94 @@ +{{/* vim: set filetype=mustache: */}} + +{{/* +Return a soft nodeAffinity definition +{{ include "common.affinities.nodes.soft" (dict "key" "FOO" "values" (list "BAR" "BAZ")) -}} +*/}} +{{- define "common.affinities.nodes.soft" -}} +preferredDuringSchedulingIgnoredDuringExecution: + - preference: + matchExpressions: + - key: {{ .key }} + operator: In + values: + {{- range .values }} + - {{ . }} + {{- end }} + weight: 1 +{{- end -}} + +{{/* +Return a hard nodeAffinity definition +{{ include "common.affinities.nodes.hard" (dict "key" "FOO" "values" (list "BAR" "BAZ")) -}} +*/}} +{{- define "common.affinities.nodes.hard" -}} +requiredDuringSchedulingIgnoredDuringExecution: + nodeSelectorTerms: + - matchExpressions: + - key: {{ .key }} + operator: In + values: + {{- range .values }} + - {{ . }} + {{- end }} +{{- end -}} + +{{/* +Return a nodeAffinity definition +{{ include "common.affinities.nodes" (dict "type" "soft" "key" "FOO" "values" (list "BAR" "BAZ")) -}} +*/}} +{{- define "common.affinities.nodes" -}} + {{- if eq .type "soft" }} + {{- include "common.affinities.nodes.soft" . -}} + {{- else if eq .type "hard" }} + {{- include "common.affinities.nodes.hard" . -}} + {{- end -}} +{{- end -}} + +{{/* +Return a soft podAffinity/podAntiAffinity definition +{{ include "common.affinities.pods.soft" (dict "component" "FOO" "context" $) -}} +*/}} +{{- define "common.affinities.pods.soft" -}} +{{- $component := default "" .component -}} +preferredDuringSchedulingIgnoredDuringExecution: + - podAffinityTerm: + labelSelector: + matchLabels: {{- (include "common.labels.matchLabels" .context) | nindent 10 }} + {{- if not (empty $component) }} + {{ printf "app.kubernetes.io/component: %s" $component }} + {{- end }} + namespaces: + - {{ .context.Release.Namespace | quote }} + topologyKey: kubernetes.io/hostname + weight: 1 +{{- end -}} + +{{/* +Return a hard podAffinity/podAntiAffinity definition +{{ include "common.affinities.pods.hard" (dict "component" "FOO" "context" $) -}} +*/}} +{{- define "common.affinities.pods.hard" -}} +{{- $component := default "" .component -}} +requiredDuringSchedulingIgnoredDuringExecution: + - labelSelector: + matchLabels: {{- (include "common.labels.matchLabels" .context) | nindent 8 }} + {{- if not (empty $component) }} + {{ printf "app.kubernetes.io/component: %s" $component }} + {{- end }} + namespaces: + - {{ .context.Release.Namespace | quote }} + topologyKey: kubernetes.io/hostname +{{- end -}} + +{{/* +Return a podAffinity/podAntiAffinity definition +{{ include "common.affinities.pods" (dict "type" "soft" "key" "FOO" "values" (list "BAR" "BAZ")) -}} +*/}} +{{- define "common.affinities.pods" -}} + {{- if eq .type "soft" }} + {{- include "common.affinities.pods.soft" . -}} + {{- else if eq .type "hard" }} + {{- include "common.affinities.pods.hard" . -}} + {{- end -}} +{{- end -}} diff --git a/charts/jfrog/artifactory-ha/107.104.10/charts/postgresql/charts/common/templates/_capabilities.tpl b/charts/jfrog/artifactory-ha/107.104.10/charts/postgresql/charts/common/templates/_capabilities.tpl new file mode 100644 index 0000000000..4dde56a38d --- /dev/null +++ b/charts/jfrog/artifactory-ha/107.104.10/charts/postgresql/charts/common/templates/_capabilities.tpl @@ -0,0 +1,95 @@ +{{/* vim: set filetype=mustache: */}} + +{{/* +Return the target Kubernetes version +*/}} +{{- define "common.capabilities.kubeVersion" -}} +{{- if .Values.global }} + {{- if .Values.global.kubeVersion }} + {{- .Values.global.kubeVersion -}} + {{- else }} + {{- default .Capabilities.KubeVersion.Version .Values.kubeVersion -}} + {{- end -}} +{{- else }} +{{- default .Capabilities.KubeVersion.Version .Values.kubeVersion -}} +{{- end -}} +{{- end -}} + +{{/* +Return the appropriate apiVersion for deployment. +*/}} +{{- define "common.capabilities.deployment.apiVersion" -}} +{{- if semverCompare "<1.14-0" (include "common.capabilities.kubeVersion" .) -}} +{{- print "extensions/v1beta1" -}} +{{- else -}} +{{- print "apps/v1" -}} +{{- end -}} +{{- end -}} + +{{/* +Return the appropriate apiVersion for statefulset. +*/}} +{{- define "common.capabilities.statefulset.apiVersion" -}} +{{- if semverCompare "<1.14-0" (include "common.capabilities.kubeVersion" .) -}} +{{- print "apps/v1beta1" -}} +{{- else -}} +{{- print "apps/v1" -}} +{{- end -}} +{{- end -}} + +{{/* +Return the appropriate apiVersion for ingress. +*/}} +{{- define "common.capabilities.ingress.apiVersion" -}} +{{- if .Values.ingress -}} +{{- if .Values.ingress.apiVersion -}} +{{- .Values.ingress.apiVersion -}} +{{- else if semverCompare "<1.14-0" (include "common.capabilities.kubeVersion" .) -}} +{{- print "extensions/v1beta1" -}} +{{- else if semverCompare "<1.19-0" (include "common.capabilities.kubeVersion" .) -}} +{{- print "networking.k8s.io/v1beta1" -}} +{{- else -}} +{{- print "networking.k8s.io/v1" -}} +{{- end }} +{{- else if semverCompare "<1.14-0" (include "common.capabilities.kubeVersion" .) -}} +{{- print "extensions/v1beta1" -}} +{{- else if semverCompare "<1.19-0" (include "common.capabilities.kubeVersion" .) -}} +{{- print "networking.k8s.io/v1beta1" -}} +{{- else -}} +{{- print "networking.k8s.io/v1" -}} +{{- end -}} +{{- end -}} + +{{/* +Return the appropriate apiVersion for RBAC resources. +*/}} +{{- define "common.capabilities.rbac.apiVersion" -}} +{{- if semverCompare "<1.17-0" (include "common.capabilities.kubeVersion" .) -}} +{{- print "rbac.authorization.k8s.io/v1beta1" -}} +{{- else -}} +{{- print "rbac.authorization.k8s.io/v1" -}} +{{- end -}} +{{- end -}} + +{{/* +Return the appropriate apiVersion for CRDs. +*/}} +{{- define "common.capabilities.crd.apiVersion" -}} +{{- if semverCompare "<1.19-0" (include "common.capabilities.kubeVersion" .) -}} +{{- print "apiextensions.k8s.io/v1beta1" -}} +{{- else -}} +{{- print "apiextensions.k8s.io/v1" -}} +{{- end -}} +{{- end -}} + +{{/* +Returns true if the used Helm version is 3.3+. +A way to check the used Helm version was not introduced until version 3.3.0 with .Capabilities.HelmVersion, which contains an additional "{}}" structure. +This check is introduced as a regexMatch instead of {{ if .Capabilities.HelmVersion }} because checking for the key HelmVersion in <3.3 results in a "interface not found" error. +**To be removed when the catalog's minimun Helm version is 3.3** +*/}} +{{- define "common.capabilities.supportsHelmVersion" -}} +{{- if regexMatch "{(v[0-9])*[^}]*}}$" (.Capabilities | toString ) }} + {{- true -}} +{{- end -}} +{{- end -}} diff --git a/charts/jfrog/artifactory-ha/107.104.10/charts/postgresql/charts/common/templates/_errors.tpl b/charts/jfrog/artifactory-ha/107.104.10/charts/postgresql/charts/common/templates/_errors.tpl new file mode 100644 index 0000000000..a79cc2e322 --- /dev/null +++ b/charts/jfrog/artifactory-ha/107.104.10/charts/postgresql/charts/common/templates/_errors.tpl @@ -0,0 +1,23 @@ +{{/* vim: set filetype=mustache: */}} +{{/* +Through error when upgrading using empty passwords values that must not be empty. + +Usage: +{{- $validationError00 := include "common.validations.values.single.empty" (dict "valueKey" "path.to.password00" "secret" "secretName" "field" "password-00") -}} +{{- $validationError01 := include "common.validations.values.single.empty" (dict "valueKey" "path.to.password01" "secret" "secretName" "field" "password-01") -}} +{{ include "common.errors.upgrade.passwords.empty" (dict "validationErrors" (list $validationError00 $validationError01) "context" $) }} + +Required password params: + - validationErrors - String - Required. List of validation strings to be return, if it is empty it won't throw error. + - context - Context - Required. Parent context. +*/}} +{{- define "common.errors.upgrade.passwords.empty" -}} + {{- $validationErrors := join "" .validationErrors -}} + {{- if and $validationErrors .context.Release.IsUpgrade -}} + {{- $errorString := "\nPASSWORDS ERROR: You must provide your current passwords when upgrading the release." -}} + {{- $errorString = print $errorString "\n Note that even after reinstallation, old credentials may be needed as they may be kept in persistent volume claims." -}} + {{- $errorString = print $errorString "\n Further information can be obtained at https://docs.bitnami.com/general/how-to/troubleshoot-helm-chart-issues/#credential-errors-while-upgrading-chart-releases" -}} + {{- $errorString = print $errorString "\n%s" -}} + {{- printf $errorString $validationErrors | fail -}} + {{- end -}} +{{- end -}} diff --git a/charts/jfrog/artifactory-ha/107.104.10/charts/postgresql/charts/common/templates/_images.tpl b/charts/jfrog/artifactory-ha/107.104.10/charts/postgresql/charts/common/templates/_images.tpl new file mode 100644 index 0000000000..60f04fd6e2 --- /dev/null +++ b/charts/jfrog/artifactory-ha/107.104.10/charts/postgresql/charts/common/templates/_images.tpl @@ -0,0 +1,47 @@ +{{/* vim: set filetype=mustache: */}} +{{/* +Return the proper image name +{{ include "common.images.image" ( dict "imageRoot" .Values.path.to.the.image "global" $) }} +*/}} +{{- define "common.images.image" -}} +{{- $registryName := .imageRoot.registry -}} +{{- $repositoryName := .imageRoot.repository -}} +{{- $tag := .imageRoot.tag | toString -}} +{{- if .global }} + {{- if .global.imageRegistry }} + {{- $registryName = .global.imageRegistry -}} + {{- end -}} +{{- end -}} +{{- if $registryName }} +{{- printf "%s/%s:%s" $registryName $repositoryName $tag -}} +{{- else -}} +{{- printf "%s:%s" $repositoryName $tag -}} +{{- end -}} +{{- end -}} + +{{/* +Return the proper Docker Image Registry Secret Names +{{ include "common.images.pullSecrets" ( dict "images" (list .Values.path.to.the.image1, .Values.path.to.the.image2) "global" .Values.global) }} +*/}} +{{- define "common.images.pullSecrets" -}} + {{- $pullSecrets := list }} + + {{- if .global }} + {{- range .global.imagePullSecrets -}} + {{- $pullSecrets = append $pullSecrets . -}} + {{- end -}} + {{- end -}} + + {{- range .images -}} + {{- range .pullSecrets -}} + {{- $pullSecrets = append $pullSecrets . -}} + {{- end -}} + {{- end -}} + + {{- if (not (empty $pullSecrets)) }} +imagePullSecrets: + {{- range $pullSecrets }} + - name: {{ . }} + {{- end }} + {{- end }} +{{- end -}} diff --git a/charts/jfrog/artifactory-ha/107.104.10/charts/postgresql/charts/common/templates/_ingress.tpl b/charts/jfrog/artifactory-ha/107.104.10/charts/postgresql/charts/common/templates/_ingress.tpl new file mode 100644 index 0000000000..622ef50e3c --- /dev/null +++ b/charts/jfrog/artifactory-ha/107.104.10/charts/postgresql/charts/common/templates/_ingress.tpl @@ -0,0 +1,42 @@ +{{/* vim: set filetype=mustache: */}} + +{{/* +Generate backend entry that is compatible with all Kubernetes API versions. + +Usage: +{{ include "common.ingress.backend" (dict "serviceName" "backendName" "servicePort" "backendPort" "context" $) }} + +Params: + - serviceName - String. Name of an existing service backend + - servicePort - String/Int. Port name (or number) of the service. It will be translated to different yaml depending if it is a string or an integer. + - context - Dict - Required. The context for the template evaluation. +*/}} +{{- define "common.ingress.backend" -}} +{{- $apiVersion := (include "common.capabilities.ingress.apiVersion" .context) -}} +{{- if or (eq $apiVersion "extensions/v1beta1") (eq $apiVersion "networking.k8s.io/v1beta1") -}} +serviceName: {{ .serviceName }} +servicePort: {{ .servicePort }} +{{- else -}} +service: + name: {{ .serviceName }} + port: + {{- if typeIs "string" .servicePort }} + name: {{ .servicePort }} + {{- else if typeIs "int" .servicePort }} + number: {{ .servicePort }} + {{- end }} +{{- end -}} +{{- end -}} + +{{/* +Print "true" if the API pathType field is supported +Usage: +{{ include "common.ingress.supportsPathType" . }} +*/}} +{{- define "common.ingress.supportsPathType" -}} +{{- if (semverCompare "<1.18-0" (include "common.capabilities.kubeVersion" .)) -}} +{{- print "false" -}} +{{- else -}} +{{- print "true" -}} +{{- end -}} +{{- end -}} diff --git a/charts/jfrog/artifactory-ha/107.104.10/charts/postgresql/charts/common/templates/_labels.tpl b/charts/jfrog/artifactory-ha/107.104.10/charts/postgresql/charts/common/templates/_labels.tpl new file mode 100644 index 0000000000..252066c7e2 --- /dev/null +++ b/charts/jfrog/artifactory-ha/107.104.10/charts/postgresql/charts/common/templates/_labels.tpl @@ -0,0 +1,18 @@ +{{/* vim: set filetype=mustache: */}} +{{/* +Kubernetes standard labels +*/}} +{{- define "common.labels.standard" -}} +app.kubernetes.io/name: {{ include "common.names.name" . }} +helm.sh/chart: {{ include "common.names.chart" . }} +app.kubernetes.io/instance: {{ .Release.Name }} +app.kubernetes.io/managed-by: {{ .Release.Service }} +{{- end -}} + +{{/* +Labels to use on deploy.spec.selector.matchLabels and svc.spec.selector +*/}} +{{- define "common.labels.matchLabels" -}} +app.kubernetes.io/name: {{ include "common.names.name" . }} +app.kubernetes.io/instance: {{ .Release.Name }} +{{- end -}} diff --git a/charts/jfrog/artifactory-ha/107.104.10/charts/postgresql/charts/common/templates/_names.tpl b/charts/jfrog/artifactory-ha/107.104.10/charts/postgresql/charts/common/templates/_names.tpl new file mode 100644 index 0000000000..adf2a74f48 --- /dev/null +++ b/charts/jfrog/artifactory-ha/107.104.10/charts/postgresql/charts/common/templates/_names.tpl @@ -0,0 +1,32 @@ +{{/* vim: set filetype=mustache: */}} +{{/* +Expand the name of the chart. +*/}} +{{- define "common.names.name" -}} +{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}} +{{- end -}} + +{{/* +Create chart name and version as used by the chart label. +*/}} +{{- define "common.names.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}} +{{- end -}} + +{{/* +Create a default fully qualified app name. +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +If release name contains chart name it will be used as a full name. +*/}} +{{- define "common.names.fullname" -}} +{{- if .Values.fullnameOverride -}} +{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" -}} +{{- else -}} +{{- $name := default .Chart.Name .Values.nameOverride -}} +{{- if contains $name .Release.Name -}} +{{- .Release.Name | trunc 63 | trimSuffix "-" -}} +{{- else -}} +{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}} +{{- end -}} +{{- end -}} +{{- end -}} diff --git a/charts/jfrog/artifactory-ha/107.104.10/charts/postgresql/charts/common/templates/_secrets.tpl b/charts/jfrog/artifactory-ha/107.104.10/charts/postgresql/charts/common/templates/_secrets.tpl new file mode 100644 index 0000000000..60b84a7019 --- /dev/null +++ b/charts/jfrog/artifactory-ha/107.104.10/charts/postgresql/charts/common/templates/_secrets.tpl @@ -0,0 +1,129 @@ +{{/* vim: set filetype=mustache: */}} +{{/* +Generate secret name. + +Usage: +{{ include "common.secrets.name" (dict "existingSecret" .Values.path.to.the.existingSecret "defaultNameSuffix" "mySuffix" "context" $) }} + +Params: + - existingSecret - ExistingSecret/String - Optional. The path to the existing secrets in the values.yaml given by the user + to be used instead of the default one. Allows for it to be of type String (just the secret name) for backwards compatibility. + +info: https://github.com/bitnami/charts/tree/master/bitnami/common#existingsecret + - defaultNameSuffix - String - Optional. It is used only if we have several secrets in the same deployment. + - context - Dict - Required. The context for the template evaluation. +*/}} +{{- define "common.secrets.name" -}} +{{- $name := (include "common.names.fullname" .context) -}} + +{{- if .defaultNameSuffix -}} +{{- $name = printf "%s-%s" $name .defaultNameSuffix | trunc 63 | trimSuffix "-" -}} +{{- end -}} + +{{- with .existingSecret -}} +{{- if not (typeIs "string" .) -}} +{{- with .name -}} +{{- $name = . -}} +{{- end -}} +{{- else -}} +{{- $name = . -}} +{{- end -}} +{{- end -}} + +{{- printf "%s" $name -}} +{{- end -}} + +{{/* +Generate secret key. + +Usage: +{{ include "common.secrets.key" (dict "existingSecret" .Values.path.to.the.existingSecret "key" "keyName") }} + +Params: + - existingSecret - ExistingSecret/String - Optional. The path to the existing secrets in the values.yaml given by the user + to be used instead of the default one. Allows for it to be of type String (just the secret name) for backwards compatibility. + +info: https://github.com/bitnami/charts/tree/master/bitnami/common#existingsecret + - key - String - Required. Name of the key in the secret. +*/}} +{{- define "common.secrets.key" -}} +{{- $key := .key -}} + +{{- if .existingSecret -}} + {{- if not (typeIs "string" .existingSecret) -}} + {{- if .existingSecret.keyMapping -}} + {{- $key = index .existingSecret.keyMapping $.key -}} + {{- end -}} + {{- end }} +{{- end -}} + +{{- printf "%s" $key -}} +{{- end -}} + +{{/* +Generate secret password or retrieve one if already created. + +Usage: +{{ include "common.secrets.passwords.manage" (dict "secret" "secret-name" "key" "keyName" "providedValues" (list "path.to.password1" "path.to.password2") "length" 10 "strong" false "chartName" "chartName" "context" $) }} + +Params: + - secret - String - Required - Name of the 'Secret' resource where the password is stored. + - key - String - Required - Name of the key in the secret. + - providedValues - List - Required - The path to the validating value in the values.yaml, e.g: "mysql.password". Will pick first parameter with a defined value. + - length - int - Optional - Length of the generated random password. + - strong - Boolean - Optional - Whether to add symbols to the generated random password. + - chartName - String - Optional - Name of the chart used when said chart is deployed as a subchart. + - context - Context - Required - Parent context. +*/}} +{{- define "common.secrets.passwords.manage" -}} + +{{- $password := "" }} +{{- $subchart := "" }} +{{- $chartName := default "" .chartName }} +{{- $passwordLength := default 10 .length }} +{{- $providedPasswordKey := include "common.utils.getKeyFromList" (dict "keys" .providedValues "context" $.context) }} +{{- $providedPasswordValue := include "common.utils.getValueFromKey" (dict "key" $providedPasswordKey "context" $.context) }} +{{- $secret := (lookup "v1" "Secret" $.context.Release.Namespace .secret) }} +{{- if $secret }} + {{- if index $secret.data .key }} + {{- $password = index $secret.data .key }} + {{- end -}} +{{- else if $providedPasswordValue }} + {{- $password = $providedPasswordValue | toString | b64enc | quote }} +{{- else }} + + {{- if .context.Values.enabled }} + {{- $subchart = $chartName }} + {{- end -}} + + {{- $requiredPassword := dict "valueKey" $providedPasswordKey "secret" .secret "field" .key "subchart" $subchart "context" $.context -}} + {{- $requiredPasswordError := include "common.validations.values.single.empty" $requiredPassword -}} + {{- $passwordValidationErrors := list $requiredPasswordError -}} + {{- include "common.errors.upgrade.passwords.empty" (dict "validationErrors" $passwordValidationErrors "context" $.context) -}} + + {{- if .strong }} + {{- $subStr := list (lower (randAlpha 1)) (randNumeric 1) (upper (randAlpha 1)) | join "_" }} + {{- $password = randAscii $passwordLength }} + {{- $password = regexReplaceAllLiteral "\\W" $password "@" | substr 5 $passwordLength }} + {{- $password = printf "%s%s" $subStr $password | toString | shuffle | b64enc | quote }} + {{- else }} + {{- $password = randAlphaNum $passwordLength | b64enc | quote }} + {{- end }} +{{- end -}} +{{- printf "%s" $password -}} +{{- end -}} + +{{/* +Returns whether a previous generated secret already exists + +Usage: +{{ include "common.secrets.exists" (dict "secret" "secret-name" "context" $) }} + +Params: + - secret - String - Required - Name of the 'Secret' resource where the password is stored. + - context - Context - Required - Parent context. +*/}} +{{- define "common.secrets.exists" -}} +{{- $secret := (lookup "v1" "Secret" $.context.Release.Namespace .secret) }} +{{- if $secret }} + {{- true -}} +{{- end -}} +{{- end -}} diff --git a/charts/jfrog/artifactory-ha/107.104.10/charts/postgresql/charts/common/templates/_storage.tpl b/charts/jfrog/artifactory-ha/107.104.10/charts/postgresql/charts/common/templates/_storage.tpl new file mode 100644 index 0000000000..60e2a844f6 --- /dev/null +++ b/charts/jfrog/artifactory-ha/107.104.10/charts/postgresql/charts/common/templates/_storage.tpl @@ -0,0 +1,23 @@ +{{/* vim: set filetype=mustache: */}} +{{/* +Return the proper Storage Class +{{ include "common.storage.class" ( dict "persistence" .Values.path.to.the.persistence "global" $) }} +*/}} +{{- define "common.storage.class" -}} + +{{- $storageClass := .persistence.storageClass -}} +{{- if .global -}} + {{- if .global.storageClass -}} + {{- $storageClass = .global.storageClass -}} + {{- end -}} +{{- end -}} + +{{- if $storageClass -}} + {{- if (eq "-" $storageClass) -}} + {{- printf "storageClassName: \"\"" -}} + {{- else }} + {{- printf "storageClassName: %s" $storageClass -}} + {{- end -}} +{{- end -}} + +{{- end -}} diff --git a/charts/jfrog/artifactory-ha/107.104.10/charts/postgresql/charts/common/templates/_tplvalues.tpl b/charts/jfrog/artifactory-ha/107.104.10/charts/postgresql/charts/common/templates/_tplvalues.tpl new file mode 100644 index 0000000000..2db166851b --- /dev/null +++ b/charts/jfrog/artifactory-ha/107.104.10/charts/postgresql/charts/common/templates/_tplvalues.tpl @@ -0,0 +1,13 @@ +{{/* vim: set filetype=mustache: */}} +{{/* +Renders a value that contains template. +Usage: +{{ include "common.tplvalues.render" ( dict "value" .Values.path.to.the.Value "context" $) }} +*/}} +{{- define "common.tplvalues.render" -}} + {{- if typeIs "string" .value }} + {{- tpl .value .context }} + {{- else }} + {{- tpl (.value | toYaml) .context }} + {{- end }} +{{- end -}} diff --git a/charts/jfrog/artifactory-ha/107.104.10/charts/postgresql/charts/common/templates/_utils.tpl b/charts/jfrog/artifactory-ha/107.104.10/charts/postgresql/charts/common/templates/_utils.tpl new file mode 100644 index 0000000000..ea083a249f --- /dev/null +++ b/charts/jfrog/artifactory-ha/107.104.10/charts/postgresql/charts/common/templates/_utils.tpl @@ -0,0 +1,62 @@ +{{/* vim: set filetype=mustache: */}} +{{/* +Print instructions to get a secret value. +Usage: +{{ include "common.utils.secret.getvalue" (dict "secret" "secret-name" "field" "secret-value-field" "context" $) }} +*/}} +{{- define "common.utils.secret.getvalue" -}} +{{- $varname := include "common.utils.fieldToEnvVar" . -}} +export {{ $varname }}=$(kubectl get secret --namespace {{ .context.Release.Namespace | quote }} {{ .secret }} -o jsonpath="{.data.{{ .field }}}" | base64 --decode) +{{- end -}} + +{{/* +Build env var name given a field +Usage: +{{ include "common.utils.fieldToEnvVar" dict "field" "my-password" }} +*/}} +{{- define "common.utils.fieldToEnvVar" -}} + {{- $fieldNameSplit := splitList "-" .field -}} + {{- $upperCaseFieldNameSplit := list -}} + + {{- range $fieldNameSplit -}} + {{- $upperCaseFieldNameSplit = append $upperCaseFieldNameSplit ( upper . ) -}} + {{- end -}} + + {{ join "_" $upperCaseFieldNameSplit }} +{{- end -}} + +{{/* +Gets a value from .Values given +Usage: +{{ include "common.utils.getValueFromKey" (dict "key" "path.to.key" "context" $) }} +*/}} +{{- define "common.utils.getValueFromKey" -}} +{{- $splitKey := splitList "." .key -}} +{{- $value := "" -}} +{{- $latestObj := $.context.Values -}} +{{- range $splitKey -}} + {{- if not $latestObj -}} + {{- printf "please review the entire path of '%s' exists in values" $.key | fail -}} + {{- end -}} + {{- $value = ( index $latestObj . ) -}} + {{- $latestObj = $value -}} +{{- end -}} +{{- printf "%v" (default "" $value) -}} +{{- end -}} + +{{/* +Returns first .Values key with a defined value or first of the list if all non-defined +Usage: +{{ include "common.utils.getKeyFromList" (dict "keys" (list "path.to.key1" "path.to.key2") "context" $) }} +*/}} +{{- define "common.utils.getKeyFromList" -}} +{{- $key := first .keys -}} +{{- $reverseKeys := reverse .keys }} +{{- range $reverseKeys }} + {{- $value := include "common.utils.getValueFromKey" (dict "key" . "context" $.context ) }} + {{- if $value -}} + {{- $key = . }} + {{- end -}} +{{- end -}} +{{- printf "%s" $key -}} +{{- end -}} diff --git a/charts/jfrog/artifactory-ha/107.104.10/charts/postgresql/charts/common/templates/_warnings.tpl b/charts/jfrog/artifactory-ha/107.104.10/charts/postgresql/charts/common/templates/_warnings.tpl new file mode 100644 index 0000000000..ae10fa41ee --- /dev/null +++ b/charts/jfrog/artifactory-ha/107.104.10/charts/postgresql/charts/common/templates/_warnings.tpl @@ -0,0 +1,14 @@ +{{/* vim: set filetype=mustache: */}} +{{/* +Warning about using rolling tag. +Usage: +{{ include "common.warnings.rollingTag" .Values.path.to.the.imageRoot }} +*/}} +{{- define "common.warnings.rollingTag" -}} + +{{- if and (contains "bitnami/" .repository) (not (.tag | toString | regexFind "-r\\d+$|sha256:")) }} +WARNING: Rolling tag detected ({{ .repository }}:{{ .tag }}), please note that it is strongly recommended to avoid using rolling tags in a production environment. ++info https://docs.bitnami.com/containers/how-to/understand-rolling-tags-containers/ +{{- end }} + +{{- end -}} diff --git a/charts/jfrog/artifactory-ha/107.104.10/charts/postgresql/charts/common/templates/validations/_cassandra.tpl b/charts/jfrog/artifactory-ha/107.104.10/charts/postgresql/charts/common/templates/validations/_cassandra.tpl new file mode 100644 index 0000000000..8679ddffb1 --- /dev/null +++ b/charts/jfrog/artifactory-ha/107.104.10/charts/postgresql/charts/common/templates/validations/_cassandra.tpl @@ -0,0 +1,72 @@ +{{/* vim: set filetype=mustache: */}} +{{/* +Validate Cassandra required passwords are not empty. + +Usage: +{{ include "common.validations.values.cassandra.passwords" (dict "secret" "secretName" "subchart" false "context" $) }} +Params: + - secret - String - Required. Name of the secret where Cassandra values are stored, e.g: "cassandra-passwords-secret" + - subchart - Boolean - Optional. Whether Cassandra is used as subchart or not. Default: false +*/}} +{{- define "common.validations.values.cassandra.passwords" -}} + {{- $existingSecret := include "common.cassandra.values.existingSecret" . -}} + {{- $enabled := include "common.cassandra.values.enabled" . -}} + {{- $dbUserPrefix := include "common.cassandra.values.key.dbUser" . -}} + {{- $valueKeyPassword := printf "%s.password" $dbUserPrefix -}} + + {{- if and (not $existingSecret) (eq $enabled "true") -}} + {{- $requiredPasswords := list -}} + + {{- $requiredPassword := dict "valueKey" $valueKeyPassword "secret" .secret "field" "cassandra-password" -}} + {{- $requiredPasswords = append $requiredPasswords $requiredPassword -}} + + {{- include "common.validations.values.multiple.empty" (dict "required" $requiredPasswords "context" .context) -}} + + {{- end -}} +{{- end -}} + +{{/* +Auxiliary function to get the right value for existingSecret. + +Usage: +{{ include "common.cassandra.values.existingSecret" (dict "context" $) }} +Params: + - subchart - Boolean - Optional. Whether Cassandra is used as subchart or not. Default: false +*/}} +{{- define "common.cassandra.values.existingSecret" -}} + {{- if .subchart -}} + {{- .context.Values.cassandra.dbUser.existingSecret | quote -}} + {{- else -}} + {{- .context.Values.dbUser.existingSecret | quote -}} + {{- end -}} +{{- end -}} + +{{/* +Auxiliary function to get the right value for enabled cassandra. + +Usage: +{{ include "common.cassandra.values.enabled" (dict "context" $) }} +*/}} +{{- define "common.cassandra.values.enabled" -}} + {{- if .subchart -}} + {{- printf "%v" .context.Values.cassandra.enabled -}} + {{- else -}} + {{- printf "%v" (not .context.Values.enabled) -}} + {{- end -}} +{{- end -}} + +{{/* +Auxiliary function to get the right value for the key dbUser + +Usage: +{{ include "common.cassandra.values.key.dbUser" (dict "subchart" "true" "context" $) }} +Params: + - subchart - Boolean - Optional. Whether Cassandra is used as subchart or not. Default: false +*/}} +{{- define "common.cassandra.values.key.dbUser" -}} + {{- if .subchart -}} + cassandra.dbUser + {{- else -}} + dbUser + {{- end -}} +{{- end -}} diff --git a/charts/jfrog/artifactory-ha/107.104.10/charts/postgresql/charts/common/templates/validations/_mariadb.tpl b/charts/jfrog/artifactory-ha/107.104.10/charts/postgresql/charts/common/templates/validations/_mariadb.tpl new file mode 100644 index 0000000000..bb5ed7253d --- /dev/null +++ b/charts/jfrog/artifactory-ha/107.104.10/charts/postgresql/charts/common/templates/validations/_mariadb.tpl @@ -0,0 +1,103 @@ +{{/* vim: set filetype=mustache: */}} +{{/* +Validate MariaDB required passwords are not empty. + +Usage: +{{ include "common.validations.values.mariadb.passwords" (dict "secret" "secretName" "subchart" false "context" $) }} +Params: + - secret - String - Required. Name of the secret where MariaDB values are stored, e.g: "mysql-passwords-secret" + - subchart - Boolean - Optional. Whether MariaDB is used as subchart or not. Default: false +*/}} +{{- define "common.validations.values.mariadb.passwords" -}} + {{- $existingSecret := include "common.mariadb.values.auth.existingSecret" . -}} + {{- $enabled := include "common.mariadb.values.enabled" . -}} + {{- $architecture := include "common.mariadb.values.architecture" . -}} + {{- $authPrefix := include "common.mariadb.values.key.auth" . -}} + {{- $valueKeyRootPassword := printf "%s.rootPassword" $authPrefix -}} + {{- $valueKeyUsername := printf "%s.username" $authPrefix -}} + {{- $valueKeyPassword := printf "%s.password" $authPrefix -}} + {{- $valueKeyReplicationPassword := printf "%s.replicationPassword" $authPrefix -}} + + {{- if and (not $existingSecret) (eq $enabled "true") -}} + {{- $requiredPasswords := list -}} + + {{- $requiredRootPassword := dict "valueKey" $valueKeyRootPassword "secret" .secret "field" "mariadb-root-password" -}} + {{- $requiredPasswords = append $requiredPasswords $requiredRootPassword -}} + + {{- $valueUsername := include "common.utils.getValueFromKey" (dict "key" $valueKeyUsername "context" .context) }} + {{- if not (empty $valueUsername) -}} + {{- $requiredPassword := dict "valueKey" $valueKeyPassword "secret" .secret "field" "mariadb-password" -}} + {{- $requiredPasswords = append $requiredPasswords $requiredPassword -}} + {{- end -}} + + {{- if (eq $architecture "replication") -}} + {{- $requiredReplicationPassword := dict "valueKey" $valueKeyReplicationPassword "secret" .secret "field" "mariadb-replication-password" -}} + {{- $requiredPasswords = append $requiredPasswords $requiredReplicationPassword -}} + {{- end -}} + + {{- include "common.validations.values.multiple.empty" (dict "required" $requiredPasswords "context" .context) -}} + + {{- end -}} +{{- end -}} + +{{/* +Auxiliary function to get the right value for existingSecret. + +Usage: +{{ include "common.mariadb.values.auth.existingSecret" (dict "context" $) }} +Params: + - subchart - Boolean - Optional. Whether MariaDB is used as subchart or not. Default: false +*/}} +{{- define "common.mariadb.values.auth.existingSecret" -}} + {{- if .subchart -}} + {{- .context.Values.mariadb.auth.existingSecret | quote -}} + {{- else -}} + {{- .context.Values.auth.existingSecret | quote -}} + {{- end -}} +{{- end -}} + +{{/* +Auxiliary function to get the right value for enabled mariadb. + +Usage: +{{ include "common.mariadb.values.enabled" (dict "context" $) }} +*/}} +{{- define "common.mariadb.values.enabled" -}} + {{- if .subchart -}} + {{- printf "%v" .context.Values.mariadb.enabled -}} + {{- else -}} + {{- printf "%v" (not .context.Values.enabled) -}} + {{- end -}} +{{- end -}} + +{{/* +Auxiliary function to get the right value for architecture + +Usage: +{{ include "common.mariadb.values.architecture" (dict "subchart" "true" "context" $) }} +Params: + - subchart - Boolean - Optional. Whether MariaDB is used as subchart or not. Default: false +*/}} +{{- define "common.mariadb.values.architecture" -}} + {{- if .subchart -}} + {{- .context.Values.mariadb.architecture -}} + {{- else -}} + {{- .context.Values.architecture -}} + {{- end -}} +{{- end -}} + +{{/* +Auxiliary function to get the right value for the key auth + +Usage: +{{ include "common.mariadb.values.key.auth" (dict "subchart" "true" "context" $) }} +Params: + - subchart - Boolean - Optional. Whether MariaDB is used as subchart or not. Default: false +*/}} +{{- define "common.mariadb.values.key.auth" -}} + {{- if .subchart -}} + mariadb.auth + {{- else -}} + auth + {{- end -}} +{{- end -}} diff --git a/charts/jfrog/artifactory-ha/107.104.10/charts/postgresql/charts/common/templates/validations/_mongodb.tpl b/charts/jfrog/artifactory-ha/107.104.10/charts/postgresql/charts/common/templates/validations/_mongodb.tpl new file mode 100644 index 0000000000..7d5ecbccb4 --- /dev/null +++ b/charts/jfrog/artifactory-ha/107.104.10/charts/postgresql/charts/common/templates/validations/_mongodb.tpl @@ -0,0 +1,108 @@ +{{/* vim: set filetype=mustache: */}} +{{/* +Validate MongoDB(R) required passwords are not empty. + +Usage: +{{ include "common.validations.values.mongodb.passwords" (dict "secret" "secretName" "subchart" false "context" $) }} +Params: + - secret - String - Required. Name of the secret where MongoDB(R) values are stored, e.g: "mongodb-passwords-secret" + - subchart - Boolean - Optional. Whether MongoDB(R) is used as subchart or not. Default: false +*/}} +{{- define "common.validations.values.mongodb.passwords" -}} + {{- $existingSecret := include "common.mongodb.values.auth.existingSecret" . -}} + {{- $enabled := include "common.mongodb.values.enabled" . -}} + {{- $authPrefix := include "common.mongodb.values.key.auth" . -}} + {{- $architecture := include "common.mongodb.values.architecture" . -}} + {{- $valueKeyRootPassword := printf "%s.rootPassword" $authPrefix -}} + {{- $valueKeyUsername := printf "%s.username" $authPrefix -}} + {{- $valueKeyDatabase := printf "%s.database" $authPrefix -}} + {{- $valueKeyPassword := printf "%s.password" $authPrefix -}} + {{- $valueKeyReplicaSetKey := printf "%s.replicaSetKey" $authPrefix -}} + {{- $valueKeyAuthEnabled := printf "%s.enabled" $authPrefix -}} + + {{- $authEnabled := include "common.utils.getValueFromKey" (dict "key" $valueKeyAuthEnabled "context" .context) -}} + + {{- if and (not $existingSecret) (eq $enabled "true") (eq $authEnabled "true") -}} + {{- $requiredPasswords := list -}} + + {{- $requiredRootPassword := dict "valueKey" $valueKeyRootPassword "secret" .secret "field" "mongodb-root-password" -}} + {{- $requiredPasswords = append $requiredPasswords $requiredRootPassword -}} + + {{- $valueUsername := include "common.utils.getValueFromKey" (dict "key" $valueKeyUsername "context" .context) }} + {{- $valueDatabase := include "common.utils.getValueFromKey" (dict "key" $valueKeyDatabase "context" .context) }} + {{- if and $valueUsername $valueDatabase -}} + {{- $requiredPassword := dict "valueKey" $valueKeyPassword "secret" .secret "field" "mongodb-password" -}} + {{- $requiredPasswords = append $requiredPasswords $requiredPassword -}} + {{- end -}} + + {{- if (eq $architecture "replicaset") -}} + {{- $requiredReplicaSetKey := dict "valueKey" $valueKeyReplicaSetKey "secret" .secret "field" "mongodb-replica-set-key" -}} + {{- $requiredPasswords = append $requiredPasswords $requiredReplicaSetKey -}} + {{- end -}} + + {{- include "common.validations.values.multiple.empty" (dict "required" $requiredPasswords "context" .context) -}} + + {{- end -}} +{{- end -}} + +{{/* +Auxiliary function to get the right value for existingSecret. + +Usage: +{{ include "common.mongodb.values.auth.existingSecret" (dict "context" $) }} +Params: + - subchart - Boolean - Optional. Whether MongoDb is used as subchart or not. Default: false +*/}} +{{- define "common.mongodb.values.auth.existingSecret" -}} + {{- if .subchart -}} + {{- .context.Values.mongodb.auth.existingSecret | quote -}} + {{- else -}} + {{- .context.Values.auth.existingSecret | quote -}} + {{- end -}} +{{- end -}} + +{{/* +Auxiliary function to get the right value for enabled mongodb. + +Usage: +{{ include "common.mongodb.values.enabled" (dict "context" $) }} +*/}} +{{- define "common.mongodb.values.enabled" -}} + {{- if .subchart -}} + {{- printf "%v" .context.Values.mongodb.enabled -}} + {{- else -}} + {{- printf "%v" (not .context.Values.enabled) -}} + {{- end -}} +{{- end -}} + +{{/* +Auxiliary function to get the right value for the key auth + +Usage: +{{ include "common.mongodb.values.key.auth" (dict "subchart" "true" "context" $) }} +Params: + - subchart - Boolean - Optional. Whether MongoDB(R) is used as subchart or not. Default: false +*/}} +{{- define "common.mongodb.values.key.auth" -}} + {{- if .subchart -}} + mongodb.auth + {{- else -}} + auth + {{- end -}} +{{- end -}} + +{{/* +Auxiliary function to get the right value for architecture + +Usage: +{{ include "common.mongodb.values.architecture" (dict "subchart" "true" "context" $) }} +Params: + - subchart - Boolean - Optional. Whether MariaDB is used as subchart or not. Default: false +*/}} +{{- define "common.mongodb.values.architecture" -}} + {{- if .subchart -}} + {{- .context.Values.mongodb.architecture -}} + {{- else -}} + {{- .context.Values.architecture -}} + {{- end -}} +{{- end -}} diff --git a/charts/jfrog/artifactory-ha/107.104.10/charts/postgresql/charts/common/templates/validations/_postgresql.tpl b/charts/jfrog/artifactory-ha/107.104.10/charts/postgresql/charts/common/templates/validations/_postgresql.tpl new file mode 100644 index 0000000000..992bcd3908 --- /dev/null +++ b/charts/jfrog/artifactory-ha/107.104.10/charts/postgresql/charts/common/templates/validations/_postgresql.tpl @@ -0,0 +1,131 @@ +{{/* vim: set filetype=mustache: */}} +{{/* +Validate PostgreSQL required passwords are not empty. + +Usage: +{{ include "common.validations.values.postgresql.passwords" (dict "secret" "secretName" "subchart" false "context" $) }} +Params: + - secret - String - Required. Name of the secret where postgresql values are stored, e.g: "postgresql-passwords-secret" + - subchart - Boolean - Optional. Whether postgresql is used as subchart or not. Default: false +*/}} +{{- define "common.validations.values.postgresql.passwords" -}} + {{- $existingSecret := include "common.postgresql.values.existingSecret" . -}} + {{- $enabled := include "common.postgresql.values.enabled" . -}} + {{- $valueKeyPostgresqlPassword := include "common.postgresql.values.key.postgressPassword" . -}} + {{- $valueKeyPostgresqlReplicationEnabled := include "common.postgresql.values.key.replicationPassword" . -}} + + {{- if and (not $existingSecret) (eq $enabled "true") -}} + {{- $requiredPasswords := list -}} + + {{- $requiredPostgresqlPassword := dict "valueKey" $valueKeyPostgresqlPassword "secret" .secret "field" "postgresql-password" -}} + {{- $requiredPasswords = append $requiredPasswords $requiredPostgresqlPassword -}} + + {{- $enabledReplication := include "common.postgresql.values.enabled.replication" . -}} + {{- if (eq $enabledReplication "true") -}} + {{- $requiredPostgresqlReplicationPassword := dict "valueKey" $valueKeyPostgresqlReplicationEnabled "secret" .secret "field" "postgresql-replication-password" -}} + {{- $requiredPasswords = append $requiredPasswords $requiredPostgresqlReplicationPassword -}} + {{- end -}} + + {{- include "common.validations.values.multiple.empty" (dict "required" $requiredPasswords "context" .context) -}} + {{- end -}} +{{- end -}} + +{{/* +Auxiliary function to decide whether evaluate global values. + +Usage: +{{ include "common.postgresql.values.use.global" (dict "key" "key-of-global" "context" $) }} +Params: + - key - String - Required. Field to be evaluated within global, e.g: "existingSecret" +*/}} +{{- define "common.postgresql.values.use.global" -}} + {{- if .context.Values.global -}} + {{- if .context.Values.global.postgresql -}} + {{- index .context.Values.global.postgresql .key | quote -}} + {{- end -}} + {{- end -}} +{{- end -}} + +{{/* +Auxiliary function to get the right value for existingSecret. + +Usage: +{{ include "common.postgresql.values.existingSecret" (dict "context" $) }} +*/}} +{{- define "common.postgresql.values.existingSecret" -}} + {{- $globalValue := include "common.postgresql.values.use.global" (dict "key" "existingSecret" "context" .context) -}} + + {{- if .subchart -}} + {{- default (.context.Values.postgresql.existingSecret | quote) $globalValue -}} + {{- else -}} + {{- default (.context.Values.existingSecret | quote) $globalValue -}} + {{- end -}} +{{- end -}} + +{{/* +Auxiliary function to get the right value for enabled postgresql. + +Usage: +{{ include "common.postgresql.values.enabled" (dict "context" $) }} +*/}} +{{- define "common.postgresql.values.enabled" -}} + {{- if .subchart -}} + {{- printf "%v" .context.Values.postgresql.enabled -}} + {{- else -}} + {{- printf "%v" (not .context.Values.enabled) -}} + {{- end -}} +{{- end -}} + +{{/* +Auxiliary function to get the right value for the key postgressPassword. + +Usage: +{{ include "common.postgresql.values.key.postgressPassword" (dict "subchart" "true" "context" $) }} +Params: + - subchart - Boolean - Optional. Whether postgresql is used as subchart or not. Default: false +*/}} +{{- define "common.postgresql.values.key.postgressPassword" -}} + {{- $globalValue := include "common.postgresql.values.use.global" (dict "key" "postgresqlUsername" "context" .context) -}} + + {{- if not $globalValue -}} + {{- if .subchart -}} + postgresql.postgresqlPassword + {{- else -}} + postgresqlPassword + {{- end -}} + {{- else -}} + global.postgresql.postgresqlPassword + {{- end -}} +{{- end -}} + +{{/* +Auxiliary function to get the right value for enabled.replication. + +Usage: +{{ include "common.postgresql.values.enabled.replication" (dict "subchart" "true" "context" $) }} +Params: + - subchart - Boolean - Optional. Whether postgresql is used as subchart or not. Default: false +*/}} +{{- define "common.postgresql.values.enabled.replication" -}} + {{- if .subchart -}} + {{- printf "%v" .context.Values.postgresql.replication.enabled -}} + {{- else -}} + {{- printf "%v" .context.Values.replication.enabled -}} + {{- end -}} +{{- end -}} + +{{/* +Auxiliary function to get the right value for the key replication.password. + +Usage: +{{ include "common.postgresql.values.key.replicationPassword" (dict "subchart" "true" "context" $) }} +Params: + - subchart - Boolean - Optional. Whether postgresql is used as subchart or not. Default: false +*/}} +{{- define "common.postgresql.values.key.replicationPassword" -}} + {{- if .subchart -}} + postgresql.replication.password + {{- else -}} + replication.password + {{- end -}} +{{- end -}} diff --git a/charts/jfrog/artifactory-ha/107.104.10/charts/postgresql/charts/common/templates/validations/_redis.tpl b/charts/jfrog/artifactory-ha/107.104.10/charts/postgresql/charts/common/templates/validations/_redis.tpl new file mode 100644 index 0000000000..3e2a47c039 --- /dev/null +++ b/charts/jfrog/artifactory-ha/107.104.10/charts/postgresql/charts/common/templates/validations/_redis.tpl @@ -0,0 +1,72 @@ + +{{/* vim: set filetype=mustache: */}} +{{/* +Validate Redis(TM) required passwords are not empty. + +Usage: +{{ include "common.validations.values.redis.passwords" (dict "secret" "secretName" "subchart" false "context" $) }} +Params: + - secret - String - Required. Name of the secret where redis values are stored, e.g: "redis-passwords-secret" + - subchart - Boolean - Optional. Whether redis is used as subchart or not. Default: false +*/}} +{{- define "common.validations.values.redis.passwords" -}} + {{- $existingSecret := include "common.redis.values.existingSecret" . -}} + {{- $enabled := include "common.redis.values.enabled" . -}} + {{- $valueKeyPrefix := include "common.redis.values.keys.prefix" . -}} + {{- $valueKeyRedisPassword := printf "%s%s" $valueKeyPrefix "password" -}} + {{- $valueKeyRedisUsePassword := printf "%s%s" $valueKeyPrefix "usePassword" -}} + + {{- if and (not $existingSecret) (eq $enabled "true") -}} + {{- $requiredPasswords := list -}} + + {{- $usePassword := include "common.utils.getValueFromKey" (dict "key" $valueKeyRedisUsePassword "context" .context) -}} + {{- if eq $usePassword "true" -}} + {{- $requiredRedisPassword := dict "valueKey" $valueKeyRedisPassword "secret" .secret "field" "redis-password" -}} + {{- $requiredPasswords = append $requiredPasswords $requiredRedisPassword -}} + {{- end -}} + + {{- include "common.validations.values.multiple.empty" (dict "required" $requiredPasswords "context" .context) -}} + {{- end -}} +{{- end -}} + +{{/* +Redis Auxiliary function to get the right value for existingSecret. + +Usage: +{{ include "common.redis.values.existingSecret" (dict "context" $) }} +Params: + - subchart - Boolean - Optional. Whether Redis(TM) is used as subchart or not. Default: false +*/}} +{{- define "common.redis.values.existingSecret" -}} + {{- if .subchart -}} + {{- .context.Values.redis.existingSecret | quote -}} + {{- else -}} + {{- .context.Values.existingSecret | quote -}} + {{- end -}} +{{- end -}} + +{{/* +Auxiliary function to get the right value for enabled redis. + +Usage: +{{ include "common.redis.values.enabled" (dict "context" $) }} +*/}} +{{- define "common.redis.values.enabled" -}} + {{- if .subchart -}} + {{- printf "%v" .context.Values.redis.enabled -}} + {{- else -}} + {{- printf "%v" (not .context.Values.enabled) -}} + {{- end -}} +{{- end -}} + +{{/* +Auxiliary function to get the right prefix path for the values + +Usage: +{{ include "common.redis.values.key.prefix" (dict "subchart" "true" "context" $) }} +Params: + - subchart - Boolean - Optional. Whether redis is used as subchart or not. Default: false +*/}} +{{- define "common.redis.values.keys.prefix" -}} + {{- if .subchart -}}redis.{{- else -}}{{- end -}} +{{- end -}} diff --git a/charts/jfrog/artifactory-ha/107.104.10/charts/postgresql/charts/common/templates/validations/_validations.tpl b/charts/jfrog/artifactory-ha/107.104.10/charts/postgresql/charts/common/templates/validations/_validations.tpl new file mode 100644 index 0000000000..9a814cf40d --- /dev/null +++ b/charts/jfrog/artifactory-ha/107.104.10/charts/postgresql/charts/common/templates/validations/_validations.tpl @@ -0,0 +1,46 @@ +{{/* vim: set filetype=mustache: */}} +{{/* +Validate values must not be empty. + +Usage: +{{- $validateValueConf00 := (dict "valueKey" "path.to.value" "secret" "secretName" "field" "password-00") -}} +{{- $validateValueConf01 := (dict "valueKey" "path.to.value" "secret" "secretName" "field" "password-01") -}} +{{ include "common.validations.values.empty" (dict "required" (list $validateValueConf00 $validateValueConf01) "context" $) }} + +Validate value params: + - valueKey - String - Required. The path to the validating value in the values.yaml, e.g: "mysql.password" + - secret - String - Optional. Name of the secret where the validating value is generated/stored, e.g: "mysql-passwords-secret" + - field - String - Optional. Name of the field in the secret data, e.g: "mysql-password" +*/}} +{{- define "common.validations.values.multiple.empty" -}} + {{- range .required -}} + {{- include "common.validations.values.single.empty" (dict "valueKey" .valueKey "secret" .secret "field" .field "context" $.context) -}} + {{- end -}} +{{- end -}} + +{{/* +Validate a value must not be empty. + +Usage: +{{ include "common.validations.value.empty" (dict "valueKey" "mariadb.password" "secret" "secretName" "field" "my-password" "subchart" "subchart" "context" $) }} + +Validate value params: + - valueKey - String - Required. The path to the validating value in the values.yaml, e.g: "mysql.password" + - secret - String - Optional. Name of the secret where the validating value is generated/stored, e.g: "mysql-passwords-secret" + - field - String - Optional. Name of the field in the secret data, e.g: "mysql-password" + - subchart - String - Optional - Name of the subchart that the validated password is part of. +*/}} +{{- define "common.validations.values.single.empty" -}} + {{- $value := include "common.utils.getValueFromKey" (dict "key" .valueKey "context" .context) }} + {{- $subchart := ternary "" (printf "%s." .subchart) (empty .subchart) }} + + {{- if not $value -}} + {{- $varname := "my-value" -}} + {{- $getCurrentValue := "" -}} + {{- if and .secret .field -}} + {{- $varname = include "common.utils.fieldToEnvVar" . -}} + {{- $getCurrentValue = printf " To get the current value:\n\n %s\n" (include "common.utils.secret.getvalue" .) -}} + {{- end -}} + {{- printf "\n '%s' must not be empty, please add '--set %s%s=$%s' to the command.%s" .valueKey $subchart .valueKey $varname $getCurrentValue -}} + {{- end -}} +{{- end -}} diff --git a/charts/jfrog/artifactory-ha/107.104.10/charts/postgresql/charts/common/values.yaml b/charts/jfrog/artifactory-ha/107.104.10/charts/postgresql/charts/common/values.yaml new file mode 100644 index 0000000000..9ecdc93f58 --- /dev/null +++ b/charts/jfrog/artifactory-ha/107.104.10/charts/postgresql/charts/common/values.yaml @@ -0,0 +1,3 @@ +## bitnami/common +## It is required by CI/CD tools and processes. +exampleValue: common-chart diff --git a/charts/jfrog/artifactory-ha/107.104.10/charts/postgresql/ci/commonAnnotations.yaml b/charts/jfrog/artifactory-ha/107.104.10/charts/postgresql/ci/commonAnnotations.yaml new file mode 100644 index 0000000000..97e18a4cc0 --- /dev/null +++ b/charts/jfrog/artifactory-ha/107.104.10/charts/postgresql/ci/commonAnnotations.yaml @@ -0,0 +1,3 @@ +commonAnnotations: + helm.sh/hook: "\"pre-install, pre-upgrade\"" + helm.sh/hook-weight: "-1" diff --git a/charts/jfrog/artifactory-ha/107.104.10/charts/postgresql/ci/default-values.yaml b/charts/jfrog/artifactory-ha/107.104.10/charts/postgresql/ci/default-values.yaml new file mode 100644 index 0000000000..fc2ba605ad --- /dev/null +++ b/charts/jfrog/artifactory-ha/107.104.10/charts/postgresql/ci/default-values.yaml @@ -0,0 +1 @@ +# Leave this file empty to ensure that CI runs builds against the default configuration in values.yaml. diff --git a/charts/jfrog/artifactory-ha/107.104.10/charts/postgresql/ci/shmvolume-disabled-values.yaml b/charts/jfrog/artifactory-ha/107.104.10/charts/postgresql/ci/shmvolume-disabled-values.yaml new file mode 100644 index 0000000000..347d3b40a8 --- /dev/null +++ b/charts/jfrog/artifactory-ha/107.104.10/charts/postgresql/ci/shmvolume-disabled-values.yaml @@ -0,0 +1,2 @@ +shmVolume: + enabled: false diff --git a/charts/jfrog/artifactory-ha/107.104.10/charts/postgresql/files/README.md b/charts/jfrog/artifactory-ha/107.104.10/charts/postgresql/files/README.md new file mode 100644 index 0000000000..1813a2feaa --- /dev/null +++ b/charts/jfrog/artifactory-ha/107.104.10/charts/postgresql/files/README.md @@ -0,0 +1 @@ +Copy here your postgresql.conf and/or pg_hba.conf files to use it as a config map. diff --git a/charts/jfrog/artifactory-ha/107.104.10/charts/postgresql/files/conf.d/README.md b/charts/jfrog/artifactory-ha/107.104.10/charts/postgresql/files/conf.d/README.md new file mode 100644 index 0000000000..184c1875d5 --- /dev/null +++ b/charts/jfrog/artifactory-ha/107.104.10/charts/postgresql/files/conf.d/README.md @@ -0,0 +1,4 @@ +If you don't want to provide the whole configuration file and only specify certain parameters, you can copy here your extended `.conf` files. +These files will be injected as a config maps and add/overwrite the default configuration using the `include_dir` directive that allows settings to be loaded from files other than the default `postgresql.conf`. + +More info in the [bitnami-docker-postgresql README](https://github.com/bitnami/bitnami-docker-postgresql#configuration-file). diff --git a/charts/jfrog/artifactory-ha/107.104.10/charts/postgresql/files/docker-entrypoint-initdb.d/README.md b/charts/jfrog/artifactory-ha/107.104.10/charts/postgresql/files/docker-entrypoint-initdb.d/README.md new file mode 100644 index 0000000000..cba38091e0 --- /dev/null +++ b/charts/jfrog/artifactory-ha/107.104.10/charts/postgresql/files/docker-entrypoint-initdb.d/README.md @@ -0,0 +1,3 @@ +You can copy here your custom `.sh`, `.sql` or `.sql.gz` file so they are executed during the first boot of the image. + +More info in the [bitnami-docker-postgresql](https://github.com/bitnami/bitnami-docker-postgresql#initializing-a-new-instance) repository. \ No newline at end of file diff --git a/charts/jfrog/artifactory-ha/107.104.10/charts/postgresql/templates/NOTES.txt b/charts/jfrog/artifactory-ha/107.104.10/charts/postgresql/templates/NOTES.txt new file mode 100644 index 0000000000..4e98958c13 --- /dev/null +++ b/charts/jfrog/artifactory-ha/107.104.10/charts/postgresql/templates/NOTES.txt @@ -0,0 +1,59 @@ +** Please be patient while the chart is being deployed ** + +PostgreSQL can be accessed via port {{ template "postgresql.port" . }} on the following DNS name from within your cluster: + + {{ template "common.names.fullname" . }}.{{ .Release.Namespace }}.svc.cluster.local - Read/Write connection +{{- if .Values.replication.enabled }} + {{ template "common.names.fullname" . }}-read.{{ .Release.Namespace }}.svc.cluster.local - Read only connection +{{- end }} + +{{- if not (eq (include "postgresql.username" .) "postgres") }} + +To get the password for "postgres" run: + + export POSTGRES_ADMIN_PASSWORD=$(kubectl get secret --namespace {{ .Release.Namespace }} {{ template "postgresql.secretName" . }} -o jsonpath="{.data.postgresql-postgres-password}" | base64 --decode) +{{- end }} + +To get the password for "{{ template "postgresql.username" . }}" run: + + export POSTGRES_PASSWORD=$(kubectl get secret --namespace {{ .Release.Namespace }} {{ template "postgresql.secretName" . }} -o jsonpath="{.data.postgresql-password}" | base64 --decode) + +To connect to your database run the following command: + + kubectl run {{ template "common.names.fullname" . }}-client --rm --tty -i --restart='Never' --namespace {{ .Release.Namespace }} --image {{ template "postgresql.image" . }} --env="PGPASSWORD=$POSTGRES_PASSWORD" {{- if and (.Values.networkPolicy.enabled) (not .Values.networkPolicy.allowExternal) }} + --labels="{{ template "common.names.fullname" . }}-client=true" {{- end }} --command -- psql --host {{ template "common.names.fullname" . }} -U {{ .Values.postgresqlUsername }} -d {{- if .Values.postgresqlDatabase }} {{ .Values.postgresqlDatabase }}{{- else }} postgres{{- end }} -p {{ template "postgresql.port" . }} + +{{ if and (.Values.networkPolicy.enabled) (not .Values.networkPolicy.allowExternal) }} +Note: Since NetworkPolicy is enabled, only pods with label {{ template "common.names.fullname" . }}-client=true" will be able to connect to this PostgreSQL cluster. +{{- end }} + +To connect to your database from outside the cluster execute the following commands: + +{{- if contains "NodePort" .Values.service.type }} + + export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}") + export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ template "common.names.fullname" . }}) + {{ if (include "postgresql.password" . ) }}PGPASSWORD="$POSTGRES_PASSWORD" {{ end }}psql --host $NODE_IP --port $NODE_PORT -U {{ .Values.postgresqlUsername }} -d {{- if .Values.postgresqlDatabase }} {{ .Values.postgresqlDatabase }}{{- else }} postgres{{- end }} + +{{- else if contains "LoadBalancer" .Values.service.type }} + + NOTE: It may take a few minutes for the LoadBalancer IP to be available. + Watch the status with: 'kubectl get svc --namespace {{ .Release.Namespace }} -w {{ template "common.names.fullname" . }}' + + export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ template "common.names.fullname" . }} --template "{{"{{ range (index .status.loadBalancer.ingress 0) }}{{.}}{{ end }}"}}") + {{ if (include "postgresql.password" . ) }}PGPASSWORD="$POSTGRES_PASSWORD" {{ end }}psql --host $SERVICE_IP --port {{ template "postgresql.port" . }} -U {{ .Values.postgresqlUsername }} -d {{- if .Values.postgresqlDatabase }} {{ .Values.postgresqlDatabase }}{{- else }} postgres{{- end }} + +{{- else if contains "ClusterIP" .Values.service.type }} + + kubectl port-forward --namespace {{ .Release.Namespace }} svc/{{ template "common.names.fullname" . }} {{ template "postgresql.port" . }}:{{ template "postgresql.port" . }} & + {{ if (include "postgresql.password" . ) }}PGPASSWORD="$POSTGRES_PASSWORD" {{ end }}psql --host 127.0.0.1 -U {{ .Values.postgresqlUsername }} -d {{- if .Values.postgresqlDatabase }} {{ .Values.postgresqlDatabase }}{{- else }} postgres{{- end }} -p {{ template "postgresql.port" . }} + +{{- end }} + +{{- include "postgresql.validateValues" . -}} + +{{- include "common.warnings.rollingTag" .Values.image -}} + +{{- $passwordValidationErrors := include "common.validations.values.postgresql.passwords" (dict "secret" (include "common.names.fullname" .) "context" $) -}} + +{{- include "common.errors.upgrade.passwords.empty" (dict "validationErrors" (list $passwordValidationErrors) "context" $) -}} diff --git a/charts/jfrog/artifactory-ha/107.104.10/charts/postgresql/templates/_helpers.tpl b/charts/jfrog/artifactory-ha/107.104.10/charts/postgresql/templates/_helpers.tpl new file mode 100644 index 0000000000..1f98efe789 --- /dev/null +++ b/charts/jfrog/artifactory-ha/107.104.10/charts/postgresql/templates/_helpers.tpl @@ -0,0 +1,337 @@ +{{/* vim: set filetype=mustache: */}} + +{{/* +Expand the name of the chart. +*/}} +{{- define "postgresql.name" -}} +{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}} +{{- end -}} + +{{/* +Create a default fully qualified app name. +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +*/}} +{{- define "postgresql.primary.fullname" -}} +{{- $name := default .Chart.Name .Values.nameOverride -}} +{{- $fullname := default (printf "%s-%s" .Release.Name $name) .Values.fullnameOverride -}} +{{- if .Values.replication.enabled -}} +{{- printf "%s-%s" $fullname "primary" | trunc 63 | trimSuffix "-" -}} +{{- else -}} +{{- printf "%s" $fullname | trunc 63 | trimSuffix "-" -}} +{{- end -}} +{{- end -}} + +{{/* +Return the proper PostgreSQL image name +*/}} +{{- define "postgresql.image" -}} +{{ include "common.images.image" (dict "imageRoot" .Values.image "global" .Values.global) }} +{{- end -}} + +{{/* +Return the proper PostgreSQL metrics image name +*/}} +{{- define "postgresql.metrics.image" -}} +{{ include "common.images.image" (dict "imageRoot" .Values.metrics.image "global" .Values.global) }} +{{- end -}} + +{{/* +Return the proper image name (for the init container volume-permissions image) +*/}} +{{- define "postgresql.volumePermissions.image" -}} +{{ include "common.images.image" (dict "imageRoot" .Values.volumePermissions.image "global" .Values.global) }} +{{- end -}} + +{{/* +Return the proper Docker Image Registry Secret Names +*/}} +{{- define "postgresql.imagePullSecrets" -}} +{{ include "common.images.pullSecrets" (dict "images" (list .Values.image .Values.metrics.image .Values.volumePermissions.image) "global" .Values.global) }} +{{- end -}} + +{{/* +Return PostgreSQL postgres user password +*/}} +{{- define "postgresql.postgres.password" -}} +{{- if .Values.global.postgresql.postgresqlPostgresPassword }} + {{- .Values.global.postgresql.postgresqlPostgresPassword -}} +{{- else if .Values.postgresqlPostgresPassword -}} + {{- .Values.postgresqlPostgresPassword -}} +{{- else -}} + {{- randAlphaNum 10 -}} +{{- end -}} +{{- end -}} + +{{/* +Return PostgreSQL password +*/}} +{{- define "postgresql.password" -}} +{{- if .Values.global.postgresql.postgresqlPassword }} + {{- .Values.global.postgresql.postgresqlPassword -}} +{{- else if .Values.postgresqlPassword -}} + {{- .Values.postgresqlPassword -}} +{{- else -}} + {{- randAlphaNum 10 -}} +{{- end -}} +{{- end -}} + +{{/* +Return PostgreSQL replication password +*/}} +{{- define "postgresql.replication.password" -}} +{{- if .Values.global.postgresql.replicationPassword }} + {{- .Values.global.postgresql.replicationPassword -}} +{{- else if .Values.replication.password -}} + {{- .Values.replication.password -}} +{{- else -}} + {{- randAlphaNum 10 -}} +{{- end -}} +{{- end -}} + +{{/* +Return PostgreSQL username +*/}} +{{- define "postgresql.username" -}} +{{- if .Values.global.postgresql.postgresqlUsername }} + {{- .Values.global.postgresql.postgresqlUsername -}} +{{- else -}} + {{- .Values.postgresqlUsername -}} +{{- end -}} +{{- end -}} + +{{/* +Return PostgreSQL replication username +*/}} +{{- define "postgresql.replication.username" -}} +{{- if .Values.global.postgresql.replicationUser }} + {{- .Values.global.postgresql.replicationUser -}} +{{- else -}} + {{- .Values.replication.user -}} +{{- end -}} +{{- end -}} + +{{/* +Return PostgreSQL port +*/}} +{{- define "postgresql.port" -}} +{{- if .Values.global.postgresql.servicePort }} + {{- .Values.global.postgresql.servicePort -}} +{{- else -}} + {{- .Values.service.port -}} +{{- end -}} +{{- end -}} + +{{/* +Return PostgreSQL created database +*/}} +{{- define "postgresql.database" -}} +{{- if .Values.global.postgresql.postgresqlDatabase }} + {{- .Values.global.postgresql.postgresqlDatabase -}} +{{- else if .Values.postgresqlDatabase -}} + {{- .Values.postgresqlDatabase -}} +{{- end -}} +{{- end -}} + +{{/* +Get the password secret. +*/}} +{{- define "postgresql.secretName" -}} +{{- if .Values.global.postgresql.existingSecret }} + {{- printf "%s" (tpl .Values.global.postgresql.existingSecret $) -}} +{{- else if .Values.existingSecret -}} + {{- printf "%s" (tpl .Values.existingSecret $) -}} +{{- else -}} + {{- printf "%s" (include "common.names.fullname" .) -}} +{{- end -}} +{{- end -}} + +{{/* +Return true if we should use an existingSecret. +*/}} +{{- define "postgresql.useExistingSecret" -}} +{{- if or .Values.global.postgresql.existingSecret .Values.existingSecret -}} + {{- true -}} +{{- end -}} +{{- end -}} + +{{/* +Return true if a secret object should be created +*/}} +{{- define "postgresql.createSecret" -}} +{{- if not (include "postgresql.useExistingSecret" .) -}} + {{- true -}} +{{- end -}} +{{- end -}} + +{{/* +Get the configuration ConfigMap name. +*/}} +{{- define "postgresql.configurationCM" -}} +{{- if .Values.configurationConfigMap -}} +{{- printf "%s" (tpl .Values.configurationConfigMap $) -}} +{{- else -}} +{{- printf "%s-configuration" (include "common.names.fullname" .) -}} +{{- end -}} +{{- end -}} + +{{/* +Get the extended configuration ConfigMap name. +*/}} +{{- define "postgresql.extendedConfigurationCM" -}} +{{- if .Values.extendedConfConfigMap -}} +{{- printf "%s" (tpl .Values.extendedConfConfigMap $) -}} +{{- else -}} +{{- printf "%s-extended-configuration" (include "common.names.fullname" .) -}} +{{- end -}} +{{- end -}} + +{{/* +Return true if a configmap should be mounted with PostgreSQL configuration +*/}} +{{- define "postgresql.mountConfigurationCM" -}} +{{- if or (.Files.Glob "files/postgresql.conf") (.Files.Glob "files/pg_hba.conf") .Values.postgresqlConfiguration .Values.pgHbaConfiguration .Values.configurationConfigMap }} + {{- true -}} +{{- end -}} +{{- end -}} + +{{/* +Get the initialization scripts ConfigMap name. +*/}} +{{- define "postgresql.initdbScriptsCM" -}} +{{- if .Values.initdbScriptsConfigMap -}} +{{- printf "%s" (tpl .Values.initdbScriptsConfigMap $) -}} +{{- else -}} +{{- printf "%s-init-scripts" (include "common.names.fullname" .) -}} +{{- end -}} +{{- end -}} + +{{/* +Get the initialization scripts Secret name. +*/}} +{{- define "postgresql.initdbScriptsSecret" -}} +{{- printf "%s" (tpl .Values.initdbScriptsSecret $) -}} +{{- end -}} + +{{/* +Get the metrics ConfigMap name. +*/}} +{{- define "postgresql.metricsCM" -}} +{{- printf "%s-metrics" (include "common.names.fullname" .) -}} +{{- end -}} + +{{/* +Get the readiness probe command +*/}} +{{- define "postgresql.readinessProbeCommand" -}} +- | +{{- if (include "postgresql.database" .) }} + exec pg_isready -U {{ include "postgresql.username" . | quote }} -d "dbname={{ include "postgresql.database" . }} {{- if and .Values.tls.enabled .Values.tls.certCAFilename }} sslcert={{ include "postgresql.tlsCert" . }} sslkey={{ include "postgresql.tlsCertKey" . }}{{- end }}" -h 127.0.0.1 -p {{ template "postgresql.port" . }} +{{- else }} + exec pg_isready -U {{ include "postgresql.username" . | quote }} {{- if and .Values.tls.enabled .Values.tls.certCAFilename }} -d "sslcert={{ include "postgresql.tlsCert" . }} sslkey={{ include "postgresql.tlsCertKey" . }}"{{- end }} -h 127.0.0.1 -p {{ template "postgresql.port" . }} +{{- end }} +{{- if contains "bitnami/" .Values.image.repository }} + [ -f /opt/bitnami/postgresql/tmp/.initialized ] || [ -f /bitnami/postgresql/.initialized ] +{{- end -}} +{{- end -}} + +{{/* +Compile all warnings into a single message, and call fail. +*/}} +{{- define "postgresql.validateValues" -}} +{{- $messages := list -}} +{{- $messages := append $messages (include "postgresql.validateValues.ldapConfigurationMethod" .) -}} +{{- $messages := append $messages (include "postgresql.validateValues.psp" .) -}} +{{- $messages := append $messages (include "postgresql.validateValues.tls" .) -}} +{{- $messages := without $messages "" -}} +{{- $message := join "\n" $messages -}} + +{{- if $message -}} +{{- printf "\nVALUES VALIDATION:\n%s" $message | fail -}} +{{- end -}} +{{- end -}} + +{{/* +Validate values of Postgresql - If ldap.url is used then you don't need the other settings for ldap +*/}} +{{- define "postgresql.validateValues.ldapConfigurationMethod" -}} +{{- if and .Values.ldap.enabled (and (not (empty .Values.ldap.url)) (not (empty .Values.ldap.server))) }} +postgresql: ldap.url, ldap.server + You cannot set both `ldap.url` and `ldap.server` at the same time. + Please provide a unique way to configure LDAP. + More info at https://www.postgresql.org/docs/current/auth-ldap.html +{{- end -}} +{{- end -}} + +{{/* +Validate values of Postgresql - If PSP is enabled RBAC should be enabled too +*/}} +{{- define "postgresql.validateValues.psp" -}} +{{- if and .Values.psp.create (not .Values.rbac.create) }} +postgresql: psp.create, rbac.create + RBAC should be enabled if PSP is enabled in order for PSP to work. + More info at https://kubernetes.io/docs/concepts/policy/pod-security-policy/#authorizing-policies +{{- end -}} +{{- end -}} + +{{/* +Return the appropriate apiVersion for podsecuritypolicy. +*/}} +{{- define "podsecuritypolicy.apiVersion" -}} +{{- if semverCompare "<1.10-0" .Capabilities.KubeVersion.GitVersion -}} +{{- print "extensions/v1beta1" -}} +{{- else -}} +{{- print "policy/v1beta1" -}} +{{- end -}} +{{- end -}} + +{{/* +Return the appropriate apiVersion for networkpolicy. +*/}} +{{- define "postgresql.networkPolicy.apiVersion" -}} +{{- if semverCompare ">=1.4-0, <1.7-0" .Capabilities.KubeVersion.GitVersion -}} +"extensions/v1beta1" +{{- else if semverCompare "^1.7-0" .Capabilities.KubeVersion.GitVersion -}} +"networking.k8s.io/v1" +{{- end -}} +{{- end -}} + +{{/* +Validate values of Postgresql TLS - When TLS is enabled, so must be VolumePermissions +*/}} +{{- define "postgresql.validateValues.tls" -}} +{{- if and .Values.tls.enabled (not .Values.volumePermissions.enabled) }} +postgresql: tls.enabled, volumePermissions.enabled + When TLS is enabled you must enable volumePermissions as well to ensure certificates files have + the right permissions. +{{- end -}} +{{- end -}} + +{{/* +Return the path to the cert file. +*/}} +{{- define "postgresql.tlsCert" -}} +{{- required "Certificate filename is required when TLS in enabled" .Values.tls.certFilename | printf "/opt/bitnami/postgresql/certs/%s" -}} +{{- end -}} + +{{/* +Return the path to the cert key file. +*/}} +{{- define "postgresql.tlsCertKey" -}} +{{- required "Certificate Key filename is required when TLS in enabled" .Values.tls.certKeyFilename | printf "/opt/bitnami/postgresql/certs/%s" -}} +{{- end -}} + +{{/* +Return the path to the CA cert file. +*/}} +{{- define "postgresql.tlsCACert" -}} +{{- printf "/opt/bitnami/postgresql/certs/%s" .Values.tls.certCAFilename -}} +{{- end -}} + +{{/* +Return the path to the CRL file. +*/}} +{{- define "postgresql.tlsCRL" -}} +{{- if .Values.tls.crlFilename -}} +{{- printf "/opt/bitnami/postgresql/certs/%s" .Values.tls.crlFilename -}} +{{- end -}} +{{- end -}} diff --git a/charts/jfrog/artifactory-ha/107.104.10/charts/postgresql/templates/configmap.yaml b/charts/jfrog/artifactory-ha/107.104.10/charts/postgresql/templates/configmap.yaml new file mode 100644 index 0000000000..3a5ea18ae9 --- /dev/null +++ b/charts/jfrog/artifactory-ha/107.104.10/charts/postgresql/templates/configmap.yaml @@ -0,0 +1,31 @@ +{{ if and (or (.Files.Glob "files/postgresql.conf") (.Files.Glob "files/pg_hba.conf") .Values.postgresqlConfiguration .Values.pgHbaConfiguration) (not .Values.configurationConfigMap) }} +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ template "common.names.fullname" . }}-configuration + labels: + {{- include "common.labels.standard" . | nindent 4 }} + {{- if .Values.commonAnnotations }} + annotations: {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} + {{- end }} + namespace: {{ .Release.Namespace }} +data: +{{- if (.Files.Glob "files/postgresql.conf") }} +{{ (.Files.Glob "files/postgresql.conf").AsConfig | indent 2 }} +{{- else if .Values.postgresqlConfiguration }} + postgresql.conf: | +{{- range $key, $value := default dict .Values.postgresqlConfiguration }} + {{- if kindIs "string" $value }} + {{ $key | snakecase }} = '{{ $value }}' + {{- else }} + {{ $key | snakecase }} = {{ $value }} + {{- end }} +{{- end }} +{{- end }} +{{- if (.Files.Glob "files/pg_hba.conf") }} +{{ (.Files.Glob "files/pg_hba.conf").AsConfig | indent 2 }} +{{- else if .Values.pgHbaConfiguration }} + pg_hba.conf: | +{{ .Values.pgHbaConfiguration | indent 4 }} +{{- end }} +{{ end }} diff --git a/charts/jfrog/artifactory-ha/107.104.10/charts/postgresql/templates/extended-config-configmap.yaml b/charts/jfrog/artifactory-ha/107.104.10/charts/postgresql/templates/extended-config-configmap.yaml new file mode 100644 index 0000000000..b0dad253b5 --- /dev/null +++ b/charts/jfrog/artifactory-ha/107.104.10/charts/postgresql/templates/extended-config-configmap.yaml @@ -0,0 +1,26 @@ +{{- if and (or (.Files.Glob "files/conf.d/*.conf") .Values.postgresqlExtendedConf) (not .Values.extendedConfConfigMap)}} +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ template "common.names.fullname" . }}-extended-configuration + labels: + {{- include "common.labels.standard" . | nindent 4 }} + {{- if .Values.commonAnnotations }} + annotations: {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} + {{- end }} + namespace: {{ .Release.Namespace }} +data: +{{- with .Files.Glob "files/conf.d/*.conf" }} +{{ .AsConfig | indent 2 }} +{{- end }} +{{ with .Values.postgresqlExtendedConf }} + override.conf: | +{{- range $key, $value := . }} + {{- if kindIs "string" $value }} + {{ $key | snakecase }} = '{{ $value }}' + {{- else }} + {{ $key | snakecase }} = {{ $value }} + {{- end }} +{{- end }} +{{- end }} +{{- end }} diff --git a/charts/jfrog/artifactory-ha/107.104.10/charts/postgresql/templates/extra-list.yaml b/charts/jfrog/artifactory-ha/107.104.10/charts/postgresql/templates/extra-list.yaml new file mode 100644 index 0000000000..9ac65f9e16 --- /dev/null +++ b/charts/jfrog/artifactory-ha/107.104.10/charts/postgresql/templates/extra-list.yaml @@ -0,0 +1,4 @@ +{{- range .Values.extraDeploy }} +--- +{{ include "common.tplvalues.render" (dict "value" . "context" $) }} +{{- end }} diff --git a/charts/jfrog/artifactory-ha/107.104.10/charts/postgresql/templates/initialization-configmap.yaml b/charts/jfrog/artifactory-ha/107.104.10/charts/postgresql/templates/initialization-configmap.yaml new file mode 100644 index 0000000000..7796c67a93 --- /dev/null +++ b/charts/jfrog/artifactory-ha/107.104.10/charts/postgresql/templates/initialization-configmap.yaml @@ -0,0 +1,25 @@ +{{- if and (or (.Files.Glob "files/docker-entrypoint-initdb.d/*.{sh,sql,sql.gz}") .Values.initdbScripts) (not .Values.initdbScriptsConfigMap) }} +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ template "common.names.fullname" . }}-init-scripts + labels: + {{- include "common.labels.standard" . | nindent 4 }} + {{- if .Values.commonAnnotations }} + annotations: {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} + {{- end }} + namespace: {{ .Release.Namespace }} +{{- with .Files.Glob "files/docker-entrypoint-initdb.d/*.sql.gz" }} +binaryData: +{{- range $path, $bytes := . }} + {{ base $path }}: {{ $.Files.Get $path | b64enc | quote }} +{{- end }} +{{- end }} +data: +{{- with .Files.Glob "files/docker-entrypoint-initdb.d/*.{sh,sql}" }} +{{ .AsConfig | indent 2 }} +{{- end }} +{{- with .Values.initdbScripts }} +{{ toYaml . | indent 2 }} +{{- end }} +{{- end }} diff --git a/charts/jfrog/artifactory-ha/107.104.10/charts/postgresql/templates/metrics-configmap.yaml b/charts/jfrog/artifactory-ha/107.104.10/charts/postgresql/templates/metrics-configmap.yaml new file mode 100644 index 0000000000..fa539582bb --- /dev/null +++ b/charts/jfrog/artifactory-ha/107.104.10/charts/postgresql/templates/metrics-configmap.yaml @@ -0,0 +1,14 @@ +{{- if and .Values.metrics.enabled .Values.metrics.customMetrics }} +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ template "postgresql.metricsCM" . }} + labels: + {{- include "common.labels.standard" . | nindent 4 }} + {{- if .Values.commonAnnotations }} + annotations: {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} + {{- end }} + namespace: {{ .Release.Namespace }} +data: + custom-metrics.yaml: {{ toYaml .Values.metrics.customMetrics | quote }} +{{- end }} diff --git a/charts/jfrog/artifactory-ha/107.104.10/charts/postgresql/templates/metrics-svc.yaml b/charts/jfrog/artifactory-ha/107.104.10/charts/postgresql/templates/metrics-svc.yaml new file mode 100644 index 0000000000..af8b67e2ff --- /dev/null +++ b/charts/jfrog/artifactory-ha/107.104.10/charts/postgresql/templates/metrics-svc.yaml @@ -0,0 +1,26 @@ +{{- if .Values.metrics.enabled }} +apiVersion: v1 +kind: Service +metadata: + name: {{ template "common.names.fullname" . }}-metrics + labels: + {{- include "common.labels.standard" . | nindent 4 }} + annotations: + {{- if .Values.commonAnnotations }} + {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} + {{- end }} + {{- toYaml .Values.metrics.service.annotations | nindent 4 }} + namespace: {{ .Release.Namespace }} +spec: + type: {{ .Values.metrics.service.type }} + {{- if and (eq .Values.metrics.service.type "LoadBalancer") .Values.metrics.service.loadBalancerIP }} + loadBalancerIP: {{ .Values.metrics.service.loadBalancerIP }} + {{- end }} + ports: + - name: http-metrics + port: 9187 + targetPort: http-metrics + selector: + {{- include "common.labels.matchLabels" . | nindent 4 }} + role: primary +{{- end }} diff --git a/charts/jfrog/artifactory-ha/107.104.10/charts/postgresql/templates/networkpolicy.yaml b/charts/jfrog/artifactory-ha/107.104.10/charts/postgresql/templates/networkpolicy.yaml new file mode 100644 index 0000000000..4f2740ea0c --- /dev/null +++ b/charts/jfrog/artifactory-ha/107.104.10/charts/postgresql/templates/networkpolicy.yaml @@ -0,0 +1,39 @@ +{{- if .Values.networkPolicy.enabled }} +kind: NetworkPolicy +apiVersion: {{ template "postgresql.networkPolicy.apiVersion" . }} +metadata: + name: {{ template "common.names.fullname" . }} + labels: + {{- include "common.labels.standard" . | nindent 4 }} + {{- if .Values.commonAnnotations }} + annotations: {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} + {{- end }} + namespace: {{ .Release.Namespace }} +spec: + podSelector: + matchLabels: + {{- include "common.labels.matchLabels" . | nindent 6 }} + ingress: + # Allow inbound connections + - ports: + - port: {{ template "postgresql.port" . }} + {{- if not .Values.networkPolicy.allowExternal }} + from: + - podSelector: + matchLabels: + {{ template "common.names.fullname" . }}-client: "true" + {{- if .Values.networkPolicy.explicitNamespacesSelector }} + namespaceSelector: +{{ toYaml .Values.networkPolicy.explicitNamespacesSelector | indent 12 }} + {{- end }} + - podSelector: + matchLabels: + {{- include "common.labels.matchLabels" . | nindent 14 }} + role: read + {{- end }} + {{- if .Values.metrics.enabled }} + # Allow prometheus scrapes + - ports: + - port: 9187 + {{- end }} +{{- end }} diff --git a/charts/jfrog/artifactory-ha/107.104.10/charts/postgresql/templates/podsecuritypolicy.yaml b/charts/jfrog/artifactory-ha/107.104.10/charts/postgresql/templates/podsecuritypolicy.yaml new file mode 100644 index 0000000000..0c49694fad --- /dev/null +++ b/charts/jfrog/artifactory-ha/107.104.10/charts/postgresql/templates/podsecuritypolicy.yaml @@ -0,0 +1,38 @@ +{{- if .Values.psp.create }} +apiVersion: {{ include "podsecuritypolicy.apiVersion" . }} +kind: PodSecurityPolicy +metadata: + name: {{ template "common.names.fullname" . }} + labels: + {{- include "common.labels.standard" . | nindent 4 }} + {{- if .Values.commonAnnotations }} + annotations: {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} + {{- end }} + namespace: {{ .Release.Namespace }} +spec: + privileged: false + volumes: + - 'configMap' + - 'secret' + - 'persistentVolumeClaim' + - 'emptyDir' + - 'projected' + hostNetwork: false + hostIPC: false + hostPID: false + runAsUser: + rule: 'RunAsAny' + seLinux: + rule: 'RunAsAny' + supplementalGroups: + rule: 'MustRunAs' + ranges: + - min: 1 + max: 65535 + fsGroup: + rule: 'MustRunAs' + ranges: + - min: 1 + max: 65535 + readOnlyRootFilesystem: false +{{- end }} diff --git a/charts/jfrog/artifactory-ha/107.104.10/charts/postgresql/templates/prometheusrule.yaml b/charts/jfrog/artifactory-ha/107.104.10/charts/postgresql/templates/prometheusrule.yaml new file mode 100644 index 0000000000..d0f408c78f --- /dev/null +++ b/charts/jfrog/artifactory-ha/107.104.10/charts/postgresql/templates/prometheusrule.yaml @@ -0,0 +1,23 @@ +{{- if and .Values.metrics.enabled .Values.metrics.prometheusRule.enabled }} +apiVersion: monitoring.coreos.com/v1 +kind: PrometheusRule +metadata: + name: {{ template "common.names.fullname" . }} +{{- with .Values.metrics.prometheusRule.namespace }} + namespace: {{ . }} +{{- end }} + labels: + {{- include "common.labels.standard" . | nindent 4 }} + {{- with .Values.metrics.prometheusRule.additionalLabels }} + {{- toYaml . | nindent 4 }} + {{- end }} + {{- if .Values.commonAnnotations }} + annotations: {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} + {{- end }} +spec: +{{- with .Values.metrics.prometheusRule.rules }} + groups: + - name: {{ template "postgresql.name" $ }} + rules: {{ tpl (toYaml .) $ | nindent 8 }} +{{- end }} +{{- end }} diff --git a/charts/jfrog/artifactory-ha/107.104.10/charts/postgresql/templates/role.yaml b/charts/jfrog/artifactory-ha/107.104.10/charts/postgresql/templates/role.yaml new file mode 100644 index 0000000000..017a5716b1 --- /dev/null +++ b/charts/jfrog/artifactory-ha/107.104.10/charts/postgresql/templates/role.yaml @@ -0,0 +1,20 @@ +{{- if .Values.rbac.create }} +kind: Role +apiVersion: {{ include "common.capabilities.rbac.apiVersion" . }} +metadata: + name: {{ template "common.names.fullname" . }} + labels: + {{- include "common.labels.standard" . | nindent 4 }} + {{- if .Values.commonAnnotations }} + annotations: {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} + {{- end }} + namespace: {{ .Release.Namespace }} +rules: + {{- if .Values.psp.create }} + - apiGroups: ["extensions"] + resources: ["podsecuritypolicies"] + verbs: ["use"] + resourceNames: + - {{ template "common.names.fullname" . }} + {{- end }} +{{- end }} diff --git a/charts/jfrog/artifactory-ha/107.104.10/charts/postgresql/templates/rolebinding.yaml b/charts/jfrog/artifactory-ha/107.104.10/charts/postgresql/templates/rolebinding.yaml new file mode 100644 index 0000000000..189775a15a --- /dev/null +++ b/charts/jfrog/artifactory-ha/107.104.10/charts/postgresql/templates/rolebinding.yaml @@ -0,0 +1,20 @@ +{{- if .Values.rbac.create }} +kind: RoleBinding +apiVersion: {{ include "common.capabilities.rbac.apiVersion" . }} +metadata: + name: {{ template "common.names.fullname" . }} + labels: + {{- include "common.labels.standard" . | nindent 4 }} + {{- if .Values.commonAnnotations }} + annotations: {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} + {{- end }} + namespace: {{ .Release.Namespace }} +roleRef: + kind: Role + name: {{ template "common.names.fullname" . }} + apiGroup: rbac.authorization.k8s.io +subjects: + - kind: ServiceAccount + name: {{ default (include "common.names.fullname" . ) .Values.serviceAccount.name }} + namespace: {{ .Release.Namespace }} +{{- end }} diff --git a/charts/jfrog/artifactory-ha/107.104.10/charts/postgresql/templates/secrets.yaml b/charts/jfrog/artifactory-ha/107.104.10/charts/postgresql/templates/secrets.yaml new file mode 100644 index 0000000000..d492cd593b --- /dev/null +++ b/charts/jfrog/artifactory-ha/107.104.10/charts/postgresql/templates/secrets.yaml @@ -0,0 +1,24 @@ +{{- if (include "postgresql.createSecret" .) }} +apiVersion: v1 +kind: Secret +metadata: + name: {{ template "common.names.fullname" . }} + labels: + {{- include "common.labels.standard" . | nindent 4 }} + {{- if .Values.commonAnnotations }} + annotations: {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} + {{- end }} + namespace: {{ .Release.Namespace }} +type: Opaque +data: + {{- if not (eq (include "postgresql.username" .) "postgres") }} + postgresql-postgres-password: {{ include "postgresql.postgres.password" . | b64enc | quote }} + {{- end }} + postgresql-password: {{ include "postgresql.password" . | b64enc | quote }} + {{- if .Values.replication.enabled }} + postgresql-replication-password: {{ include "postgresql.replication.password" . | b64enc | quote }} + {{- end }} + {{- if (and .Values.ldap.enabled .Values.ldap.bind_password)}} + postgresql-ldap-password: {{ .Values.ldap.bind_password | b64enc | quote }} + {{- end }} +{{- end -}} diff --git a/charts/jfrog/artifactory-ha/107.104.10/charts/postgresql/templates/serviceaccount.yaml b/charts/jfrog/artifactory-ha/107.104.10/charts/postgresql/templates/serviceaccount.yaml new file mode 100644 index 0000000000..03f0f50e7d --- /dev/null +++ b/charts/jfrog/artifactory-ha/107.104.10/charts/postgresql/templates/serviceaccount.yaml @@ -0,0 +1,12 @@ +{{- if and (.Values.serviceAccount.enabled) (not .Values.serviceAccount.name) }} +apiVersion: v1 +kind: ServiceAccount +metadata: + labels: + {{- include "common.labels.standard" . | nindent 4 }} + name: {{ template "common.names.fullname" . }} + {{- if .Values.commonAnnotations }} + annotations: {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} + {{- end }} + namespace: {{ .Release.Namespace }} +{{- end }} diff --git a/charts/jfrog/artifactory-ha/107.104.10/charts/postgresql/templates/servicemonitor.yaml b/charts/jfrog/artifactory-ha/107.104.10/charts/postgresql/templates/servicemonitor.yaml new file mode 100644 index 0000000000..587ce85b87 --- /dev/null +++ b/charts/jfrog/artifactory-ha/107.104.10/charts/postgresql/templates/servicemonitor.yaml @@ -0,0 +1,33 @@ +{{- if and .Values.metrics.enabled .Values.metrics.serviceMonitor.enabled }} +apiVersion: monitoring.coreos.com/v1 +kind: ServiceMonitor +metadata: + name: {{ include "common.names.fullname" . }} + {{- if .Values.metrics.serviceMonitor.namespace }} + namespace: {{ .Values.metrics.serviceMonitor.namespace }} + {{- end }} + labels: + {{- include "common.labels.standard" . | nindent 4 }} + {{- if .Values.metrics.serviceMonitor.additionalLabels }} + {{- toYaml .Values.metrics.serviceMonitor.additionalLabels | nindent 4 }} + {{- end }} + {{- if .Values.commonAnnotations }} + annotations: {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} + {{- end }} + +spec: + endpoints: + - port: http-metrics + {{- if .Values.metrics.serviceMonitor.interval }} + interval: {{ .Values.metrics.serviceMonitor.interval }} + {{- end }} + {{- if .Values.metrics.serviceMonitor.scrapeTimeout }} + scrapeTimeout: {{ .Values.metrics.serviceMonitor.scrapeTimeout }} + {{- end }} + namespaceSelector: + matchNames: + - {{ .Release.Namespace }} + selector: + matchLabels: + {{- include "common.labels.matchLabels" . | nindent 6 }} +{{- end }} diff --git a/charts/jfrog/artifactory-ha/107.104.10/charts/postgresql/templates/statefulset-readreplicas.yaml b/charts/jfrog/artifactory-ha/107.104.10/charts/postgresql/templates/statefulset-readreplicas.yaml new file mode 100644 index 0000000000..b038299bf6 --- /dev/null +++ b/charts/jfrog/artifactory-ha/107.104.10/charts/postgresql/templates/statefulset-readreplicas.yaml @@ -0,0 +1,411 @@ +{{- if .Values.replication.enabled }} +{{- $readReplicasResources := coalesce .Values.readReplicas.resources .Values.resources -}} +apiVersion: {{ include "common.capabilities.statefulset.apiVersion" . }} +kind: StatefulSet +metadata: + name: "{{ template "common.names.fullname" . }}-read" + labels: {{- include "common.labels.standard" . | nindent 4 }} + app.kubernetes.io/component: read +{{- with .Values.readReplicas.labels }} +{{ toYaml . | indent 4 }} +{{- end }} + annotations: + {{- if .Values.commonAnnotations }} + {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} + {{- end }} + {{- with .Values.readReplicas.annotations }} + {{- toYaml . | nindent 4 }} + {{- end }} + namespace: {{ .Release.Namespace }} +spec: + serviceName: {{ template "common.names.fullname" . }}-headless + replicas: {{ .Values.replication.readReplicas }} + selector: + matchLabels: + {{- include "common.labels.matchLabels" . | nindent 6 }} + role: read + template: + metadata: + name: {{ template "common.names.fullname" . }} + labels: + {{- include "common.labels.standard" . | nindent 8 }} + app.kubernetes.io/component: read + role: read +{{- with .Values.readReplicas.podLabels }} +{{ toYaml . | indent 8 }} +{{- end }} +{{- with .Values.readReplicas.podAnnotations }} + annotations: +{{ toYaml . | indent 8 }} +{{- end }} + spec: + {{- if .Values.schedulerName }} + schedulerName: "{{ .Values.schedulerName }}" + {{- end }} +{{- include "postgresql.imagePullSecrets" . | indent 6 }} + {{- if .Values.readReplicas.affinity }} + affinity: {{- include "common.tplvalues.render" (dict "value" .Values.readReplicas.affinity "context" $) | nindent 8 }} + {{- else }} + affinity: + podAffinity: {{- include "common.affinities.pods" (dict "type" .Values.readReplicas.podAffinityPreset "component" "read" "context" $) | nindent 10 }} + podAntiAffinity: {{- include "common.affinities.pods" (dict "type" .Values.readReplicas.podAntiAffinityPreset "component" "read" "context" $) | nindent 10 }} + nodeAffinity: {{- include "common.affinities.nodes" (dict "type" .Values.readReplicas.nodeAffinityPreset.type "key" .Values.readReplicas.nodeAffinityPreset.key "values" .Values.readReplicas.nodeAffinityPreset.values) | nindent 10 }} + {{- end }} + {{- if .Values.readReplicas.nodeSelector }} + nodeSelector: {{- include "common.tplvalues.render" (dict "value" .Values.readReplicas.nodeSelector "context" $) | nindent 8 }} + {{- end }} + {{- if .Values.readReplicas.tolerations }} + tolerations: {{- include "common.tplvalues.render" (dict "value" .Values.readReplicas.tolerations "context" $) | nindent 8 }} + {{- end }} + {{- if .Values.terminationGracePeriodSeconds }} + terminationGracePeriodSeconds: {{ .Values.terminationGracePeriodSeconds }} + {{- end }} + {{- if .Values.securityContext.enabled }} + securityContext: {{- omit .Values.securityContext "enabled" | toYaml | nindent 8 }} + {{- end }} + {{- if .Values.serviceAccount.enabled }} + serviceAccountName: {{ default (include "common.names.fullname" . ) .Values.serviceAccount.name}} + {{- end }} + {{- if or .Values.readReplicas.extraInitContainers (and .Values.volumePermissions.enabled (or .Values.persistence.enabled (and .Values.shmVolume.enabled .Values.shmVolume.chmod.enabled))) }} + initContainers: + {{- if and .Values.volumePermissions.enabled (or .Values.persistence.enabled (and .Values.shmVolume.enabled .Values.shmVolume.chmod.enabled) .Values.tls.enabled) }} + - name: init-chmod-data + image: {{ template "postgresql.volumePermissions.image" . }} + imagePullPolicy: {{ .Values.volumePermissions.image.pullPolicy | quote }} + {{- if .Values.resources }} + resources: {{- toYaml .Values.resources | nindent 12 }} + {{- end }} + command: + - /bin/sh + - -cx + - | + {{- if .Values.persistence.enabled }} + {{- if eq ( toString ( .Values.volumePermissions.securityContext.runAsUser )) "auto" }} + chown `id -u`:`id -G | cut -d " " -f2` {{ .Values.persistence.mountPath }} + {{- else }} + chown {{ .Values.containerSecurityContext.runAsUser }}:{{ .Values.securityContext.fsGroup }} {{ .Values.persistence.mountPath }} + {{- end }} + mkdir -p {{ .Values.persistence.mountPath }}/data {{- if (include "postgresql.mountConfigurationCM" .) }} {{ .Values.persistence.mountPath }}/conf {{- end }} + chmod 700 {{ .Values.persistence.mountPath }}/data {{- if (include "postgresql.mountConfigurationCM" .) }} {{ .Values.persistence.mountPath }}/conf {{- end }} + find {{ .Values.persistence.mountPath }} -mindepth 1 -maxdepth 1 {{- if not (include "postgresql.mountConfigurationCM" .) }} -not -name "conf" {{- end }} -not -name ".snapshot" -not -name "lost+found" | \ + {{- if eq ( toString ( .Values.volumePermissions.securityContext.runAsUser )) "auto" }} + xargs chown -R `id -u`:`id -G | cut -d " " -f2` + {{- else }} + xargs chown -R {{ .Values.containerSecurityContext.runAsUser }}:{{ .Values.securityContext.fsGroup }} + {{- end }} + {{- end }} + {{- if and .Values.shmVolume.enabled .Values.shmVolume.chmod.enabled }} + chmod -R 777 /dev/shm + {{- end }} + {{- if .Values.tls.enabled }} + cp /tmp/certs/* /opt/bitnami/postgresql/certs/ + {{- if eq ( toString ( .Values.volumePermissions.securityContext.runAsUser )) "auto" }} + chown -R `id -u`:`id -G | cut -d " " -f2` /opt/bitnami/postgresql/certs/ + {{- else }} + chown -R {{ .Values.containerSecurityContext.runAsUser }}:{{ .Values.securityContext.fsGroup }} /opt/bitnami/postgresql/certs/ + {{- end }} + chmod 600 {{ template "postgresql.tlsCertKey" . }} + {{- end }} + {{- if eq ( toString ( .Values.volumePermissions.securityContext.runAsUser )) "auto" }} + securityContext: {{- omit .Values.volumePermissions.securityContext "runAsUser" | toYaml | nindent 12 }} + {{- else }} + securityContext: {{- .Values.volumePermissions.securityContext | toYaml | nindent 12 }} + {{- end }} + volumeMounts: + {{ if .Values.persistence.enabled }} + - name: data + mountPath: {{ .Values.persistence.mountPath }} + subPath: {{ .Values.persistence.subPath }} + {{- end }} + {{- if .Values.shmVolume.enabled }} + - name: dshm + mountPath: /dev/shm + {{- end }} + {{- if .Values.tls.enabled }} + - name: raw-certificates + mountPath: /tmp/certs + - name: postgresql-certificates + mountPath: /opt/bitnami/postgresql/certs + {{- end }} + {{- end }} + {{- if .Values.readReplicas.extraInitContainers }} + {{- include "common.tplvalues.render" ( dict "value" .Values.readReplicas.extraInitContainers "context" $ ) | nindent 8 }} + {{- end }} + {{- end }} + {{- if .Values.readReplicas.priorityClassName }} + priorityClassName: {{ .Values.readReplicas.priorityClassName }} + {{- end }} + containers: + - name: {{ template "common.names.fullname" . }} + image: {{ template "postgresql.image" . }} + imagePullPolicy: "{{ .Values.image.pullPolicy }}" + {{- if $readReplicasResources }} + resources: {{- toYaml $readReplicasResources | nindent 12 }} + {{- end }} + {{- if .Values.containerSecurityContext.enabled }} + securityContext: {{- omit .Values.containerSecurityContext "enabled" | toYaml | nindent 12 }} + {{- end }} + env: + - name: BITNAMI_DEBUG + value: {{ ternary "true" "false" .Values.image.debug | quote }} + - name: POSTGRESQL_VOLUME_DIR + value: "{{ .Values.persistence.mountPath }}" + - name: POSTGRESQL_PORT_NUMBER + value: "{{ template "postgresql.port" . }}" + {{- if .Values.persistence.mountPath }} + - name: PGDATA + value: {{ .Values.postgresqlDataDir | quote }} + {{- end }} + - name: POSTGRES_REPLICATION_MODE + value: "slave" + - name: POSTGRES_REPLICATION_USER + value: {{ include "postgresql.replication.username" . | quote }} + {{- if .Values.usePasswordFile }} + - name: POSTGRES_REPLICATION_PASSWORD_FILE + value: "/opt/bitnami/postgresql/secrets/postgresql-replication-password" + {{- else }} + - name: POSTGRES_REPLICATION_PASSWORD + valueFrom: + secretKeyRef: + name: {{ template "postgresql.secretName" . }} + key: postgresql-replication-password + {{- end }} + - name: POSTGRES_CLUSTER_APP_NAME + value: {{ .Values.replication.applicationName }} + - name: POSTGRES_MASTER_HOST + value: {{ template "common.names.fullname" . }} + - name: POSTGRES_MASTER_PORT_NUMBER + value: {{ include "postgresql.port" . | quote }} + {{- if not (eq (include "postgresql.username" .) "postgres") }} + {{- if .Values.usePasswordFile }} + - name: POSTGRES_POSTGRES_PASSWORD_FILE + value: "/opt/bitnami/postgresql/secrets/postgresql-postgres-password" + {{- else }} + - name: POSTGRES_POSTGRES_PASSWORD + valueFrom: + secretKeyRef: + name: {{ template "postgresql.secretName" . }} + key: postgresql-postgres-password + {{- end }} + {{- end }} + {{- if .Values.usePasswordFile }} + - name: POSTGRES_PASSWORD_FILE + value: "/opt/bitnami/postgresql/secrets/postgresql-password" + {{- else }} + - name: POSTGRES_PASSWORD + valueFrom: + secretKeyRef: + name: {{ template "postgresql.secretName" . }} + key: postgresql-password + {{- end }} + - name: POSTGRESQL_ENABLE_TLS + value: {{ ternary "yes" "no" .Values.tls.enabled | quote }} + {{- if .Values.tls.enabled }} + - name: POSTGRESQL_TLS_PREFER_SERVER_CIPHERS + value: {{ ternary "yes" "no" .Values.tls.preferServerCiphers | quote }} + - name: POSTGRESQL_TLS_CERT_FILE + value: {{ template "postgresql.tlsCert" . }} + - name: POSTGRESQL_TLS_KEY_FILE + value: {{ template "postgresql.tlsCertKey" . }} + {{- if .Values.tls.certCAFilename }} + - name: POSTGRESQL_TLS_CA_FILE + value: {{ template "postgresql.tlsCACert" . }} + {{- end }} + {{- if .Values.tls.crlFilename }} + - name: POSTGRESQL_TLS_CRL_FILE + value: {{ template "postgresql.tlsCRL" . }} + {{- end }} + {{- end }} + - name: POSTGRESQL_LOG_HOSTNAME + value: {{ .Values.audit.logHostname | quote }} + - name: POSTGRESQL_LOG_CONNECTIONS + value: {{ .Values.audit.logConnections | quote }} + - name: POSTGRESQL_LOG_DISCONNECTIONS + value: {{ .Values.audit.logDisconnections | quote }} + {{- if .Values.audit.logLinePrefix }} + - name: POSTGRESQL_LOG_LINE_PREFIX + value: {{ .Values.audit.logLinePrefix | quote }} + {{- end }} + {{- if .Values.audit.logTimezone }} + - name: POSTGRESQL_LOG_TIMEZONE + value: {{ .Values.audit.logTimezone | quote }} + {{- end }} + {{- if .Values.audit.pgAuditLog }} + - name: POSTGRESQL_PGAUDIT_LOG + value: {{ .Values.audit.pgAuditLog | quote }} + {{- end }} + - name: POSTGRESQL_PGAUDIT_LOG_CATALOG + value: {{ .Values.audit.pgAuditLogCatalog | quote }} + - name: POSTGRESQL_CLIENT_MIN_MESSAGES + value: {{ .Values.audit.clientMinMessages | quote }} + - name: POSTGRESQL_SHARED_PRELOAD_LIBRARIES + value: {{ .Values.postgresqlSharedPreloadLibraries | quote }} + {{- if .Values.postgresqlMaxConnections }} + - name: POSTGRESQL_MAX_CONNECTIONS + value: {{ .Values.postgresqlMaxConnections | quote }} + {{- end }} + {{- if .Values.postgresqlPostgresConnectionLimit }} + - name: POSTGRESQL_POSTGRES_CONNECTION_LIMIT + value: {{ .Values.postgresqlPostgresConnectionLimit | quote }} + {{- end }} + {{- if .Values.postgresqlDbUserConnectionLimit }} + - name: POSTGRESQL_USERNAME_CONNECTION_LIMIT + value: {{ .Values.postgresqlDbUserConnectionLimit | quote }} + {{- end }} + {{- if .Values.postgresqlTcpKeepalivesInterval }} + - name: POSTGRESQL_TCP_KEEPALIVES_INTERVAL + value: {{ .Values.postgresqlTcpKeepalivesInterval | quote }} + {{- end }} + {{- if .Values.postgresqlTcpKeepalivesIdle }} + - name: POSTGRESQL_TCP_KEEPALIVES_IDLE + value: {{ .Values.postgresqlTcpKeepalivesIdle | quote }} + {{- end }} + {{- if .Values.postgresqlStatementTimeout }} + - name: POSTGRESQL_STATEMENT_TIMEOUT + value: {{ .Values.postgresqlStatementTimeout | quote }} + {{- end }} + {{- if .Values.postgresqlTcpKeepalivesCount }} + - name: POSTGRESQL_TCP_KEEPALIVES_COUNT + value: {{ .Values.postgresqlTcpKeepalivesCount | quote }} + {{- end }} + {{- if .Values.postgresqlPghbaRemoveFilters }} + - name: POSTGRESQL_PGHBA_REMOVE_FILTERS + value: {{ .Values.postgresqlPghbaRemoveFilters | quote }} + {{- end }} + ports: + - name: tcp-postgresql + containerPort: {{ template "postgresql.port" . }} + {{- if .Values.livenessProbe.enabled }} + livenessProbe: + exec: + command: + - /bin/sh + - -c + {{- if (include "postgresql.database" .) }} + - exec pg_isready -U {{ include "postgresql.username" . | quote }} -d "dbname={{ include "postgresql.database" . }} {{- if and .Values.tls.enabled .Values.tls.certCAFilename }} sslcert={{ include "postgresql.tlsCert" . }} sslkey={{ include "postgresql.tlsCertKey" . }}{{- end }}" -h 127.0.0.1 -p {{ template "postgresql.port" . }} + {{- else }} + - exec pg_isready -U {{ include "postgresql.username" . | quote }} {{- if and .Values.tls.enabled .Values.tls.certCAFilename }} -d "sslcert={{ include "postgresql.tlsCert" . }} sslkey={{ include "postgresql.tlsCertKey" . }}"{{- end }} -h 127.0.0.1 -p {{ template "postgresql.port" . }} + {{- end }} + initialDelaySeconds: {{ .Values.livenessProbe.initialDelaySeconds }} + periodSeconds: {{ .Values.livenessProbe.periodSeconds }} + timeoutSeconds: {{ .Values.livenessProbe.timeoutSeconds }} + successThreshold: {{ .Values.livenessProbe.successThreshold }} + failureThreshold: {{ .Values.livenessProbe.failureThreshold }} + {{- else if .Values.customLivenessProbe }} + livenessProbe: {{- include "common.tplvalues.render" (dict "value" .Values.customLivenessProbe "context" $) | nindent 12 }} + {{- end }} + {{- if .Values.readinessProbe.enabled }} + readinessProbe: + exec: + command: + - /bin/sh + - -c + - -e + {{- include "postgresql.readinessProbeCommand" . | nindent 16 }} + initialDelaySeconds: {{ .Values.readinessProbe.initialDelaySeconds }} + periodSeconds: {{ .Values.readinessProbe.periodSeconds }} + timeoutSeconds: {{ .Values.readinessProbe.timeoutSeconds }} + successThreshold: {{ .Values.readinessProbe.successThreshold }} + failureThreshold: {{ .Values.readinessProbe.failureThreshold }} + {{- else if .Values.customReadinessProbe }} + readinessProbe: {{- include "common.tplvalues.render" (dict "value" .Values.customReadinessProbe "context" $) | nindent 12 }} + {{- end }} + volumeMounts: + {{- if .Values.usePasswordFile }} + - name: postgresql-password + mountPath: /opt/bitnami/postgresql/secrets/ + {{- end }} + {{- if .Values.shmVolume.enabled }} + - name: dshm + mountPath: /dev/shm + {{- end }} + {{- if .Values.persistence.enabled }} + - name: data + mountPath: {{ .Values.persistence.mountPath }} + subPath: {{ .Values.persistence.subPath }} + {{ end }} + {{- if or (.Files.Glob "files/conf.d/*.conf") .Values.postgresqlExtendedConf .Values.extendedConfConfigMap }} + - name: postgresql-extended-config + mountPath: /bitnami/postgresql/conf/conf.d/ + {{- end }} + {{- if or (.Files.Glob "files/postgresql.conf") (.Files.Glob "files/pg_hba.conf") .Values.postgresqlConfiguration .Values.pgHbaConfiguration .Values.configurationConfigMap }} + - name: postgresql-config + mountPath: /bitnami/postgresql/conf + {{- end }} + {{- if .Values.tls.enabled }} + - name: postgresql-certificates + mountPath: /opt/bitnami/postgresql/certs + readOnly: true + {{- end }} + {{- if .Values.readReplicas.extraVolumeMounts }} + {{- toYaml .Values.readReplicas.extraVolumeMounts | nindent 12 }} + {{- end }} +{{- if .Values.readReplicas.sidecars }} +{{- include "common.tplvalues.render" ( dict "value" .Values.readReplicas.sidecars "context" $ ) | nindent 8 }} +{{- end }} + volumes: + {{- if .Values.usePasswordFile }} + - name: postgresql-password + secret: + secretName: {{ template "postgresql.secretName" . }} + {{- end }} + {{- if or (.Files.Glob "files/postgresql.conf") (.Files.Glob "files/pg_hba.conf") .Values.postgresqlConfiguration .Values.pgHbaConfiguration .Values.configurationConfigMap}} + - name: postgresql-config + configMap: + name: {{ template "postgresql.configurationCM" . }} + {{- end }} + {{- if or (.Files.Glob "files/conf.d/*.conf") .Values.postgresqlExtendedConf .Values.extendedConfConfigMap }} + - name: postgresql-extended-config + configMap: + name: {{ template "postgresql.extendedConfigurationCM" . }} + {{- end }} + {{- if .Values.tls.enabled }} + - name: raw-certificates + secret: + secretName: {{ required "A secret containing TLS certificates is required when TLS is enabled" .Values.tls.certificatesSecret }} + - name: postgresql-certificates + emptyDir: {} + {{- end }} + {{- if .Values.shmVolume.enabled }} + - name: dshm + emptyDir: + medium: Memory + sizeLimit: 1Gi + {{- end }} + {{- if or (not .Values.persistence.enabled) (not .Values.readReplicas.persistence.enabled) }} + - name: data + emptyDir: {} + {{- end }} + {{- if .Values.readReplicas.extraVolumes }} + {{- toYaml .Values.readReplicas.extraVolumes | nindent 8 }} + {{- end }} + updateStrategy: + type: {{ .Values.updateStrategy.type }} + {{- if (eq "Recreate" .Values.updateStrategy.type) }} + rollingUpdate: null + {{- end }} +{{- if and .Values.persistence.enabled .Values.readReplicas.persistence.enabled }} + volumeClaimTemplates: + - metadata: + name: data + {{- with .Values.persistence.annotations }} + annotations: + {{- range $key, $value := . }} + {{ $key }}: {{ $value }} + {{- end }} + {{- end }} + spec: + accessModes: + {{- range .Values.persistence.accessModes }} + - {{ . | quote }} + {{- end }} + resources: + requests: + storage: {{ .Values.persistence.size | quote }} + {{ include "common.storage.class" (dict "persistence" .Values.persistence "global" .Values.global) }} + + {{- if .Values.persistence.selector }} + selector: {{- include "common.tplvalues.render" (dict "value" .Values.persistence.selector "context" $) | nindent 10 }} + {{- end -}} +{{- end }} +{{- end }} diff --git a/charts/jfrog/artifactory-ha/107.104.10/charts/postgresql/templates/statefulset.yaml b/charts/jfrog/artifactory-ha/107.104.10/charts/postgresql/templates/statefulset.yaml new file mode 100644 index 0000000000..f8163fd99f --- /dev/null +++ b/charts/jfrog/artifactory-ha/107.104.10/charts/postgresql/templates/statefulset.yaml @@ -0,0 +1,609 @@ +apiVersion: {{ include "common.capabilities.statefulset.apiVersion" . }} +kind: StatefulSet +metadata: + name: {{ template "postgresql.primary.fullname" . }} + labels: {{- include "common.labels.standard" . | nindent 4 }} + app.kubernetes.io/component: primary + {{- with .Values.primary.labels }} + {{- toYaml . | nindent 4 }} + {{- end }} + annotations: + {{- if .Values.commonAnnotations }} + {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} + {{- end }} + {{- with .Values.primary.annotations }} + {{- toYaml . | nindent 4 }} + {{- end }} + namespace: {{ .Release.Namespace }} +spec: + serviceName: {{ template "common.names.fullname" . }}-headless + replicas: 1 + updateStrategy: + type: {{ .Values.updateStrategy.type }} + {{- if (eq "Recreate" .Values.updateStrategy.type) }} + rollingUpdate: null + {{- end }} + selector: + matchLabels: + {{- include "common.labels.matchLabels" . | nindent 6 }} + role: primary + template: + metadata: + name: {{ template "common.names.fullname" . }} + labels: + {{- include "common.labels.standard" . | nindent 8 }} + role: primary + app.kubernetes.io/component: primary + {{- with .Values.primary.podLabels }} + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.primary.podAnnotations }} + annotations: {{- toYaml . | nindent 8 }} + {{- end }} + spec: + {{- if .Values.schedulerName }} + schedulerName: "{{ .Values.schedulerName }}" + {{- end }} +{{- include "postgresql.imagePullSecrets" . | indent 6 }} + {{- if .Values.primary.affinity }} + affinity: {{- include "common.tplvalues.render" (dict "value" .Values.primary.affinity "context" $) | nindent 8 }} + {{- else }} + affinity: + podAffinity: {{- include "common.affinities.pods" (dict "type" .Values.primary.podAffinityPreset "component" "primary" "context" $) | nindent 10 }} + podAntiAffinity: {{- include "common.affinities.pods" (dict "type" .Values.primary.podAntiAffinityPreset "component" "primary" "context" $) | nindent 10 }} + nodeAffinity: {{- include "common.affinities.nodes" (dict "type" .Values.primary.nodeAffinityPreset.type "key" .Values.primary.nodeAffinityPreset.key "values" .Values.primary.nodeAffinityPreset.values) | nindent 10 }} + {{- end }} + {{- if .Values.primary.nodeSelector }} + nodeSelector: {{- include "common.tplvalues.render" (dict "value" .Values.primary.nodeSelector "context" $) | nindent 8 }} + {{- end }} + {{- if .Values.primary.tolerations }} + tolerations: {{- include "common.tplvalues.render" (dict "value" .Values.primary.tolerations "context" $) | nindent 8 }} + {{- end }} + {{- if .Values.terminationGracePeriodSeconds }} + terminationGracePeriodSeconds: {{ .Values.terminationGracePeriodSeconds }} + {{- end }} + {{- if .Values.securityContext.enabled }} + securityContext: {{- omit .Values.securityContext "enabled" | toYaml | nindent 8 }} + {{- end }} + {{- if .Values.serviceAccount.enabled }} + serviceAccountName: {{ default (include "common.names.fullname" . ) .Values.serviceAccount.name }} + {{- end }} + {{- if or .Values.primary.extraInitContainers (and .Values.volumePermissions.enabled (or .Values.persistence.enabled (and .Values.shmVolume.enabled .Values.shmVolume.chmod.enabled))) }} + initContainers: + {{- if and .Values.volumePermissions.enabled (or .Values.persistence.enabled (and .Values.shmVolume.enabled .Values.shmVolume.chmod.enabled) .Values.tls.enabled) }} + - name: init-chmod-data + image: {{ template "postgresql.volumePermissions.image" . }} + imagePullPolicy: {{ .Values.volumePermissions.image.pullPolicy | quote }} + {{- if .Values.resources }} + resources: {{- toYaml .Values.resources | nindent 12 }} + {{- end }} + command: + - /bin/sh + - -cx + - | + {{- if .Values.persistence.enabled }} + {{- if eq ( toString ( .Values.volumePermissions.securityContext.runAsUser )) "auto" }} + chown `id -u`:`id -G | cut -d " " -f2` {{ .Values.persistence.mountPath }} + {{- else }} + chown {{ .Values.containerSecurityContext.runAsUser }}:{{ .Values.securityContext.fsGroup }} {{ .Values.persistence.mountPath }} + {{- end }} + mkdir -p {{ .Values.persistence.mountPath }}/data {{- if (include "postgresql.mountConfigurationCM" .) }} {{ .Values.persistence.mountPath }}/conf {{- end }} + chmod 700 {{ .Values.persistence.mountPath }}/data {{- if (include "postgresql.mountConfigurationCM" .) }} {{ .Values.persistence.mountPath }}/conf {{- end }} + find {{ .Values.persistence.mountPath }} -mindepth 1 -maxdepth 1 {{- if not (include "postgresql.mountConfigurationCM" .) }} -not -name "conf" {{- end }} -not -name ".snapshot" -not -name "lost+found" | \ + {{- if eq ( toString ( .Values.volumePermissions.securityContext.runAsUser )) "auto" }} + xargs chown -R `id -u`:`id -G | cut -d " " -f2` + {{- else }} + xargs chown -R {{ .Values.containerSecurityContext.runAsUser }}:{{ .Values.securityContext.fsGroup }} + {{- end }} + {{- end }} + {{- if and .Values.shmVolume.enabled .Values.shmVolume.chmod.enabled }} + chmod -R 777 /dev/shm + {{- end }} + {{- if .Values.tls.enabled }} + cp /tmp/certs/* /opt/bitnami/postgresql/certs/ + {{- if eq ( toString ( .Values.volumePermissions.securityContext.runAsUser )) "auto" }} + chown -R `id -u`:`id -G | cut -d " " -f2` /opt/bitnami/postgresql/certs/ + {{- else }} + chown -R {{ .Values.containerSecurityContext.runAsUser }}:{{ .Values.securityContext.fsGroup }} /opt/bitnami/postgresql/certs/ + {{- end }} + chmod 600 {{ template "postgresql.tlsCertKey" . }} + {{- end }} + {{- if eq ( toString ( .Values.volumePermissions.securityContext.runAsUser )) "auto" }} + securityContext: {{- omit .Values.volumePermissions.securityContext "runAsUser" | toYaml | nindent 12 }} + {{- else }} + securityContext: {{- .Values.volumePermissions.securityContext | toYaml | nindent 12 }} + {{- end }} + volumeMounts: + {{- if .Values.persistence.enabled }} + - name: data + mountPath: {{ .Values.persistence.mountPath }} + subPath: {{ .Values.persistence.subPath }} + {{- end }} + {{- if .Values.shmVolume.enabled }} + - name: dshm + mountPath: /dev/shm + {{- end }} + {{- if .Values.tls.enabled }} + - name: raw-certificates + mountPath: /tmp/certs + - name: postgresql-certificates + mountPath: /opt/bitnami/postgresql/certs + {{- end }} + {{- end }} + {{- if .Values.primary.extraInitContainers }} + {{- include "common.tplvalues.render" ( dict "value" .Values.primary.extraInitContainers "context" $ ) | nindent 8 }} + {{- end }} + {{- end }} + {{- if .Values.primary.priorityClassName }} + priorityClassName: {{ .Values.primary.priorityClassName }} + {{- end }} + containers: + - name: {{ template "common.names.fullname" . }} + image: {{ template "postgresql.image" . }} + imagePullPolicy: "{{ .Values.image.pullPolicy }}" + {{- if .Values.resources }} + resources: {{- toYaml .Values.resources | nindent 12 }} + {{- end }} + {{- if .Values.containerSecurityContext.enabled }} + securityContext: {{- omit .Values.containerSecurityContext "enabled" | toYaml | nindent 12 }} + {{- end }} + env: + - name: BITNAMI_DEBUG + value: {{ ternary "true" "false" .Values.image.debug | quote }} + - name: POSTGRESQL_PORT_NUMBER + value: "{{ template "postgresql.port" . }}" + - name: POSTGRESQL_VOLUME_DIR + value: "{{ .Values.persistence.mountPath }}" + {{- if .Values.postgresqlInitdbArgs }} + - name: POSTGRES_INITDB_ARGS + value: {{ .Values.postgresqlInitdbArgs | quote }} + {{- end }} + {{- if .Values.postgresqlInitdbWalDir }} + - name: POSTGRES_INITDB_WALDIR + value: {{ .Values.postgresqlInitdbWalDir | quote }} + {{- end }} + {{- if .Values.initdbUser }} + - name: POSTGRESQL_INITSCRIPTS_USERNAME + value: {{ .Values.initdbUser }} + {{- end }} + {{- if .Values.initdbPassword }} + - name: POSTGRESQL_INITSCRIPTS_PASSWORD + value: {{ .Values.initdbPassword }} + {{- end }} + {{- if .Values.persistence.mountPath }} + - name: PGDATA + value: {{ .Values.postgresqlDataDir | quote }} + {{- end }} + {{- if .Values.primaryAsStandBy.enabled }} + - name: POSTGRES_MASTER_HOST + value: {{ .Values.primaryAsStandBy.primaryHost }} + - name: POSTGRES_MASTER_PORT_NUMBER + value: {{ .Values.primaryAsStandBy.primaryPort | quote }} + {{- end }} + {{- if or .Values.replication.enabled .Values.primaryAsStandBy.enabled }} + - name: POSTGRES_REPLICATION_MODE + {{- if .Values.primaryAsStandBy.enabled }} + value: "slave" + {{- else }} + value: "master" + {{- end }} + - name: POSTGRES_REPLICATION_USER + value: {{ include "postgresql.replication.username" . | quote }} + {{- if .Values.usePasswordFile }} + - name: POSTGRES_REPLICATION_PASSWORD_FILE + value: "/opt/bitnami/postgresql/secrets/postgresql-replication-password" + {{- else }} + - name: POSTGRES_REPLICATION_PASSWORD + valueFrom: + secretKeyRef: + name: {{ template "postgresql.secretName" . }} + key: postgresql-replication-password + {{- end }} + {{- if not (eq .Values.replication.synchronousCommit "off")}} + - name: POSTGRES_SYNCHRONOUS_COMMIT_MODE + value: {{ .Values.replication.synchronousCommit | quote }} + - name: POSTGRES_NUM_SYNCHRONOUS_REPLICAS + value: {{ .Values.replication.numSynchronousReplicas | quote }} + {{- end }} + - name: POSTGRES_CLUSTER_APP_NAME + value: {{ .Values.replication.applicationName }} + {{- end }} + {{- if not (eq (include "postgresql.username" .) "postgres") }} + {{- if .Values.usePasswordFile }} + - name: POSTGRES_POSTGRES_PASSWORD_FILE + value: "/opt/bitnami/postgresql/secrets/postgresql-postgres-password" + {{- else }} + - name: POSTGRES_POSTGRES_PASSWORD + valueFrom: + secretKeyRef: + name: {{ template "postgresql.secretName" . }} + key: postgresql-postgres-password + {{- end }} + {{- end }} + - name: POSTGRES_USER + value: {{ include "postgresql.username" . | quote }} + {{- if .Values.usePasswordFile }} + - name: POSTGRES_PASSWORD_FILE + value: "/opt/bitnami/postgresql/secrets/postgresql-password" + {{- else }} + - name: POSTGRES_PASSWORD + valueFrom: + secretKeyRef: + name: {{ template "postgresql.secretName" . }} + key: postgresql-password + {{- end }} + {{- if (include "postgresql.database" .) }} + - name: POSTGRES_DB + value: {{ (include "postgresql.database" .) | quote }} + {{- end }} + {{- if .Values.extraEnv }} + {{- include "common.tplvalues.render" (dict "value" .Values.extraEnv "context" $) | nindent 12 }} + {{- end }} + - name: POSTGRESQL_ENABLE_LDAP + value: {{ ternary "yes" "no" .Values.ldap.enabled | quote }} + {{- if .Values.ldap.enabled }} + - name: POSTGRESQL_LDAP_SERVER + value: {{ .Values.ldap.server }} + - name: POSTGRESQL_LDAP_PORT + value: {{ .Values.ldap.port | quote }} + - name: POSTGRESQL_LDAP_SCHEME + value: {{ .Values.ldap.scheme }} + {{- if .Values.ldap.tls }} + - name: POSTGRESQL_LDAP_TLS + value: "1" + {{- end }} + - name: POSTGRESQL_LDAP_PREFIX + value: {{ .Values.ldap.prefix | quote }} + - name: POSTGRESQL_LDAP_SUFFIX + value: {{ .Values.ldap.suffix | quote }} + - name: POSTGRESQL_LDAP_BASE_DN + value: {{ .Values.ldap.baseDN }} + - name: POSTGRESQL_LDAP_BIND_DN + value: {{ .Values.ldap.bindDN }} + {{- if (not (empty .Values.ldap.bind_password)) }} + - name: POSTGRESQL_LDAP_BIND_PASSWORD + valueFrom: + secretKeyRef: + name: {{ template "postgresql.secretName" . }} + key: postgresql-ldap-password + {{- end}} + - name: POSTGRESQL_LDAP_SEARCH_ATTR + value: {{ .Values.ldap.search_attr }} + - name: POSTGRESQL_LDAP_SEARCH_FILTER + value: {{ .Values.ldap.search_filter }} + - name: POSTGRESQL_LDAP_URL + value: {{ .Values.ldap.url }} + {{- end}} + - name: POSTGRESQL_ENABLE_TLS + value: {{ ternary "yes" "no" .Values.tls.enabled | quote }} + {{- if .Values.tls.enabled }} + - name: POSTGRESQL_TLS_PREFER_SERVER_CIPHERS + value: {{ ternary "yes" "no" .Values.tls.preferServerCiphers | quote }} + - name: POSTGRESQL_TLS_CERT_FILE + value: {{ template "postgresql.tlsCert" . }} + - name: POSTGRESQL_TLS_KEY_FILE + value: {{ template "postgresql.tlsCertKey" . }} + {{- if .Values.tls.certCAFilename }} + - name: POSTGRESQL_TLS_CA_FILE + value: {{ template "postgresql.tlsCACert" . }} + {{- end }} + {{- if .Values.tls.crlFilename }} + - name: POSTGRESQL_TLS_CRL_FILE + value: {{ template "postgresql.tlsCRL" . }} + {{- end }} + {{- end }} + - name: POSTGRESQL_LOG_HOSTNAME + value: {{ .Values.audit.logHostname | quote }} + - name: POSTGRESQL_LOG_CONNECTIONS + value: {{ .Values.audit.logConnections | quote }} + - name: POSTGRESQL_LOG_DISCONNECTIONS + value: {{ .Values.audit.logDisconnections | quote }} + {{- if .Values.audit.logLinePrefix }} + - name: POSTGRESQL_LOG_LINE_PREFIX + value: {{ .Values.audit.logLinePrefix | quote }} + {{- end }} + {{- if .Values.audit.logTimezone }} + - name: POSTGRESQL_LOG_TIMEZONE + value: {{ .Values.audit.logTimezone | quote }} + {{- end }} + {{- if .Values.audit.pgAuditLog }} + - name: POSTGRESQL_PGAUDIT_LOG + value: {{ .Values.audit.pgAuditLog | quote }} + {{- end }} + - name: POSTGRESQL_PGAUDIT_LOG_CATALOG + value: {{ .Values.audit.pgAuditLogCatalog | quote }} + - name: POSTGRESQL_CLIENT_MIN_MESSAGES + value: {{ .Values.audit.clientMinMessages | quote }} + - name: POSTGRESQL_SHARED_PRELOAD_LIBRARIES + value: {{ .Values.postgresqlSharedPreloadLibraries | quote }} + {{- if .Values.postgresqlMaxConnections }} + - name: POSTGRESQL_MAX_CONNECTIONS + value: {{ .Values.postgresqlMaxConnections | quote }} + {{- end }} + {{- if .Values.postgresqlPostgresConnectionLimit }} + - name: POSTGRESQL_POSTGRES_CONNECTION_LIMIT + value: {{ .Values.postgresqlPostgresConnectionLimit | quote }} + {{- end }} + {{- if .Values.postgresqlDbUserConnectionLimit }} + - name: POSTGRESQL_USERNAME_CONNECTION_LIMIT + value: {{ .Values.postgresqlDbUserConnectionLimit | quote }} + {{- end }} + {{- if .Values.postgresqlTcpKeepalivesInterval }} + - name: POSTGRESQL_TCP_KEEPALIVES_INTERVAL + value: {{ .Values.postgresqlTcpKeepalivesInterval | quote }} + {{- end }} + {{- if .Values.postgresqlTcpKeepalivesIdle }} + - name: POSTGRESQL_TCP_KEEPALIVES_IDLE + value: {{ .Values.postgresqlTcpKeepalivesIdle | quote }} + {{- end }} + {{- if .Values.postgresqlStatementTimeout }} + - name: POSTGRESQL_STATEMENT_TIMEOUT + value: {{ .Values.postgresqlStatementTimeout | quote }} + {{- end }} + {{- if .Values.postgresqlTcpKeepalivesCount }} + - name: POSTGRESQL_TCP_KEEPALIVES_COUNT + value: {{ .Values.postgresqlTcpKeepalivesCount | quote }} + {{- end }} + {{- if .Values.postgresqlPghbaRemoveFilters }} + - name: POSTGRESQL_PGHBA_REMOVE_FILTERS + value: {{ .Values.postgresqlPghbaRemoveFilters | quote }} + {{- end }} + {{- if .Values.extraEnvVarsCM }} + envFrom: + - configMapRef: + name: {{ tpl .Values.extraEnvVarsCM . }} + {{- end }} + ports: + - name: tcp-postgresql + containerPort: {{ template "postgresql.port" . }} + {{- if .Values.startupProbe.enabled }} + startupProbe: + exec: + command: + - /bin/sh + - -c + {{- if (include "postgresql.database" .) }} + - exec pg_isready -U {{ include "postgresql.username" . | quote }} -d "dbname={{ include "postgresql.database" . }} {{- if and .Values.tls.enabled .Values.tls.certCAFilename }} sslcert={{ include "postgresql.tlsCert" . }} sslkey={{ include "postgresql.tlsCertKey" . }}{{- end }}" -h 127.0.0.1 -p {{ template "postgresql.port" . }} + {{- else }} + - exec pg_isready -U {{ include "postgresql.username" . | quote }} {{- if and .Values.tls.enabled .Values.tls.certCAFilename }} -d "sslcert={{ include "postgresql.tlsCert" . }} sslkey={{ include "postgresql.tlsCertKey" . }}"{{- end }} -h 127.0.0.1 -p {{ template "postgresql.port" . }} + {{- end }} + initialDelaySeconds: {{ .Values.startupProbe.initialDelaySeconds }} + periodSeconds: {{ .Values.startupProbe.periodSeconds }} + timeoutSeconds: {{ .Values.startupProbe.timeoutSeconds }} + successThreshold: {{ .Values.startupProbe.successThreshold }} + failureThreshold: {{ .Values.startupProbe.failureThreshold }} + {{- else if .Values.customStartupProbe }} + startupProbe: {{- include "common.tplvalues.render" (dict "value" .Values.customStartupProbe "context" $) | nindent 12 }} + {{- end }} + {{- if .Values.livenessProbe.enabled }} + livenessProbe: + exec: + command: + - /bin/sh + - -c + {{- if (include "postgresql.database" .) }} + - exec pg_isready -U {{ include "postgresql.username" . | quote }} -d "dbname={{ include "postgresql.database" . }} {{- if and .Values.tls.enabled .Values.tls.certCAFilename }} sslcert={{ include "postgresql.tlsCert" . }} sslkey={{ include "postgresql.tlsCertKey" . }}{{- end }}" -h 127.0.0.1 -p {{ template "postgresql.port" . }} + {{- else }} + - exec pg_isready -U {{ include "postgresql.username" . | quote }} {{- if and .Values.tls.enabled .Values.tls.certCAFilename }} -d "sslcert={{ include "postgresql.tlsCert" . }} sslkey={{ include "postgresql.tlsCertKey" . }}"{{- end }} -h 127.0.0.1 -p {{ template "postgresql.port" . }} + {{- end }} + initialDelaySeconds: {{ .Values.livenessProbe.initialDelaySeconds }} + periodSeconds: {{ .Values.livenessProbe.periodSeconds }} + timeoutSeconds: {{ .Values.livenessProbe.timeoutSeconds }} + successThreshold: {{ .Values.livenessProbe.successThreshold }} + failureThreshold: {{ .Values.livenessProbe.failureThreshold }} + {{- else if .Values.customLivenessProbe }} + livenessProbe: {{- include "common.tplvalues.render" (dict "value" .Values.customLivenessProbe "context" $) | nindent 12 }} + {{- end }} + {{- if .Values.readinessProbe.enabled }} + readinessProbe: + exec: + command: + - /bin/sh + - -c + - -e + {{- include "postgresql.readinessProbeCommand" . | nindent 16 }} + initialDelaySeconds: {{ .Values.readinessProbe.initialDelaySeconds }} + periodSeconds: {{ .Values.readinessProbe.periodSeconds }} + timeoutSeconds: {{ .Values.readinessProbe.timeoutSeconds }} + successThreshold: {{ .Values.readinessProbe.successThreshold }} + failureThreshold: {{ .Values.readinessProbe.failureThreshold }} + {{- else if .Values.customReadinessProbe }} + readinessProbe: {{- include "common.tplvalues.render" (dict "value" .Values.customReadinessProbe "context" $) | nindent 12 }} + {{- end }} + volumeMounts: + {{- if or (.Files.Glob "files/docker-entrypoint-initdb.d/*.{sh,sql,sql.gz}") .Values.initdbScriptsConfigMap .Values.initdbScripts }} + - name: custom-init-scripts + mountPath: /docker-entrypoint-initdb.d/ + {{- end }} + {{- if .Values.initdbScriptsSecret }} + - name: custom-init-scripts-secret + mountPath: /docker-entrypoint-initdb.d/secret + {{- end }} + {{- if or (.Files.Glob "files/conf.d/*.conf") .Values.postgresqlExtendedConf .Values.extendedConfConfigMap }} + - name: postgresql-extended-config + mountPath: /bitnami/postgresql/conf/conf.d/ + {{- end }} + {{- if .Values.usePasswordFile }} + - name: postgresql-password + mountPath: /opt/bitnami/postgresql/secrets/ + {{- end }} + {{- if .Values.tls.enabled }} + - name: postgresql-certificates + mountPath: /opt/bitnami/postgresql/certs + readOnly: true + {{- end }} + {{- if .Values.shmVolume.enabled }} + - name: dshm + mountPath: /dev/shm + {{- end }} + {{- if .Values.persistence.enabled }} + - name: data + mountPath: {{ .Values.persistence.mountPath }} + subPath: {{ .Values.persistence.subPath }} + {{- end }} + {{- if or (.Files.Glob "files/postgresql.conf") (.Files.Glob "files/pg_hba.conf") .Values.postgresqlConfiguration .Values.pgHbaConfiguration .Values.configurationConfigMap }} + - name: postgresql-config + mountPath: /bitnami/postgresql/conf + {{- end }} + {{- if .Values.primary.extraVolumeMounts }} + {{- toYaml .Values.primary.extraVolumeMounts | nindent 12 }} + {{- end }} +{{- if .Values.primary.sidecars }} +{{- include "common.tplvalues.render" ( dict "value" .Values.primary.sidecars "context" $ ) | nindent 8 }} +{{- end }} +{{- if .Values.metrics.enabled }} + - name: metrics + image: {{ template "postgresql.metrics.image" . }} + imagePullPolicy: {{ .Values.metrics.image.pullPolicy | quote }} + {{- if .Values.metrics.securityContext.enabled }} + securityContext: {{- omit .Values.metrics.securityContext "enabled" | toYaml | nindent 12 }} + {{- end }} + env: + {{- $database := required "In order to enable metrics you need to specify a database (.Values.postgresqlDatabase or .Values.global.postgresql.postgresqlDatabase)" (include "postgresql.database" .) }} + {{- $sslmode := ternary "require" "disable" .Values.tls.enabled }} + {{- if and .Values.tls.enabled .Values.tls.certCAFilename }} + - name: DATA_SOURCE_NAME + value: {{ printf "host=127.0.0.1 port=%d user=%s sslmode=%s sslcert=%s sslkey=%s" (int (include "postgresql.port" .)) (include "postgresql.username" .) $sslmode (include "postgresql.tlsCert" .) (include "postgresql.tlsCertKey" .) }} + {{- else }} + - name: DATA_SOURCE_URI + value: {{ printf "127.0.0.1:%d/%s?sslmode=%s" (int (include "postgresql.port" .)) $database $sslmode }} + {{- end }} + {{- if .Values.usePasswordFile }} + - name: DATA_SOURCE_PASS_FILE + value: "/opt/bitnami/postgresql/secrets/postgresql-password" + {{- else }} + - name: DATA_SOURCE_PASS + valueFrom: + secretKeyRef: + name: {{ template "postgresql.secretName" . }} + key: postgresql-password + {{- end }} + - name: DATA_SOURCE_USER + value: {{ template "postgresql.username" . }} + {{- if .Values.metrics.extraEnvVars }} + {{- include "common.tplvalues.render" (dict "value" .Values.metrics.extraEnvVars "context" $) | nindent 12 }} + {{- end }} + {{- if .Values.livenessProbe.enabled }} + livenessProbe: + httpGet: + path: / + port: http-metrics + initialDelaySeconds: {{ .Values.metrics.livenessProbe.initialDelaySeconds }} + periodSeconds: {{ .Values.metrics.livenessProbe.periodSeconds }} + timeoutSeconds: {{ .Values.metrics.livenessProbe.timeoutSeconds }} + successThreshold: {{ .Values.metrics.livenessProbe.successThreshold }} + failureThreshold: {{ .Values.metrics.livenessProbe.failureThreshold }} + {{- end }} + {{- if .Values.readinessProbe.enabled }} + readinessProbe: + httpGet: + path: / + port: http-metrics + initialDelaySeconds: {{ .Values.metrics.readinessProbe.initialDelaySeconds }} + periodSeconds: {{ .Values.metrics.readinessProbe.periodSeconds }} + timeoutSeconds: {{ .Values.metrics.readinessProbe.timeoutSeconds }} + successThreshold: {{ .Values.metrics.readinessProbe.successThreshold }} + failureThreshold: {{ .Values.metrics.readinessProbe.failureThreshold }} + {{- end }} + volumeMounts: + {{- if .Values.usePasswordFile }} + - name: postgresql-password + mountPath: /opt/bitnami/postgresql/secrets/ + {{- end }} + {{- if .Values.tls.enabled }} + - name: postgresql-certificates + mountPath: /opt/bitnami/postgresql/certs + readOnly: true + {{- end }} + {{- if .Values.metrics.customMetrics }} + - name: custom-metrics + mountPath: /conf + readOnly: true + args: ["--extend.query-path", "/conf/custom-metrics.yaml"] + {{- end }} + ports: + - name: http-metrics + containerPort: 9187 + {{- if .Values.metrics.resources }} + resources: {{- toYaml .Values.metrics.resources | nindent 12 }} + {{- end }} +{{- end }} + volumes: + {{- if or (.Files.Glob "files/postgresql.conf") (.Files.Glob "files/pg_hba.conf") .Values.postgresqlConfiguration .Values.pgHbaConfiguration .Values.configurationConfigMap}} + - name: postgresql-config + configMap: + name: {{ template "postgresql.configurationCM" . }} + {{- end }} + {{- if or (.Files.Glob "files/conf.d/*.conf") .Values.postgresqlExtendedConf .Values.extendedConfConfigMap }} + - name: postgresql-extended-config + configMap: + name: {{ template "postgresql.extendedConfigurationCM" . }} + {{- end }} + {{- if .Values.usePasswordFile }} + - name: postgresql-password + secret: + secretName: {{ template "postgresql.secretName" . }} + {{- end }} + {{- if or (.Files.Glob "files/docker-entrypoint-initdb.d/*.{sh,sql,sql.gz}") .Values.initdbScriptsConfigMap .Values.initdbScripts }} + - name: custom-init-scripts + configMap: + name: {{ template "postgresql.initdbScriptsCM" . }} + {{- end }} + {{- if .Values.initdbScriptsSecret }} + - name: custom-init-scripts-secret + secret: + secretName: {{ template "postgresql.initdbScriptsSecret" . }} + {{- end }} + {{- if .Values.tls.enabled }} + - name: raw-certificates + secret: + secretName: {{ required "A secret containing TLS certificates is required when TLS is enabled" .Values.tls.certificatesSecret }} + - name: postgresql-certificates + emptyDir: {} + {{- end }} + {{- if .Values.primary.extraVolumes }} + {{- toYaml .Values.primary.extraVolumes | nindent 8 }} + {{- end }} + {{- if and .Values.metrics.enabled .Values.metrics.customMetrics }} + - name: custom-metrics + configMap: + name: {{ template "postgresql.metricsCM" . }} + {{- end }} + {{- if .Values.shmVolume.enabled }} + - name: dshm + emptyDir: + medium: Memory + sizeLimit: 1Gi + {{- end }} +{{- if and .Values.persistence.enabled .Values.persistence.existingClaim }} + - name: data + persistentVolumeClaim: +{{- with .Values.persistence.existingClaim }} + claimName: {{ tpl . $ }} +{{- end }} +{{- else if not .Values.persistence.enabled }} + - name: data + emptyDir: {} +{{- else if and .Values.persistence.enabled (not .Values.persistence.existingClaim) }} + volumeClaimTemplates: + - metadata: + name: data + {{- with .Values.persistence.annotations }} + annotations: + {{- range $key, $value := . }} + {{ $key }}: {{ $value }} + {{- end }} + {{- end }} + spec: + accessModes: + {{- range .Values.persistence.accessModes }} + - {{ . | quote }} + {{- end }} + resources: + requests: + storage: {{ .Values.persistence.size | quote }} + {{ include "common.storage.class" (dict "persistence" .Values.persistence "global" .Values.global) }} + {{- if .Values.persistence.selector }} + selector: {{- include "common.tplvalues.render" (dict "value" .Values.persistence.selector "context" $) | nindent 10 }} + {{- end -}} +{{- end }} diff --git a/charts/jfrog/artifactory-ha/107.104.10/charts/postgresql/templates/svc-headless.yaml b/charts/jfrog/artifactory-ha/107.104.10/charts/postgresql/templates/svc-headless.yaml new file mode 100644 index 0000000000..6f5f3b9ee4 --- /dev/null +++ b/charts/jfrog/artifactory-ha/107.104.10/charts/postgresql/templates/svc-headless.yaml @@ -0,0 +1,28 @@ +apiVersion: v1 +kind: Service +metadata: + name: {{ template "common.names.fullname" . }}-headless + labels: + {{- include "common.labels.standard" . | nindent 4 }} + {{- if .Values.commonAnnotations }} + annotations: {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} + {{- end }} + # Use this annotation in addition to the actual publishNotReadyAddresses + # field below because the annotation will stop being respected soon but the + # field is broken in some versions of Kubernetes: + # https://github.com/kubernetes/kubernetes/issues/58662 + service.alpha.kubernetes.io/tolerate-unready-endpoints: "true" + namespace: {{ .Release.Namespace }} +spec: + type: ClusterIP + clusterIP: None + # We want all pods in the StatefulSet to have their addresses published for + # the sake of the other Postgresql pods even before they're ready, since they + # have to be able to talk to each other in order to become ready. + publishNotReadyAddresses: true + ports: + - name: tcp-postgresql + port: {{ template "postgresql.port" . }} + targetPort: tcp-postgresql + selector: + {{- include "common.labels.matchLabels" . | nindent 4 }} diff --git a/charts/jfrog/artifactory-ha/107.104.10/charts/postgresql/templates/svc-read.yaml b/charts/jfrog/artifactory-ha/107.104.10/charts/postgresql/templates/svc-read.yaml new file mode 100644 index 0000000000..56195ea1e6 --- /dev/null +++ b/charts/jfrog/artifactory-ha/107.104.10/charts/postgresql/templates/svc-read.yaml @@ -0,0 +1,43 @@ +{{- if .Values.replication.enabled }} +{{- $serviceAnnotations := coalesce .Values.readReplicas.service.annotations .Values.service.annotations -}} +{{- $serviceType := coalesce .Values.readReplicas.service.type .Values.service.type -}} +{{- $serviceLoadBalancerIP := coalesce .Values.readReplicas.service.loadBalancerIP .Values.service.loadBalancerIP -}} +{{- $serviceLoadBalancerSourceRanges := coalesce .Values.readReplicas.service.loadBalancerSourceRanges .Values.service.loadBalancerSourceRanges -}} +{{- $serviceClusterIP := coalesce .Values.readReplicas.service.clusterIP .Values.service.clusterIP -}} +{{- $serviceNodePort := coalesce .Values.readReplicas.service.nodePort .Values.service.nodePort -}} +apiVersion: v1 +kind: Service +metadata: + name: {{ template "common.names.fullname" . }}-read + labels: + {{- include "common.labels.standard" . | nindent 4 }} + annotations: + {{- if .Values.commonAnnotations }} + {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} + {{- end }} + {{- if $serviceAnnotations }} + {{- include "common.tplvalues.render" (dict "value" $serviceAnnotations "context" $) | nindent 4 }} + {{- end }} + namespace: {{ .Release.Namespace }} +spec: + type: {{ $serviceType }} + {{- if and $serviceLoadBalancerIP (eq $serviceType "LoadBalancer") }} + loadBalancerIP: {{ $serviceLoadBalancerIP }} + {{- end }} + {{- if and (eq $serviceType "LoadBalancer") $serviceLoadBalancerSourceRanges }} + loadBalancerSourceRanges: {{- include "common.tplvalues.render" (dict "value" $serviceLoadBalancerSourceRanges "context" $) | nindent 4 }} + {{- end }} + {{- if and (eq $serviceType "ClusterIP") $serviceClusterIP }} + clusterIP: {{ $serviceClusterIP }} + {{- end }} + ports: + - name: tcp-postgresql + port: {{ template "postgresql.port" . }} + targetPort: tcp-postgresql + {{- if $serviceNodePort }} + nodePort: {{ $serviceNodePort }} + {{- end }} + selector: + {{- include "common.labels.matchLabels" . | nindent 4 }} + role: read +{{- end }} diff --git a/charts/jfrog/artifactory-ha/107.104.10/charts/postgresql/templates/svc.yaml b/charts/jfrog/artifactory-ha/107.104.10/charts/postgresql/templates/svc.yaml new file mode 100644 index 0000000000..a29431b6a4 --- /dev/null +++ b/charts/jfrog/artifactory-ha/107.104.10/charts/postgresql/templates/svc.yaml @@ -0,0 +1,41 @@ +{{- $serviceAnnotations := coalesce .Values.primary.service.annotations .Values.service.annotations -}} +{{- $serviceType := coalesce .Values.primary.service.type .Values.service.type -}} +{{- $serviceLoadBalancerIP := coalesce .Values.primary.service.loadBalancerIP .Values.service.loadBalancerIP -}} +{{- $serviceLoadBalancerSourceRanges := coalesce .Values.primary.service.loadBalancerSourceRanges .Values.service.loadBalancerSourceRanges -}} +{{- $serviceClusterIP := coalesce .Values.primary.service.clusterIP .Values.service.clusterIP -}} +{{- $serviceNodePort := coalesce .Values.primary.service.nodePort .Values.service.nodePort -}} +apiVersion: v1 +kind: Service +metadata: + name: {{ template "common.names.fullname" . }} + labels: + {{- include "common.labels.standard" . | nindent 4 }} + annotations: + {{- if .Values.commonAnnotations }} + {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} + {{- end }} + {{- if $serviceAnnotations }} + {{- include "common.tplvalues.render" (dict "value" $serviceAnnotations "context" $) | nindent 4 }} + {{- end }} + namespace: {{ .Release.Namespace }} +spec: + type: {{ $serviceType }} + {{- if and $serviceLoadBalancerIP (eq $serviceType "LoadBalancer") }} + loadBalancerIP: {{ $serviceLoadBalancerIP }} + {{- end }} + {{- if and (eq $serviceType "LoadBalancer") $serviceLoadBalancerSourceRanges }} + loadBalancerSourceRanges: {{- include "common.tplvalues.render" (dict "value" $serviceLoadBalancerSourceRanges "context" $) | nindent 4 }} + {{- end }} + {{- if and (eq $serviceType "ClusterIP") $serviceClusterIP }} + clusterIP: {{ $serviceClusterIP }} + {{- end }} + ports: + - name: tcp-postgresql + port: {{ template "postgresql.port" . }} + targetPort: tcp-postgresql + {{- if $serviceNodePort }} + nodePort: {{ $serviceNodePort }} + {{- end }} + selector: + {{- include "common.labels.matchLabels" . | nindent 4 }} + role: primary diff --git a/charts/jfrog/artifactory-ha/107.104.10/charts/postgresql/values.schema.json b/charts/jfrog/artifactory-ha/107.104.10/charts/postgresql/values.schema.json new file mode 100644 index 0000000000..66a2a9dd06 --- /dev/null +++ b/charts/jfrog/artifactory-ha/107.104.10/charts/postgresql/values.schema.json @@ -0,0 +1,103 @@ +{ + "$schema": "http://json-schema.org/schema#", + "type": "object", + "properties": { + "postgresqlUsername": { + "type": "string", + "title": "Admin user", + "form": true + }, + "postgresqlPassword": { + "type": "string", + "title": "Password", + "form": true + }, + "persistence": { + "type": "object", + "properties": { + "size": { + "type": "string", + "title": "Persistent Volume Size", + "form": true, + "render": "slider", + "sliderMin": 1, + "sliderMax": 100, + "sliderUnit": "Gi" + } + } + }, + "resources": { + "type": "object", + "title": "Required Resources", + "description": "Configure resource requests", + "form": true, + "properties": { + "requests": { + "type": "object", + "properties": { + "memory": { + "type": "string", + "form": true, + "render": "slider", + "title": "Memory Request", + "sliderMin": 10, + "sliderMax": 2048, + "sliderUnit": "Mi" + }, + "cpu": { + "type": "string", + "form": true, + "render": "slider", + "title": "CPU Request", + "sliderMin": 10, + "sliderMax": 2000, + "sliderUnit": "m" + } + } + } + } + }, + "replication": { + "type": "object", + "form": true, + "title": "Replication Details", + "properties": { + "enabled": { + "type": "boolean", + "title": "Enable Replication", + "form": true + }, + "readReplicas": { + "type": "integer", + "title": "read Replicas", + "form": true, + "hidden": { + "value": false, + "path": "replication/enabled" + } + } + } + }, + "volumePermissions": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean", + "form": true, + "title": "Enable Init Containers", + "description": "Change the owner of the persist volume mountpoint to RunAsUser:fsGroup" + } + } + }, + "metrics": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean", + "title": "Configure metrics exporter", + "form": true + } + } + } + } +} diff --git a/charts/jfrog/artifactory-ha/107.104.10/charts/postgresql/values.yaml b/charts/jfrog/artifactory-ha/107.104.10/charts/postgresql/values.yaml new file mode 100644 index 0000000000..82ce092344 --- /dev/null +++ b/charts/jfrog/artifactory-ha/107.104.10/charts/postgresql/values.yaml @@ -0,0 +1,824 @@ +## Global Docker image parameters +## Please, note that this will override the image parameters, including dependencies, configured to use the global value +## Current available global Docker image parameters: imageRegistry and imagePullSecrets +## +global: + postgresql: {} +# imageRegistry: myRegistryName +# imagePullSecrets: +# - myRegistryKeySecretName +# storageClass: myStorageClass + +## Bitnami PostgreSQL image version +## ref: https://hub.docker.com/r/bitnami/postgresql/tags/ +## +image: + registry: docker.io + repository: bitnami/postgresql + tag: 11.11.0-debian-10-r71 + ## Specify a imagePullPolicy + ## Defaults to 'Always' if image tag is 'latest', else set to 'IfNotPresent' + ## ref: http://kubernetes.io/docs/user-guide/images/#pre-pulling-images + ## + pullPolicy: IfNotPresent + ## Optionally specify an array of imagePullSecrets. + ## Secrets must be manually created in the namespace. + ## ref: https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry/ + ## + # pullSecrets: + # - myRegistryKeySecretName + + ## Set to true if you would like to see extra information on logs + ## It turns BASH and/or NAMI debugging in the image + ## + debug: false + +## String to partially override common.names.fullname template (will maintain the release name) +## +# nameOverride: + +## String to fully override common.names.fullname template +## +# fullnameOverride: + +## +## Init containers parameters: +## volumePermissions: Change the owner of the persist volume mountpoint to RunAsUser:fsGroup +## +volumePermissions: + enabled: false + image: + registry: docker.io + repository: bitnami/bitnami-shell + tag: "10" + ## Specify a imagePullPolicy + ## Defaults to 'Always' if image tag is 'latest', else set to 'IfNotPresent' + ## ref: http://kubernetes.io/docs/user-guide/images/#pre-pulling-images + ## + pullPolicy: Always + ## Optionally specify an array of imagePullSecrets. + ## Secrets must be manually created in the namespace. + ## ref: https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry/ + ## + # pullSecrets: + # - myRegistryKeySecretName + ## Init container Security Context + ## Note: the chown of the data folder is done to securityContext.runAsUser + ## and not the below volumePermissions.securityContext.runAsUser + ## When runAsUser is set to special value "auto", init container will try to chwon the + ## data folder to autodetermined user&group, using commands: `id -u`:`id -G | cut -d" " -f2` + ## "auto" is especially useful for OpenShift which has scc with dynamic userids (and 0 is not allowed). + ## You may want to use this volumePermissions.securityContext.runAsUser="auto" in combination with + ## pod securityContext.enabled=false and shmVolume.chmod.enabled=false + ## + securityContext: + runAsUser: 0 + +## Use an alternate scheduler, e.g. "stork". +## ref: https://kubernetes.io/docs/tasks/administer-cluster/configure-multiple-schedulers/ +## +# schedulerName: + +## Pod Security Context +## ref: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/ +## +securityContext: + enabled: true + fsGroup: 1001 + +## Container Security Context +## ref: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/ +## +containerSecurityContext: + enabled: true + runAsUser: 1001 + +## Pod Service Account +## ref: https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/ +## +serviceAccount: + enabled: false + ## Name of an already existing service account. Setting this value disables the automatic service account creation. + # name: + +## Pod Security Policy +## ref: https://kubernetes.io/docs/concepts/policy/pod-security-policy/ +## +psp: + create: false + +## Creates role for ServiceAccount +## Required for PSP +## +rbac: + create: false + +replication: + enabled: false + user: repl_user + password: repl_password + readReplicas: 1 + ## Set synchronous commit mode: on, off, remote_apply, remote_write and local + ## ref: https://www.postgresql.org/docs/9.6/runtime-config-wal.html#GUC-WAL-LEVEL + synchronousCommit: 'off' + ## From the number of `readReplicas` defined above, set the number of those that will have synchronous replication + ## NOTE: It cannot be > readReplicas + numSynchronousReplicas: 0 + ## Replication Cluster application name. Useful for defining multiple replication policies + ## + applicationName: my_application + +## PostgreSQL admin password (used when `postgresqlUsername` is not `postgres`) +## ref: https://github.com/bitnami/bitnami-docker-postgresql/blob/master/README.md#creating-a-database-user-on-first-run (see note!) +# postgresqlPostgresPassword: + +## PostgreSQL user (has superuser privileges if username is `postgres`) +## ref: https://github.com/bitnami/bitnami-docker-postgresql/blob/master/README.md#setting-the-root-password-on-first-run +## +postgresqlUsername: postgres + +## PostgreSQL password +## ref: https://github.com/bitnami/bitnami-docker-postgresql/blob/master/README.md#setting-the-root-password-on-first-run +## +# postgresqlPassword: + +## PostgreSQL password using existing secret +## existingSecret: secret +## + +## Mount PostgreSQL secret as a file instead of passing environment variable +# usePasswordFile: false + +## Create a database +## ref: https://github.com/bitnami/bitnami-docker-postgresql/blob/master/README.md#creating-a-database-on-first-run +## +# postgresqlDatabase: + +## PostgreSQL data dir +## ref: https://github.com/bitnami/bitnami-docker-postgresql/blob/master/README.md +## +postgresqlDataDir: /bitnami/postgresql/data + +## An array to add extra environment variables +## For example: +## extraEnv: +## - name: FOO +## value: "bar" +## +# extraEnv: +extraEnv: [] + +## Name of a ConfigMap containing extra env vars +## +# extraEnvVarsCM: + +## Specify extra initdb args +## ref: https://github.com/bitnami/bitnami-docker-postgresql/blob/master/README.md +## +# postgresqlInitdbArgs: + +## Specify a custom location for the PostgreSQL transaction log +## ref: https://github.com/bitnami/bitnami-docker-postgresql/blob/master/README.md +## +# postgresqlInitdbWalDir: + +## PostgreSQL configuration +## Specify runtime configuration parameters as a dict, using camelCase, e.g. +## {"sharedBuffers": "500MB"} +## Alternatively, you can put your postgresql.conf under the files/ directory +## ref: https://www.postgresql.org/docs/current/static/runtime-config.html +## +# postgresqlConfiguration: + +## PostgreSQL extended configuration +## As above, but _appended_ to the main configuration +## Alternatively, you can put your *.conf under the files/conf.d/ directory +## https://github.com/bitnami/bitnami-docker-postgresql#allow-settings-to-be-loaded-from-files-other-than-the-default-postgresqlconf +## +# postgresqlExtendedConf: + +## Configure current cluster's primary server to be the standby server in other cluster. +## This will allow cross cluster replication and provide cross cluster high availability. +## You will need to configure pgHbaConfiguration if you want to enable this feature with local cluster replication enabled. +## +primaryAsStandBy: + enabled: false + # primaryHost: + # primaryPort: + +## PostgreSQL client authentication configuration +## Specify content for pg_hba.conf +## Default: do not create pg_hba.conf +## Alternatively, you can put your pg_hba.conf under the files/ directory +# pgHbaConfiguration: |- +# local all all trust +# host all all localhost trust +# host mydatabase mysuser 192.168.0.0/24 md5 + +## ConfigMap with PostgreSQL configuration +## NOTE: This will override postgresqlConfiguration and pgHbaConfiguration +# configurationConfigMap: + +## ConfigMap with PostgreSQL extended configuration +# extendedConfConfigMap: + +## initdb scripts +## Specify dictionary of scripts to be run at first boot +## Alternatively, you can put your scripts under the files/docker-entrypoint-initdb.d directory +## +# initdbScripts: +# my_init_script.sh: | +# #!/bin/sh +# echo "Do something." + +## ConfigMap with scripts to be run at first boot +## NOTE: This will override initdbScripts +# initdbScriptsConfigMap: + +## Secret with scripts to be run at first boot (in case it contains sensitive information) +## NOTE: This can work along initdbScripts or initdbScriptsConfigMap +# initdbScriptsSecret: + +## Specify the PostgreSQL username and password to execute the initdb scripts +# initdbUser: +# initdbPassword: + +## Audit settings +## https://github.com/bitnami/bitnami-docker-postgresql#auditing +## +audit: + ## Log client hostnames + ## + logHostname: false + ## Log connections to the server + ## + logConnections: false + ## Log disconnections + ## + logDisconnections: false + ## Operation to audit using pgAudit (default if not set) + ## + pgAuditLog: "" + ## Log catalog using pgAudit + ## + pgAuditLogCatalog: "off" + ## Log level for clients + ## + clientMinMessages: error + ## Template for log line prefix (default if not set) + ## + logLinePrefix: "" + ## Log timezone + ## + logTimezone: "" + +## Shared preload libraries +## +postgresqlSharedPreloadLibraries: "pgaudit" + +## Maximum total connections +## +postgresqlMaxConnections: + +## Maximum connections for the postgres user +## +postgresqlPostgresConnectionLimit: + +## Maximum connections for the created user +## +postgresqlDbUserConnectionLimit: + +## TCP keepalives interval +## +postgresqlTcpKeepalivesInterval: + +## TCP keepalives idle +## +postgresqlTcpKeepalivesIdle: + +## TCP keepalives count +## +postgresqlTcpKeepalivesCount: + +## Statement timeout +## +postgresqlStatementTimeout: + +## Remove pg_hba.conf lines with the following comma-separated patterns +## (cannot be used with custom pg_hba.conf) +## +postgresqlPghbaRemoveFilters: + +## Optional duration in seconds the pod needs to terminate gracefully. +## ref: https://kubernetes.io/docs/concepts/workloads/pods/pod/#termination-of-pods +## +# terminationGracePeriodSeconds: 30 + +## LDAP configuration +## +ldap: + enabled: false + url: '' + server: '' + port: '' + prefix: '' + suffix: '' + baseDN: '' + bindDN: '' + bind_password: + search_attr: '' + search_filter: '' + scheme: '' + tls: {} + +## PostgreSQL service configuration +## +service: + ## PosgresSQL service type + ## + type: ClusterIP + # clusterIP: None + port: 5432 + + ## Specify the nodePort value for the LoadBalancer and NodePort service types. + ## ref: https://kubernetes.io/docs/concepts/services-networking/service/#type-nodeport + ## + # nodePort: + + ## Provide any additional annotations which may be required. Evaluated as a template. + ## + annotations: {} + ## Set the LoadBalancer service type to internal only. + ## ref: https://kubernetes.io/docs/concepts/services-networking/service/#internal-load-balancer + ## + # loadBalancerIP: + ## Load Balancer sources. Evaluated as a template. + ## https://kubernetes.io/docs/tasks/access-application-cluster/configure-cloud-provider-firewall/#restrict-access-for-loadbalancer-service + ## + # loadBalancerSourceRanges: + # - 10.10.10.0/24 + +## Start primary and read(s) pod(s) without limitations on shm memory. +## By default docker and containerd (and possibly other container runtimes) +## limit `/dev/shm` to `64M` (see e.g. the +## [docker issue](https://github.com/docker-library/postgres/issues/416) and the +## [containerd issue](https://github.com/containerd/containerd/issues/3654), +## which could be not enough if PostgreSQL uses parallel workers heavily. +## +shmVolume: + ## Set `shmVolume.enabled` to `true` to mount a new tmpfs volume to remove + ## this limitation. + ## + enabled: true + ## Set to `true` to `chmod 777 /dev/shm` on a initContainer. + ## This option is ignored if `volumePermissions.enabled` is `false` + ## + chmod: + enabled: true + +## PostgreSQL data Persistent Volume Storage Class +## If defined, storageClassName: +## If set to "-", storageClassName: "", which disables dynamic provisioning +## If undefined (the default) or set to null, no storageClassName spec is +## set, choosing the default provisioner. (gp2 on AWS, standard on +## GKE, AWS & OpenStack) +## +persistence: + enabled: true + ## A manually managed Persistent Volume and Claim + ## If defined, PVC must be created manually before volume will be bound + ## The value is evaluated as a template, so, for example, the name can depend on .Release or .Chart + ## + # existingClaim: + + ## The path the volume will be mounted at, useful when using different + ## PostgreSQL images. + ## + mountPath: /bitnami/postgresql + + ## The subdirectory of the volume to mount to, useful in dev environments + ## and one PV for multiple services. + ## + subPath: '' + + # storageClass: "-" + accessModes: + - ReadWriteOnce + size: 8Gi + annotations: {} + ## selector can be used to match an existing PersistentVolume + ## selector: + ## matchLabels: + ## app: my-app + selector: {} + +## updateStrategy for PostgreSQL StatefulSet and its reads StatefulSets +## ref: https://kubernetes.io/docs/concepts/workloads/controllers/statefulset/#update-strategies +## +updateStrategy: + type: RollingUpdate + +## +## PostgreSQL Primary parameters +## +primary: + ## PostgreSQL Primary pod affinity preset + ## ref: https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#inter-pod-affinity-and-anti-affinity + ## Allowed values: soft, hard + ## + podAffinityPreset: "" + + ## PostgreSQL Primary pod anti-affinity preset + ## ref: https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#inter-pod-affinity-and-anti-affinity + ## Allowed values: soft, hard + ## + podAntiAffinityPreset: soft + + ## PostgreSQL Primary node affinity preset + ## ref: https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#node-affinity + ## Allowed values: soft, hard + ## + nodeAffinityPreset: + ## Node affinity type + ## Allowed values: soft, hard + type: "" + ## Node label key to match + ## E.g. + ## key: "kubernetes.io/e2e-az-name" + ## + key: "" + ## Node label values to match + ## E.g. + ## values: + ## - e2e-az1 + ## - e2e-az2 + ## + values: [] + + ## Affinity for PostgreSQL primary pods assignment + ## ref: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#affinity-and-anti-affinity + ## Note: primary.podAffinityPreset, primary.podAntiAffinityPreset, and primary.nodeAffinityPreset will be ignored when it's set + ## + affinity: {} + + ## Node labels for PostgreSQL primary pods assignment + ## ref: https://kubernetes.io/docs/user-guide/node-selection/ + ## + nodeSelector: {} + + ## Tolerations for PostgreSQL primary pods assignment + ## ref: https://kubernetes.io/docs/concepts/configuration/taint-and-toleration/ + ## + tolerations: [] + + labels: {} + annotations: {} + podLabels: {} + podAnnotations: {} + priorityClassName: '' + ## Extra init containers + ## Example + ## + ## extraInitContainers: + ## - name: do-something + ## image: busybox + ## command: ['do', 'something'] + ## + extraInitContainers: [] + + ## Additional PostgreSQL primary Volume mounts + ## + extraVolumeMounts: [] + ## Additional PostgreSQL primary Volumes + ## + extraVolumes: [] + ## Add sidecars to the pod + ## + ## For example: + ## sidecars: + ## - name: your-image-name + ## image: your-image + ## imagePullPolicy: Always + ## ports: + ## - name: portname + ## containerPort: 1234 + ## + sidecars: [] + + ## Override the service configuration for primary + ## + service: {} + # type: + # nodePort: + # clusterIP: + +## +## PostgreSQL read only replica parameters +## +readReplicas: + ## PostgreSQL read only pod affinity preset + ## ref: https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#inter-pod-affinity-and-anti-affinity + ## Allowed values: soft, hard + ## + podAffinityPreset: "" + + ## PostgreSQL read only pod anti-affinity preset + ## ref: https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#inter-pod-affinity-and-anti-affinity + ## Allowed values: soft, hard + ## + podAntiAffinityPreset: soft + + ## PostgreSQL read only node affinity preset + ## ref: https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#node-affinity + ## Allowed values: soft, hard + ## + nodeAffinityPreset: + ## Node affinity type + ## Allowed values: soft, hard + type: "" + ## Node label key to match + ## E.g. + ## key: "kubernetes.io/e2e-az-name" + ## + key: "" + ## Node label values to match + ## E.g. + ## values: + ## - e2e-az1 + ## - e2e-az2 + ## + values: [] + + ## Affinity for PostgreSQL read only pods assignment + ## ref: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#affinity-and-anti-affinity + ## Note: readReplicas.podAffinityPreset, readReplicas.podAntiAffinityPreset, and readReplicas.nodeAffinityPreset will be ignored when it's set + ## + affinity: {} + + ## Node labels for PostgreSQL read only pods assignment + ## ref: https://kubernetes.io/docs/user-guide/node-selection/ + ## + nodeSelector: {} + + ## Tolerations for PostgreSQL read only pods assignment + ## ref: https://kubernetes.io/docs/concepts/configuration/taint-and-toleration/ + ## + tolerations: [] + labels: {} + annotations: {} + podLabels: {} + podAnnotations: {} + priorityClassName: '' + + ## Extra init containers + ## Example + ## + ## extraInitContainers: + ## - name: do-something + ## image: busybox + ## command: ['do', 'something'] + ## + extraInitContainers: [] + + ## Additional PostgreSQL read replicas Volume mounts + ## + extraVolumeMounts: [] + + ## Additional PostgreSQL read replicas Volumes + ## + extraVolumes: [] + + ## Add sidecars to the pod + ## + ## For example: + ## sidecars: + ## - name: your-image-name + ## image: your-image + ## imagePullPolicy: Always + ## ports: + ## - name: portname + ## containerPort: 1234 + ## + sidecars: [] + + ## Override the service configuration for read + ## + service: {} + # type: + # nodePort: + # clusterIP: + + ## Whether to enable PostgreSQL read replicas data Persistent + ## + persistence: + enabled: true + + # Override the resource configuration for read replicas + resources: {} + # requests: + # memory: 256Mi + # cpu: 250m + +## Configure resource requests and limits +## ref: http://kubernetes.io/docs/user-guide/compute-resources/ +## +resources: + requests: + memory: 256Mi + cpu: 250m + +## Add annotations to all the deployed resources +## +commonAnnotations: {} + +networkPolicy: + ## Enable creation of NetworkPolicy resources. Only Ingress traffic is filtered for now. + ## + enabled: false + + ## The Policy model to apply. When set to false, only pods with the correct + ## client label will have network access to the port PostgreSQL is listening + ## on. When true, PostgreSQL will accept connections from any source + ## (with the correct destination port). + ## + allowExternal: true + + ## if explicitNamespacesSelector is missing or set to {}, only client Pods that are in the networkPolicy's namespace + ## and that match other criteria, the ones that have the good label, can reach the DB. + ## But sometimes, we want the DB to be accessible to clients from other namespaces, in this case, we can use this + ## LabelSelector to select these namespaces, note that the networkPolicy's namespace should also be explicitly added. + ## + ## Example: + ## explicitNamespacesSelector: + ## matchLabels: + ## role: frontend + ## matchExpressions: + ## - {key: role, operator: In, values: [frontend]} + ## + explicitNamespacesSelector: {} + +## Configure extra options for startup, liveness and readiness probes +## ref: https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/#configure-probes +## +startupProbe: + enabled: false + initialDelaySeconds: 30 + periodSeconds: 15 + timeoutSeconds: 5 + failureThreshold: 10 + successThreshold: 1 + +livenessProbe: + enabled: true + initialDelaySeconds: 30 + periodSeconds: 10 + timeoutSeconds: 5 + failureThreshold: 6 + successThreshold: 1 + +readinessProbe: + enabled: true + initialDelaySeconds: 5 + periodSeconds: 10 + timeoutSeconds: 5 + failureThreshold: 6 + successThreshold: 1 + +## Custom Startup probe +## +customStartupProbe: {} + +## Custom Liveness probe +## +customLivenessProbe: {} + +## Custom Rediness probe +## +customReadinessProbe: {} + +## +## TLS configuration +## +tls: + # Enable TLS traffic + enabled: false + # + # Whether to use the server's TLS cipher preferences rather than the client's. + preferServerCiphers: true + # + # Name of the Secret that contains the certificates + certificatesSecret: '' + # + # Certificate filename + certFilename: '' + # + # Certificate Key filename + certKeyFilename: '' + # + # CA Certificate filename + # If provided, PostgreSQL will authenticate TLS/SSL clients by requesting them a certificate + # ref: https://www.postgresql.org/docs/9.6/auth-methods.html + certCAFilename: + # + # File containing a Certificate Revocation List + crlFilename: + +## Configure metrics exporter +## +metrics: + enabled: false + # resources: {} + service: + type: ClusterIP + annotations: + prometheus.io/scrape: 'true' + prometheus.io/port: '9187' + loadBalancerIP: + serviceMonitor: + enabled: false + additionalLabels: {} + # namespace: monitoring + # interval: 30s + # scrapeTimeout: 10s + ## Custom PrometheusRule to be defined + ## The value is evaluated as a template, so, for example, the value can depend on .Release or .Chart + ## ref: https://github.com/coreos/prometheus-operator#customresourcedefinitions + ## + prometheusRule: + enabled: false + additionalLabels: {} + namespace: '' + ## These are just examples rules, please adapt them to your needs. + ## Make sure to constraint the rules to the current postgresql service. + ## rules: + ## - alert: HugeReplicationLag + ## expr: pg_replication_lag{service="{{ template "common.names.fullname" . }}-metrics"} / 3600 > 1 + ## for: 1m + ## labels: + ## severity: critical + ## annotations: + ## description: replication for {{ template "common.names.fullname" . }} PostgreSQL is lagging by {{ "{{ $value }}" }} hour(s). + ## summary: PostgreSQL replication is lagging by {{ "{{ $value }}" }} hour(s). + ## + rules: [] + + image: + registry: docker.io + repository: bitnami/postgres-exporter + tag: 0.9.0-debian-10-r43 + pullPolicy: IfNotPresent + ## Optionally specify an array of imagePullSecrets. + ## Secrets must be manually created in the namespace. + ## ref: https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry/ + ## + # pullSecrets: + # - myRegistryKeySecretName + ## Define additional custom metrics + ## ref: https://github.com/wrouesnel/postgres_exporter#adding-new-metrics-via-a-config-file + # customMetrics: + # pg_database: + # query: "SELECT d.datname AS name, CASE WHEN pg_catalog.has_database_privilege(d.datname, 'CONNECT') THEN pg_catalog.pg_database_size(d.datname) ELSE 0 END AS size_bytes FROM pg_catalog.pg_database d where datname not in ('template0', 'template1', 'postgres')" + # metrics: + # - name: + # usage: "LABEL" + # description: "Name of the database" + # - size_bytes: + # usage: "GAUGE" + # description: "Size of the database in bytes" + # + ## An array to add extra env vars to configure postgres-exporter + ## see: https://github.com/wrouesnel/postgres_exporter#environment-variables + ## For example: + # extraEnvVars: + # - name: PG_EXPORTER_DISABLE_DEFAULT_METRICS + # value: "true" + extraEnvVars: {} + + ## Pod Security Context + ## ref: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/ + ## + securityContext: + enabled: false + runAsUser: 1001 + ## ref: https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-probes/#configure-probes) + ## Configure extra options for liveness and readiness probes + ## + livenessProbe: + enabled: true + initialDelaySeconds: 5 + periodSeconds: 10 + timeoutSeconds: 5 + failureThreshold: 6 + successThreshold: 1 + + readinessProbe: + enabled: true + initialDelaySeconds: 5 + periodSeconds: 10 + timeoutSeconds: 5 + failureThreshold: 6 + successThreshold: 1 + +## Array with extra yaml to deploy with the chart. Evaluated as a template +## +extraDeploy: [] diff --git a/charts/jfrog/artifactory-ha/107.104.10/ci/access-tls-values.yaml b/charts/jfrog/artifactory-ha/107.104.10/ci/access-tls-values.yaml new file mode 100644 index 0000000000..27e24d3468 --- /dev/null +++ b/charts/jfrog/artifactory-ha/107.104.10/ci/access-tls-values.yaml @@ -0,0 +1,34 @@ +databaseUpgradeReady: true +artifactory: + masterKey: FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + persistence: + enabled: false + primary: + resources: + requests: + memory: "4Gi" + cpu: "2" + limits: + memory: "6Gi" + cpu: "4" + node: + resources: + requests: + memory: "4Gi" + cpu: "2" + limits: + memory: "6Gi" + cpu: "4" + javaOpts: + xms: "4g" + xmx: "4g" +# To Fix ct tool --reuse-values - PASSWORDS ERROR: you must provide your current passwords when upgrade the release +postgresql: + postgresqlPassword: password + persistence: + enabled: false +access: + accessConfig: + security: + tls: true + resetAccessCAKeys: true diff --git a/charts/jfrog/artifactory-ha/107.104.10/ci/default-values.yaml b/charts/jfrog/artifactory-ha/107.104.10/ci/default-values.yaml new file mode 100644 index 0000000000..020f523352 --- /dev/null +++ b/charts/jfrog/artifactory-ha/107.104.10/ci/default-values.yaml @@ -0,0 +1,32 @@ +# Leave this file empty to ensure that CI runs builds against the default configuration in values.yaml. +databaseUpgradeReady: true +## This is an exception here because HA needs masterKey to connect with other node members and it is commented in values to support 6.x to 7.x Migration +## Please refer https://github.com/jfrog/charts/blob/master/stable/artifactory-ha/README.md#special-upgrade-notes-1 +artifactory: + masterKey: FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + persistence: + enabled: false + primary: + resources: + requests: + memory: "4Gi" + cpu: "2" + limits: + memory: "6Gi" + cpu: "4" + javaOpts: + xms: "4g" + xmx: "4g" + node: + resources: + requests: + memory: "4Gi" + cpu: "2" + limits: + memory: "6Gi" + cpu: "4" +# To Fix ct tool --reuse-values - PASSWORDS ERROR: you must provide your current passwords when upgrade the release +postgresql: + postgresqlPassword: password + persistence: + enabled: false diff --git a/charts/jfrog/artifactory-ha/107.104.10/ci/global-values.yaml b/charts/jfrog/artifactory-ha/107.104.10/ci/global-values.yaml new file mode 100644 index 0000000000..0987e17ca7 --- /dev/null +++ b/charts/jfrog/artifactory-ha/107.104.10/ci/global-values.yaml @@ -0,0 +1,255 @@ +databaseUpgradeReady: true +artifactory: + persistence: + enabled: false + primary: + resources: + requests: + memory: "4Gi" + cpu: "2" + limits: + memory: "6Gi" + cpu: "4" + javaOpts: + xms: "4g" + xmx: "4g" + node: + resources: + requests: + memory: "4Gi" + cpu: "2" + limits: + memory: "6Gi" + cpu: "4" + customInitContainersBegin: | + - name: "custom-init-begin-local" + image: {{ include "artifactory-ha.getImageInfoByValue" (list . "initContainers") }} + imagePullPolicy: {{ .Values.initContainers.image.pullPolicy }} + command: + - 'sh' + - '-c' + - echo "running in local" + volumeMounts: + - mountPath: "{{ .Values.artifactory.persistence.mountPath }}" + name: volume + customInitContainers: | + - name: "custom-init-local" + image: {{ include "artifactory-ha.getImageInfoByValue" (list . "initContainers") }} + imagePullPolicy: {{ .Values.initContainers.image.pullPolicy }} + command: + - 'sh' + - '-c' + - echo "running in local" + volumeMounts: + - mountPath: "{{ .Values.artifactory.persistence.mountPath }}" + name: volume + # Add custom volumes + customVolumes: | + - name: custom-script-local + emptyDir: + sizeLimit: 100Mi + # Add custom volumesMounts + customVolumeMounts: | + - name: custom-script-local + mountPath: "/scriptslocal" + # Add custom sidecar containers + customSidecarContainers: | + - name: "sidecar-list-local" + image: {{ include "artifactory-ha.getImageInfoByValue" (list . "initContainers") }} + imagePullPolicy: {{ .Values.initContainers.image.pullPolicy }} + securityContext: + allowPrivilegeEscalation: false + capabilities: + drop: + - NET_RAW + command: ["sh","-c","echo 'Sidecar is running in local' >> /scriptslocal/sidecarlocal.txt; cat /scriptslocal/sidecarlocal.txt; while true; do sleep 30; done"] + volumeMounts: + - mountPath: "/scriptslocal" + name: custom-script-local + resources: + requests: + memory: "32Mi" + cpu: "50m" + limits: + memory: "128Mi" + cpu: "100m" + +# To Fix ct tool --reuse-values - PASSWORDS ERROR: you must provide your current passwords when upgrade the release +postgresql: + postgresqlPassword: password + persistence: + enabled: false +global: + masterKey: FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + joinKey: EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE + customInitContainersBegin: | + - name: "custom-init-begin-global" + image: {{ include "artifactory-ha.getImageInfoByValue" (list . "initContainers") }} + imagePullPolicy: {{ .Values.initContainers.image.pullPolicy }} + command: + - 'sh' + - '-c' + - echo "running in global" + volumeMounts: + - mountPath: "{{ .Values.artifactory.persistence.mountPath }}" + name: volume + customInitContainers: | + - name: "custom-init-global" + image: {{ include "artifactory-ha.getImageInfoByValue" (list . "initContainers") }} + imagePullPolicy: {{ .Values.initContainers.image.pullPolicy }} + command: + - 'sh' + - '-c' + - echo "running in global" + volumeMounts: + - mountPath: "{{ .Values.artifactory.persistence.mountPath }}" + name: volume + # Add custom volumes + customVolumes: | + - name: custom-script-global + emptyDir: + sizeLimit: 100Mi + # Add custom volumesMounts + customVolumeMounts: | + - name: custom-script-global + mountPath: "/scripts" + # Add custom sidecar containers + customSidecarContainers: | + - name: "sidecar-list-global" + image: {{ include "artifactory-ha.getImageInfoByValue" (list . "initContainers") }} + imagePullPolicy: {{ .Values.initContainers.image.pullPolicy }} + securityContext: + allowPrivilegeEscalation: false + capabilities: + drop: + - NET_RAW + command: ["sh","-c","echo 'Sidecar is running in global' >> /scripts/sidecarglobal.txt; cat /scripts/sidecarglobal.txt; while true; do sleep 30; done"] + volumeMounts: + - mountPath: "/scripts" + name: custom-script-global + resources: + requests: + memory: "32Mi" + cpu: "50m" + limits: + memory: "128Mi" + cpu: "100m" + +nginx: + customInitContainers: | + - name: "custom-init-begin-nginx" + image: {{ include "artifactory-ha.getImageInfoByValue" (list . "initContainers") }} + imagePullPolicy: {{ .Values.initContainers.image.pullPolicy }} + command: + - 'sh' + - '-c' + - echo "running in nginx" + volumeMounts: + - mountPath: "{{ .Values.artifactory.persistence.mountPath }}" + name: custom-script-local + customSidecarContainers: | + - name: "sidecar-list-nginx" + image: {{ include "artifactory-ha.getImageInfoByValue" (list . "initContainers") }} + imagePullPolicy: {{ .Values.initContainers.image.pullPolicy }} + securityContext: + allowPrivilegeEscalation: false + capabilities: + drop: + - NET_RAW + command: ["sh","-c","echo 'Sidecar is running in local' >> /scriptslocal/sidecarlocal.txt; cat /scriptslocal/sidecarlocal.txt; while true; do sleep 30; done"] + volumeMounts: + - mountPath: "/scriptslocal" + name: custom-script-local + resources: + requests: + memory: "32Mi" + cpu: "50m" + limits: + memory: "128Mi" + cpu: "100m" + # Add custom volumes + customVolumes: | + - name: custom-script-local + emptyDir: + sizeLimit: 100Mi + + artifactoryConf: | + {{- if .Values.nginx.https.enabled }} + ssl_protocols TLSv1 TLSv1.1 TLSv1.2 TLSv1.3; + ssl_certificate {{ .Values.nginx.persistence.mountPath }}/ssl/tls.crt; + ssl_certificate_key {{ .Values.nginx.persistence.mountPath }}/ssl/tls.key; + ssl_session_cache shared:SSL:1m; + ssl_prefer_server_ciphers on; + {{- end }} + ## server configuration + server { + listen 8088; + {{- if .Values.nginx.internalPortHttps }} + listen {{ .Values.nginx.internalPortHttps }} ssl; + {{- else -}} + {{- if .Values.nginx.https.enabled }} + listen {{ .Values.nginx.https.internalPort }} ssl; + {{- end }} + {{- end }} + {{- if .Values.nginx.internalPortHttp }} + listen {{ .Values.nginx.internalPortHttp }}; + {{- else -}} + {{- if .Values.nginx.http.enabled }} + listen {{ .Values.nginx.http.internalPort }}; + {{- end }} + {{- end }} + server_name ~(?.+)\.{{ include "artifactory-ha.fullname" . }} {{ include "artifactory-ha.fullname" . }} + {{- range .Values.ingress.hosts -}} + {{- if contains "." . -}} + {{ "" | indent 0 }} ~(?.+)\.{{ . }} + {{- end -}} + {{- end -}}; + if ($http_x_forwarded_proto = '') { + set $http_x_forwarded_proto $scheme; + } + ## Application specific logs + ## access_log /var/log/nginx/artifactory-access.log timing; + ## error_log /var/log/nginx/artifactory-error.log; + rewrite ^/artifactory/?$ / redirect; + if ( $repo != "" ) { + rewrite ^/(v1|v2)/(.*) /artifactory/api/docker/$repo/$1/$2 break; + } + chunked_transfer_encoding on; + client_max_body_size 0; + + location / { + proxy_read_timeout 900; + proxy_pass_header Server; + proxy_cookie_path ~*^/.* /; + proxy_pass {{ include "artifactory-ha.scheme" . }}://{{ include "artifactory-ha.fullname" . }}:{{ .Values.artifactory.externalPort }}/; + {{- if .Values.nginx.service.ssloffload}} + proxy_set_header X-JFrog-Override-Base-Url $http_x_forwarded_proto://$host; + {{- else }} + proxy_set_header X-JFrog-Override-Base-Url $http_x_forwarded_proto://$host:$server_port; + proxy_set_header X-Forwarded-Port $server_port; + {{- end }} + proxy_set_header X-Forwarded-Proto $http_x_forwarded_proto; + proxy_set_header Host $http_host; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always; + + location /artifactory/ { + if ( $request_uri ~ ^/artifactory/(.*)$ ) { + proxy_pass {{ include "artifactory-ha.scheme" . }}://{{ include "artifactory-ha.fullname" . }}:{{ .Values.artifactory.externalArtifactoryPort }}/artifactory/$1; + } + proxy_pass {{ include "artifactory-ha.scheme" . }}://{{ include "artifactory-ha.fullname" . }}:{{ .Values.artifactory.externalArtifactoryPort }}/artifactory/; + } + } + } + + ## A list of custom ports to expose on the NGINX pod. Follows the conventional Kubernetes yaml syntax for container ports. + customPorts: + - containerPort: 8088 + name: http2 + service: + ## A list of custom ports to expose through the Ingress controller service. Follows the conventional Kubernetes yaml syntax for service ports. + customPorts: + - port: 8088 + targetPort: 8088 + protocol: TCP + name: http2 diff --git a/charts/jfrog/artifactory-ha/107.104.10/ci/large-values.yaml b/charts/jfrog/artifactory-ha/107.104.10/ci/large-values.yaml new file mode 100644 index 0000000000..153307aa26 --- /dev/null +++ b/charts/jfrog/artifactory-ha/107.104.10/ci/large-values.yaml @@ -0,0 +1,85 @@ +# Leave this file empty to ensure that CI runs builds against the default configuration in values.yaml. +databaseUpgradeReady: true + +# To Fix ct tool --reuse-values - PASSWORDS ERROR: you must provide your current passwords when upgrade the release +postgresql: + postgresqlPassword: password + persistence: + enabled: false +artifactory: + masterKey: FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + persistence: + enabled: false + database: + maxOpenConnections: 150 + tomcat: + connector: + maxThreads: 300 + primary: + replicaCount: 4 + resources: + requests: + memory: "6Gi" + cpu: "2" + limits: + memory: "10Gi" + cpu: "8" + javaOpts: + xms: "8g" + xmx: "10g" +access: + database: + maxOpenConnections: 150 + tomcat: + connector: + maxThreads: 100 +router: + resources: + requests: + memory: "200Mi" + cpu: "200m" + limits: + memory: "1Gi" + cpu: "1" +frontend: + resources: + requests: + memory: "200Mi" + cpu: "200m" + limits: + memory: "1Gi" + cpu: "1" +metadata: + database: + maxOpenConnections: 150 + resources: + requests: + memory: "200Mi" + cpu: "200m" + limits: + memory: "1Gi" + cpu: "1" +event: + resources: + requests: + memory: "200Mi" + cpu: "200m" + limits: + memory: "1Gi" + cpu: "1" +jfconnect: + resources: + requests: + memory: "200Mi" + cpu: "200m" + limits: + memory: "1Gi" + cpu: "1" +observability: + resources: + requests: + memory: "200Mi" + cpu: "200m" + limits: + memory: "1Gi" + cpu: "1" diff --git a/charts/jfrog/artifactory-ha/107.104.10/ci/loggers-values.yaml b/charts/jfrog/artifactory-ha/107.104.10/ci/loggers-values.yaml new file mode 100644 index 0000000000..03c94be953 --- /dev/null +++ b/charts/jfrog/artifactory-ha/107.104.10/ci/loggers-values.yaml @@ -0,0 +1,43 @@ +# Leave this file empty to ensure that CI runs builds against the default configuration in values.yaml. +databaseUpgradeReady: true + +# To Fix ct tool --reuse-values - PASSWORDS ERROR: you must provide your current passwords when upgrade the release +postgresql: + postgresqlPassword: password + persistence: + enabled: false +artifactory: + persistence: + enabled: false + resources: + requests: + memory: "4Gi" + cpu: "2" + limits: + memory: "6Gi" + cpu: "4" + javaOpts: + xms: "4g" + xmx: "4g" + + loggers: + - access-audit.log + - access-request.log + - access-security-audit.log + - access-service.log + - artifactory-access.log + - artifactory-event.log + - artifactory-import-export.log + - artifactory-request.log + - artifactory-service.log + - frontend-request.log + - frontend-service.log + - metadata-request.log + - metadata-service.log + - router-request.log + - router-service.log + - router-traefik.log + + catalinaLoggers: + - tomcat-catalina.log + - tomcat-localhost.log diff --git a/charts/jfrog/artifactory-ha/107.104.10/ci/medium-values.yaml b/charts/jfrog/artifactory-ha/107.104.10/ci/medium-values.yaml new file mode 100644 index 0000000000..115e7d4602 --- /dev/null +++ b/charts/jfrog/artifactory-ha/107.104.10/ci/medium-values.yaml @@ -0,0 +1,85 @@ +# Leave this file empty to ensure that CI runs builds against the default configuration in values.yaml. +databaseUpgradeReady: true + +# To Fix ct tool --reuse-values - PASSWORDS ERROR: you must provide your current passwords when upgrade the release +postgresql: + postgresqlPassword: password + persistence: + enabled: false +artifactory: + masterKey: FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + persistence: + enabled: false + database: + maxOpenConnections: 100 + tomcat: + connector: + maxThreads: 200 + primary: + replicaCount: 3 + resources: + requests: + memory: "4Gi" + cpu: "2" + limits: + memory: "8Gi" + cpu: "6" + javaOpts: + xms: "6g" + xmx: "8g" +access: + database: + maxOpenConnections: 100 + tomcat: + connector: + maxThreads: 50 +router: + resources: + requests: + memory: "200Mi" + cpu: "200m" + limits: + memory: "1Gi" + cpu: "1" +frontend: + resources: + requests: + memory: "200Mi" + cpu: "200m" + limits: + memory: "1Gi" + cpu: "1" +metadata: + database: + maxOpenConnections: 100 + resources: + requests: + memory: "200Mi" + cpu: "200m" + limits: + memory: "1Gi" + cpu: "1" +event: + resources: + requests: + memory: "200Mi" + cpu: "200m" + limits: + memory: "1Gi" + cpu: "1" +jfconnect: + resources: + requests: + memory: "200Mi" + cpu: "200m" + limits: + memory: "1Gi" + cpu: "1" +observability: + resources: + requests: + memory: "200Mi" + cpu: "200m" + limits: + memory: "1Gi" + cpu: "1" diff --git a/charts/jfrog/artifactory-ha/107.104.10/ci/migration-disabled-values.yaml b/charts/jfrog/artifactory-ha/107.104.10/ci/migration-disabled-values.yaml new file mode 100644 index 0000000000..44895a3731 --- /dev/null +++ b/charts/jfrog/artifactory-ha/107.104.10/ci/migration-disabled-values.yaml @@ -0,0 +1,31 @@ +databaseUpgradeReady: true +# To Fix ct tool --reuse-values - PASSWORDS ERROR: you must provide your current passwords when upgrade the release +postgresql: + postgresqlPassword: password + persistence: + enabled: false +artifactory: + masterKey: FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + migration: + enabled: false + persistence: + enabled: false + primary: + resources: + requests: + memory: "4Gi" + cpu: "2" + limits: + memory: "6Gi" + cpu: "4" + javaOpts: + xms: "4g" + xmx: "4g" + node: + resources: + requests: + memory: "4Gi" + cpu: "2" + limits: + memory: "6Gi" + cpu: "4" diff --git a/charts/jfrog/artifactory-ha/107.104.10/ci/nginx-autoreload-values.yaml b/charts/jfrog/artifactory-ha/107.104.10/ci/nginx-autoreload-values.yaml new file mode 100644 index 0000000000..a6f4e8001f --- /dev/null +++ b/charts/jfrog/artifactory-ha/107.104.10/ci/nginx-autoreload-values.yaml @@ -0,0 +1,53 @@ +# Leave this file empty to ensure that CI runs builds against the default configuration in values.yaml. +databaseUpgradeReady: true +## This is an exception here because HA needs masterKey to connect with other node members and it is commented in values to support 6.x to 7.x Migration +## Please refer https://github.com/jfrog/charts/blob/master/stable/artifactory-ha/README.md#special-upgrade-notes-1 +artifactory: + masterKey: FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + persistence: + enabled: false + primary: + resources: + requests: + memory: "4Gi" + cpu: "2" + limits: + memory: "6Gi" + cpu: "4" + javaOpts: + xms: "4g" + xmx: "4g" + node: + resources: + requests: + memory: "4Gi" + cpu: "2" + limits: + memory: "6Gi" + cpu: "4" +# To Fix ct tool --reuse-values - PASSWORDS ERROR: you must provide your current passwords when upgrade the release +postgresql: + postgresqlPassword: password + persistence: + enabled: false + +nginx: + customVolumes: | + - name: scripts + configMap: + name: {{ template "artifactory-ha.fullname" . }}-nginx-scripts + defaultMode: 0550 + customVolumeMounts: | + - name: scripts + mountPath: /var/opt/jfrog/nginx/scripts/ + customCommand: + - /bin/sh + - -c + - | + # watch for configmap changes + /sbin/inotifyd /var/opt/jfrog/nginx/scripts/configreloader.sh {{ .Values.nginx.persistence.mountPath -}}/conf.d:n & + {{ if .Values.nginx.https.enabled -}} + # watch for tls secret changes + /sbin/inotifyd /var/opt/jfrog/nginx/scripts/configreloader.sh {{ .Values.nginx.persistence.mountPath -}}/ssl:n & + {{ end -}} + nginx -g 'daemon off;' diff --git a/charts/jfrog/artifactory-ha/107.104.10/ci/rtsplit-access-tls-values.yaml b/charts/jfrog/artifactory-ha/107.104.10/ci/rtsplit-access-tls-values.yaml new file mode 100644 index 0000000000..6f3b13cb14 --- /dev/null +++ b/charts/jfrog/artifactory-ha/107.104.10/ci/rtsplit-access-tls-values.yaml @@ -0,0 +1,106 @@ +databaseUpgradeReady: true +artifactory: + replicaCount: 3 + joinKey: EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE + masterKey: FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + persistence: + enabled: false + primary: + resources: + requests: + memory: "4Gi" + cpu: "2" + limits: + memory: "6Gi" + cpu: "4" + node: + resources: + requests: + memory: "4Gi" + cpu: "2" + limits: + memory: "6Gi" + cpu: "4" + javaOpts: + xms: "4g" + xmx: "4g" + +access: + accessConfig: + security: + tls: true + resetAccessCAKeys: true + +postgresql: + postgresqlPassword: password + postgresqlExtendedConf: + maxConnections: 102 + persistence: + enabled: false + +rbac: + create: true +serviceAccount: + create: true + automountServiceAccountToken: true + +ingress: + enabled: true + className: "testclass" + hosts: + - demonow.xyz +nginx: + enabled: false +jfconnect: + enabled: true + resources: + requests: + memory: "100Mi" + cpu: "100m" + limits: + memory: "1Gi" + cpu: "1" +mc: + enabled: true +splitServicesToContainers: true + +router: + resources: + requests: + memory: "100Mi" + cpu: "100m" + limits: + memory: "1Gi" + cpu: "1" +frontend: + resources: + requests: + memory: "100Mi" + cpu: "100m" + limits: + memory: "1Gi" + cpu: "1" +metadata: + resources: + requests: + memory: "100Mi" + cpu: "100m" + limits: + memory: "1Gi" + cpu: "1" +event: + resources: + requests: + memory: "100Mi" + cpu: "100m" + limits: + memory: "1Gi" + cpu: "1" +observability: + resources: + requests: + memory: "100Mi" + cpu: "100m" + limits: + memory: "1Gi" + cpu: "1" diff --git a/charts/jfrog/artifactory-ha/107.104.10/ci/rtsplit-values.yaml b/charts/jfrog/artifactory-ha/107.104.10/ci/rtsplit-values.yaml new file mode 100644 index 0000000000..87832a5051 --- /dev/null +++ b/charts/jfrog/artifactory-ha/107.104.10/ci/rtsplit-values.yaml @@ -0,0 +1,155 @@ +databaseUpgradeReady: true +artifactory: + replicaCount: 3 + joinKey: EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE + masterKey: FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + persistence: + enabled: false + primary: + resources: + requests: + memory: "4Gi" + cpu: "2" + limits: + memory: "6Gi" + cpu: "4" + javaOpts: + xms: "4g" + xmx: "4g" + node: + resources: + requests: + memory: "4Gi" + cpu: "2" + limits: + memory: "6Gi" + cpu: "4" + + # Add lifecycle hooks for artifactory container + lifecycle: + postStart: + exec: + command: ["/bin/sh", "-c", "echo Hello from the artifactory postStart handler >> /tmp/message"] + preStop: + exec: + command: ["/bin/sh", "-c", "echo Hello from the artifactory postStart handler >> /tmp/message"] + +postgresql: + postgresqlPassword: password + postgresqlExtendedConf: + maxConnections: 102 + persistence: + enabled: false + +rbac: + create: true +serviceAccount: + create: true + automountServiceAccountToken: true + +ingress: + enabled: true + className: "testclass" + hosts: + - demonow.xyz +nginx: + enabled: false +jfconnect: + enabled: true + resources: + requests: + memory: "100Mi" + cpu: "100m" + limits: + memory: "1Gi" + cpu: "1" + # Add lifecycle hooks for jfconect container + lifecycle: + postStart: + exec: + command: ["/bin/sh", "-c", "echo Hello from the jfconnect postStart handler >> /tmp/message"] + preStop: + exec: + command: ["/bin/sh", "-c", "echo Hello from the jfconnect postStart handler >> /tmp/message"] + +mc: + enabled: true +splitServicesToContainers: true + +router: + resources: + requests: + memory: "100Mi" + cpu: "100m" + limits: + memory: "1Gi" + cpu: "1" + # Add lifecycle hooks for router container + lifecycle: + postStart: + exec: + command: ["/bin/sh", "-c", "echo Hello from the router postStart handler >> /tmp/message"] + preStop: + exec: + command: ["/bin/sh", "-c", "echo Hello from the router postStart handler >> /tmp/message"] +frontend: + resources: + requests: + memory: "100Mi" + cpu: "100m" + limits: + memory: "1Gi" + cpu: "1" + # Add lifecycle hooks for frontend container + lifecycle: + postStart: + exec: + command: ["/bin/sh", "-c", "echo Hello from the frontend postStart handler >> /tmp/message"] + preStop: + exec: + command: ["/bin/sh", "-c", "echo Hello from the frontend postStart handler >> /tmp/message"] +metadata: + resources: + requests: + memory: "100Mi" + cpu: "100m" + limits: + memory: "1Gi" + cpu: "1" + lifecycle: + postStart: + exec: + command: ["/bin/sh", "-c", "echo Hello from the metadata postStart handler >> /tmp/message"] + preStop: + exec: + command: ["/bin/sh", "-c", "echo Hello from the metadata postStart handler >> /tmp/message"] +event: + resources: + requests: + memory: "100Mi" + cpu: "100m" + limits: + memory: "1Gi" + cpu: "1" + lifecycle: + postStart: + exec: + command: ["/bin/sh", "-c", "echo Hello from the event postStart handler >> /tmp/message"] + preStop: + exec: + command: ["/bin/sh", "-c", "echo Hello from the event postStart handler >> /tmp/message"] +observability: + resources: + requests: + memory: "100Mi" + cpu: "100m" + limits: + memory: "1Gi" + cpu: "1" + lifecycle: + postStart: + exec: + command: ["/bin/sh", "-c", "echo Hello from the observability postStart handler >> /tmp/message"] + preStop: + exec: + command: ["/bin/sh", "-c", "echo Hello from the observability postStart handler >> /tmp/message"] diff --git a/charts/jfrog/artifactory-ha/107.104.10/ci/small-values.yaml b/charts/jfrog/artifactory-ha/107.104.10/ci/small-values.yaml new file mode 100644 index 0000000000..9da2419d80 --- /dev/null +++ b/charts/jfrog/artifactory-ha/107.104.10/ci/small-values.yaml @@ -0,0 +1,90 @@ +# Leave this file empty to ensure that CI runs builds against the default configuration in values.yaml. +databaseUpgradeReady: true + +# To Fix ct tool --reuse-values - PASSWORDS ERROR: you must provide your current passwords when upgrade the release +postgresql: + postgresqlPassword: password + persistence: + enabled: false +artifactory: + masterKey: FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + persistence: + enabled: false + database: + maxOpenConnections: 80 + tomcat: + connector: + maxThreads: 200 + primary: + replicaCount: 1 + resources: + requests: + memory: "4Gi" + cpu: "2" + limits: + memory: "6Gi" + cpu: "4" + javaOpts: + xms: "4g" + xmx: "6g" + node: + replicaCount: 2 +access: + database: + maxOpenConnections: 80 + tomcat: + connector: + maxThreads: 50 +router: + resources: + requests: + memory: "100Mi" + cpu: "100m" + limits: + memory: "1Gi" + cpu: "1" +frontend: + resources: + requests: + memory: "100Mi" + cpu: "100m" + limits: + memory: "1Gi" + cpu: "1" +metadata: + database: + maxOpenConnections: 80 + resources: + requests: + memory: "100Mi" + cpu: "100m" + limits: + memory: "1Gi" + cpu: "1" +event: + resources: + requests: + memory: "100Mi" + cpu: "100m" + limits: + memory: "1Gi" + cpu: "1" +jfconnect: + resources: + requests: + memory: "100Mi" + cpu: "100m" + limits: + memory: "1Gi" + cpu: "1" +observability: + resources: + requests: + memory: "100Mi" + cpu: "100m" + limits: + memory: "1Gi" + cpu: "1" + +rtfs: + enabled: true diff --git a/charts/jfrog/artifactory-ha/107.104.10/ci/test-values.yaml b/charts/jfrog/artifactory-ha/107.104.10/ci/test-values.yaml new file mode 100644 index 0000000000..8bbbb5b3e5 --- /dev/null +++ b/charts/jfrog/artifactory-ha/107.104.10/ci/test-values.yaml @@ -0,0 +1,85 @@ +databaseUpgradeReady: true +artifactory: + metrics: + enabled: true + podSecurityContext: + fsGroupChangePolicy: "OnRootMismatch" + masterKey: FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + unifiedSecretInstallation: false + persistence: + enabled: false + primary: + resources: + requests: + memory: "4Gi" + cpu: "2" + limits: + memory: "6Gi" + cpu: "4" + javaOpts: + xms: "4g" + xmx: "4g" + node: + resources: + requests: + memory: "4Gi" + cpu: "2" + limits: + memory: "6Gi" + cpu: "4" + statefulset: + annotations: + artifactory: test + +postgresql: + postgresqlPassword: "password" + postgresqlExtendedConf: + maxConnections: "102" + persistence: + enabled: false +rbac: + create: true +serviceAccount: + create: true + automountServiceAccountToken: true +ingress: + enabled: true + className: "testclass" + hosts: + - demonow.xyz +nginx: + enabled: false + +jfconnect: + enabled: false + +## filebeat sidecar +filebeat: + enabled: true + filebeatYml: | + logging.level: info + path.data: {{ .Values.artifactory.persistence.mountPath }}/log/filebeat + name: artifactory-filebeat + queue.spool: + file: + permissions: 0760 + filebeat.inputs: + - type: log + enabled: true + close_eof: ${CLOSE:false} + paths: + - {{ .Values.artifactory.persistence.mountPath }}/log/*.log + fields: + service: "jfrt" + log_type: "artifactory" + output.file: + path: "/tmp/filebeat" + filename: filebeat + readinessProbe: + exec: + command: + - sh + - -c + - | + #!/usr/bin/env bash -e + curl --fail 127.0.0.1:5066 diff --git a/charts/jfrog/artifactory-ha/107.104.10/files/binarystore.xml b/charts/jfrog/artifactory-ha/107.104.10/files/binarystore.xml new file mode 100644 index 0000000000..d807bf2e45 --- /dev/null +++ b/charts/jfrog/artifactory-ha/107.104.10/files/binarystore.xml @@ -0,0 +1,442 @@ +{{- if and (eq .Values.artifactory.persistence.type "nfs") (.Values.artifactory.haDataDir.enabled) }} + + + + + + + +{{- end }} +{{- if and (eq .Values.artifactory.persistence.type "nfs") (not .Values.artifactory.haDataDir.enabled) }} + + {{- if (.Values.artifactory.persistence.maxCacheSize) }} + + + + + + {{- else }} + + + + {{- end }} + + {{- if .Values.artifactory.persistence.maxCacheSize }} + + {{ .Values.artifactory.persistence.maxCacheSize | int64 }} + {{ .Values.artifactory.persistence.cacheProviderDir }} + {{- if .Values.artifactory.persistence.maxFileSizeLimit }} + {{.Values.artifactory.persistence.maxFileSizeLimit | int64}} + {{- end }} + {{- if .Values.artifactory.persistence.skipDuringUpload }} + {{.Values.artifactory.persistence.skipDuringUpload}} + {{- end }} + + {{- end }} + + + {{ .Values.artifactory.persistence.nfs.dataDir }}/filestore + + + +{{- end }} + +{{- if eq .Values.artifactory.persistence.type "file-system" }} + +{{- if .Values.artifactory.persistence.fileSystem.existingSharedClaim.enabled }} + + + + + + {{- range $sharedClaimNumber, $e := until (.Values.artifactory.persistence.fileSystem.existingSharedClaim.numberOfExistingClaims|int) -}} + + {{- end }} + + + + + + {{ .Values.artifactory.persistence.maxCacheSize | int64 }} + {{ .Values.artifactory.persistence.cacheProviderDir }} + {{- if .Values.artifactory.persistence.maxFileSizeLimit }} + {{.Values.artifactory.persistence.maxFileSizeLimit | int64}} + {{- end }} + {{- if .Values.artifactory.persistence.skipDuringUpload }} + {{.Values.artifactory.persistence.skipDuringUpload}} + {{- end }} + + + // Specify the read and write strategy and redundancy for the sharding binary provider + + roundRobin + percentageFreeSpace + 2 + + + {{- range $sharedClaimNumber, $e := until (.Values.artifactory.persistence.fileSystem.existingSharedClaim.numberOfExistingClaims|int) -}} + //For each sub-provider (mount), specify the filestore location + + filestore{{ $sharedClaimNumber }} + + {{- end }} + +{{- else }} + + + + + crossNetworkStrategy + crossNetworkStrategy + {{ .Values.artifactory.persistence.redundancy }} + 2 + 2 + + + + + + + + + {{ .Values.artifactory.persistence.maxCacheSize | int64 }} + {{ .Values.artifactory.persistence.cacheProviderDir }} + {{- if .Values.artifactory.persistence.maxFileSizeLimit }} + {{.Values.artifactory.persistence.maxFileSizeLimit | int64}} + {{- end }} + {{- if .Values.artifactory.persistence.skipDuringUpload }} + {{.Values.artifactory.persistence.skipDuringUpload}} + {{- end }} + + + + + shard-fs-1 + local + + + + + 30 + tester-remote1 + 10000 + remote + + + +{{- end }} +{{- end }} +{{- if or (eq .Values.artifactory.persistence.type "google-storage") (eq .Values.artifactory.persistence.type "google-storage-v2") (eq .Values.artifactory.persistence.type "google-storage-v2-direct") }} + + + {{- if or (eq .Values.artifactory.persistence.type "google-storage") (eq .Values.artifactory.persistence.type "google-storage-v2") }} + + + + crossNetworkStrategy + crossNetworkStrategy + {{ .Values.artifactory.persistence.redundancy }} + 2 + + + + + + + + + + + {{- else if eq .Values.artifactory.persistence.type "google-storage-v2-direct" }} + + + + + + {{- end }} + + + + {{ .Values.artifactory.persistence.maxCacheSize | int64 }} + {{ .Values.artifactory.persistence.cacheProviderDir }} + {{- if .Values.artifactory.persistence.maxFileSizeLimit }} + {{.Values.artifactory.persistence.maxFileSizeLimit | int64}} + {{- end }} + {{- if .Values.artifactory.persistence.skipDuringUpload }} + {{.Values.artifactory.persistence.skipDuringUpload}} + {{- end }} + + + {{- if or (eq .Values.artifactory.persistence.type "google-storage") (eq .Values.artifactory.persistence.type "google-storage-v2") }} + + local + + + + + 30 + 10000 + remote + + {{- end }} + + + + {{- if .Values.artifactory.persistence.googleStorage.useInstanceCredentials }} + true + {{- else }} + false + {{- end }} + {{ .Values.artifactory.persistence.googleStorage.enableSignedUrlRedirect }} + google-cloud-storage + {{ .Values.artifactory.persistence.googleStorage.endpoint }} + {{ .Values.artifactory.persistence.googleStorage.httpsOnly }} + {{ .Values.artifactory.persistence.googleStorage.bucketName }} + {{ .Values.artifactory.persistence.googleStorage.path }} + {{ .Values.artifactory.persistence.googleStorage.bucketExists }} + {{- if .Values.artifactory.persistence.googleStorage.signedUrlExpirySeconds }} + {{ .Values.artifactory.persistence.googleStorage.signedUrlExpirySeconds | int64 }} + {{- end }} + + +{{- end }} +{{- if or (eq .Values.artifactory.persistence.type "aws-s3-v3") (eq .Values.artifactory.persistence.type "s3-storage-v3-direct") (eq .Values.artifactory.persistence.type "s3-storage-v3-archive") }} + + + {{- if eq .Values.artifactory.persistence.type "aws-s3-v3" }} + + + + + + + + + + + + + {{- else if eq .Values.artifactory.persistence.type "s3-storage-v3-direct" }} + + + + + + {{- else if eq .Values.artifactory.persistence.type "s3-storage-v3-archive" }} + + + + + + + {{- end }} + + {{- if eq .Values.artifactory.persistence.type "aws-s3-v3" }} + + crossNetworkStrategy + crossNetworkStrategy + {{ .Values.artifactory.persistence.redundancy }} + + + + + remote + + + + local + + + + + {{ .Values.artifactory.persistence.maxCacheSize | int64 }} + {{ .Values.artifactory.persistence.cacheProviderDir }} + {{- if .Values.artifactory.persistence.maxFileSizeLimit }} + {{.Values.artifactory.persistence.maxFileSizeLimit | int64}} + {{- end }} + {{- if .Values.artifactory.persistence.skipDuringUpload }} + {{.Values.artifactory.persistence.skipDuringUpload}} + {{- end }} + + {{- end }} + + {{- if eq .Values.artifactory.persistence.type "s3-storage-v3-direct" }} + + + {{ .Values.artifactory.persistence.maxCacheSize | int64 }} + {{ .Values.artifactory.persistence.cacheProviderDir }} + {{- if .Values.artifactory.persistence.maxFileSizeLimit }} + {{.Values.artifactory.persistence.maxFileSizeLimit | int64}} + {{- end }} + {{- if .Values.artifactory.persistence.skipDuringUpload }} + {{.Values.artifactory.persistence.skipDuringUpload}} + {{- end }} + + {{- end }} + + {{- with .Values.artifactory.persistence.awsS3V3 }} + + {{ .testConnection }} + {{- if .identity }} + {{ .identity }} + {{- end }} + {{- if .credential }} + {{ .credential }} + {{- end }} + {{ .region }} + {{ .bucketName }} + {{ .path }} + {{ .endpoint }} + {{- with .port }} + {{ . }} + {{- end }} + {{- with .useHttp }} + {{ . }} + {{- end }} + {{- with .maxConnections }} + {{ . }} + {{- end }} + {{- with .connectionTimeout }} + {{ . }} + {{- end }} + {{- with .socketTimeout }} + {{ . }} + {{- end }} + {{- with .kmsServerSideEncryptionKeyId }} + {{ . }} + {{- end }} + {{- with .kmsKeyRegion }} + {{ . }} + {{- end }} + {{- with .kmsCryptoMode }} + {{ . }} + {{- end }} + {{- if .useInstanceCredentials }} + true + {{- else }} + false + {{- end }} + {{ .usePresigning }} + {{ .signatureExpirySeconds }} + {{ .signedUrlExpirySeconds }} + {{- with .cloudFrontDomainName }} + {{ . }} + {{- end }} + {{- with .cloudFrontKeyPairId }} + {{ . }} + {{- end }} + {{- with .cloudFrontPrivateKey }} + {{ . }} + {{- end }} + {{- with .enableSignedUrlRedirect }} + {{ . }} + {{- end }} + {{- with .enablePathStyleAccess }} + {{ . }} + {{- end }} + {{- with .multiPartLimit }} + {{ . | int64 }} + {{- end }} + {{- with .multipartElementSize }} + {{ . | int64 }} + {{- end }} + + {{- end }} + +{{- end }} + +{{- if or (eq .Values.artifactory.persistence.type "azure-blob") (eq .Values.artifactory.persistence.type "azure-blob-storage-direct") }} + + + {{- if eq .Values.artifactory.persistence.type "azure-blob" }} + + + + + + + + + + + + + {{- else if eq .Values.artifactory.persistence.type "azure-blob-storage-direct" }} + + + + + + {{- end }} + + + + {{ .Values.artifactory.persistence.maxCacheSize | int64 }} + {{ .Values.artifactory.persistence.cacheProviderDir }} + {{- if .Values.artifactory.persistence.maxFileSizeLimit }} + {{.Values.artifactory.persistence.maxFileSizeLimit | int64}} + {{- end }} + {{- if .Values.artifactory.persistence.skipDuringUpload }} + {{.Values.artifactory.persistence.skipDuringUpload}} + {{- end }} + + + {{- if eq .Values.artifactory.persistence.type "azure-blob" }} + + + crossNetworkStrategy + crossNetworkStrategy + 2 + 1 + + + + + remote + + + + local + + {{- end }} + + + + {{ .Values.artifactory.persistence.azureBlob.accountName }} + {{ .Values.artifactory.persistence.azureBlob.accountKey }} + {{ .Values.artifactory.persistence.azureBlob.endpoint }} + {{ .Values.artifactory.persistence.azureBlob.containerName }} + {{ .Values.artifactory.persistence.azureBlob.multiPartLimit | int64 }} + {{ .Values.artifactory.persistence.azureBlob.multipartElementSize | int64 }} + {{ .Values.artifactory.persistence.azureBlob.testConnection }} + + +{{- end }} +{{- if eq .Values.artifactory.persistence.type "azure-blob-storage-v2-direct" -}} + + + + {{ .Values.artifactory.persistence.maxCacheSize | int64 }} + {{ .Values.artifactory.persistence.cacheProviderDir }} + {{- if .Values.artifactory.persistence.maxFileSizeLimit }} + {{.Values.artifactory.persistence.maxFileSizeLimit | int64}} + {{- end }} + {{- if .Values.artifactory.persistence.skipDuringUpload }} + {{.Values.artifactory.persistence.skipDuringUpload}} + {{- end }} + + + {{ .Values.artifactory.persistence.azureBlob.accountName }} + {{ .Values.artifactory.persistence.azureBlob.accountKey }} + {{ .Values.artifactory.persistence.azureBlob.endpoint }} + {{ .Values.artifactory.persistence.azureBlob.containerName }} + {{ .Values.artifactory.persistence.azureBlob.multiPartLimit | int64 }} + {{ .Values.artifactory.persistence.azureBlob.multipartElementSize | int64 }} + {{ .Values.artifactory.persistence.azureBlob.testConnection }} + + +{{- end }} \ No newline at end of file diff --git a/charts/jfrog/artifactory-ha/107.104.10/files/installer-info.json b/charts/jfrog/artifactory-ha/107.104.10/files/installer-info.json new file mode 100644 index 0000000000..cf6b020fb3 --- /dev/null +++ b/charts/jfrog/artifactory-ha/107.104.10/files/installer-info.json @@ -0,0 +1,32 @@ +{ + "productId": "Helm_artifactory-ha/{{ .Chart.Version }}", + "features": [ + { + "featureId": "Platform/{{ printf "%s-%s" "kubernetes" .Capabilities.KubeVersion.Version }}" + }, + { + "featureId": "Database/{{ .Values.database.type }}" + }, + { + "featureId": "PostgreSQL_Enabled/{{ .Values.postgresql.enabled }}" + }, + { + "featureId": "Nginx_Enabled/{{ .Values.nginx.enabled }}" + }, + { + "featureId": "ArtifactoryPersistence_Type/{{ .Values.artifactory.persistence.type }}" + }, + { + "featureId": "SplitServicesToContainers_Enabled/{{ .Values.splitServicesToContainers }}" + }, + { + "featureId": "UnifiedSecretInstallation_Enabled/{{ .Values.artifactory.unifiedSecretInstallation }}" + }, + { + "featureId": "Filebeat_Enabled/{{ .Values.filebeat.enabled }}" + }, + { + "featureId": "ReplicaCount/{{ add .Values.artifactory.primary.replicaCount .Values.artifactory.node.replicaCount }}" + } + ] +} \ No newline at end of file diff --git a/charts/jfrog/artifactory-ha/107.104.10/files/migrate.sh b/charts/jfrog/artifactory-ha/107.104.10/files/migrate.sh new file mode 100644 index 0000000000..ba44160f47 --- /dev/null +++ b/charts/jfrog/artifactory-ha/107.104.10/files/migrate.sh @@ -0,0 +1,4311 @@ +#!/bin/bash + +# Flags +FLAG_Y="y" +FLAG_N="n" +FLAGS_Y_N="$FLAG_Y $FLAG_N" +FLAG_NOT_APPLICABLE="_NA_" + +CURRENT_VERSION=$1 + +WRAPPER_SCRIPT_TYPE_RPMDEB="RPMDEB" +WRAPPER_SCRIPT_TYPE_DOCKER_COMPOSE="DOCKERCOMPOSE" + +SENSITIVE_KEY_VALUE="__sensitive_key_hidden___" + +# Shared system keys +SYS_KEY_SHARED_JFROGURL="shared.jfrogUrl" +SYS_KEY_SHARED_SECURITY_JOINKEY="shared.security.joinKey" +SYS_KEY_SHARED_SECURITY_MASTERKEY="shared.security.masterKey" + +SYS_KEY_SHARED_NODE_ID="shared.node.id" +SYS_KEY_SHARED_JAVAHOME="shared.javaHome" + +SYS_KEY_SHARED_DATABASE_TYPE="shared.database.type" +SYS_KEY_SHARED_DATABASE_TYPE_VALUE_POSTGRES="postgresql" +SYS_KEY_SHARED_DATABASE_DRIVER="shared.database.driver" +SYS_KEY_SHARED_DATABASE_URL="shared.database.url" +SYS_KEY_SHARED_DATABASE_USERNAME="shared.database.username" +SYS_KEY_SHARED_DATABASE_PASSWORD="shared.database.password" + +SYS_KEY_SHARED_ELASTICSEARCH_URL="shared.elasticsearch.url" +SYS_KEY_SHARED_ELASTICSEARCH_USERNAME="shared.elasticsearch.username" +SYS_KEY_SHARED_ELASTICSEARCH_PASSWORD="shared.elasticsearch.password" +SYS_KEY_SHARED_ELASTICSEARCH_CLUSTERSETUP="shared.elasticsearch.clusterSetup" +SYS_KEY_SHARED_ELASTICSEARCH_UNICASTFILE="shared.elasticsearch.unicastFile" +SYS_KEY_SHARED_ELASTICSEARCH_CLUSTERSETUP_VALUE="YES" + +# Define this in product specific script. Should contain the path to unitcast file +# File used by insight server to write cluster active nodes info. This will be read by elasticsearch +#SYS_KEY_SHARED_ELASTICSEARCH_UNICASTFILE_VALUE="" + +SYS_KEY_RABBITMQ_ACTIVE_NODE_NAME="shared.rabbitMq.active.node.name" +SYS_KEY_RABBITMQ_ACTIVE_NODE_IP="shared.rabbitMq.active.node.ip" + +# Filenames +FILE_NAME_SYSTEM_YAML="system.yaml" +FILE_NAME_JOIN_KEY="join.key" +FILE_NAME_MASTER_KEY="master.key" +FILE_NAME_INSTALLER_YAML="installer.yaml" + +# Global constants used in business logic +NODE_TYPE_STANDALONE="standalone" +NODE_TYPE_CLUSTER_NODE="node" +NODE_TYPE_DATABASE="database" + +# External(isable) databases +DATABASE_POSTGRES="POSTGRES" +DATABASE_ELASTICSEARCH="ELASTICSEARCH" +DATABASE_RABBITMQ="RABBITMQ" + +POSTGRES_LABEL="PostgreSQL" +ELASTICSEARCH_LABEL="Elasticsearch" +RABBITMQ_LABEL="Rabbitmq" + +ARTIFACTORY_LABEL="Artifactory" +JFMC_LABEL="Mission Control" +DISTRIBUTION_LABEL="Distribution" +XRAY_LABEL="Xray" + +POSTGRES_CONTAINER="postgres" +ELASTICSEARCH_CONTAINER="elasticsearch" +RABBITMQ_CONTAINER="rabbitmq" +REDIS_CONTAINER="redis" + +#Adding a small timeout before a read ensures it is positioned correctly in the screen +read_timeout=0.5 + +# Options related to data directory location +PROMPT_DATA_DIR_LOCATION="Installation Directory" +KEY_DATA_DIR_LOCATION="installer.data_dir" + +SYS_KEY_SHARED_NODE_HAENABLED="shared.node.haEnabled" +PROMPT_ADD_TO_CLUSTER="Are you adding an additional node to an existing product cluster?" +KEY_ADD_TO_CLUSTER="installer.ha" +VALID_VALUES_ADD_TO_CLUSTER="$FLAGS_Y_N" + +MESSAGE_POSTGRES_INSTALL="The installer can install a $POSTGRES_LABEL database, or you can connect to an existing compatible $POSTGRES_LABEL database\n(compatible databases: https://www.jfrog.com/confluence/display/JFROG/System+Requirements#SystemRequirements-RequirementsMatrix)" +PROMPT_POSTGRES_INSTALL="Do you want to install $POSTGRES_LABEL?" +KEY_POSTGRES_INSTALL="installer.install_postgresql" +VALID_VALUES_POSTGRES_INSTALL="$FLAGS_Y_N" + +# Postgres connection details +RPM_DEB_POSTGRES_HOME_DEFAULT="/var/opt/jfrog/postgres" +RPM_DEB_MESSAGE_STANDALONE_POSTGRES_DATA="$POSTGRES_LABEL home will have data and its configuration" +RPM_DEB_PROMPT_STANDALONE_POSTGRES_DATA="Type desired $POSTGRES_LABEL home location" +RPM_DEB_KEY_STANDALONE_POSTGRES_DATA="installer.postgresql.home" + +MESSAGE_DATABASE_URL="Provide the database connection details" +PROMPT_DATABASE_URL(){ + local databaseURlExample= + case "$PRODUCT_NAME" in + $ARTIFACTORY_LABEL) + databaseURlExample="jdbc:postgresql://:/artifactory" + ;; + $JFMC_LABEL) + databaseURlExample="postgresql://:/mission_control?sslmode=disable" + ;; + $DISTRIBUTION_LABEL) + databaseURlExample="jdbc:postgresql://:/distribution?sslmode=disable" + ;; + $XRAY_LABEL) + databaseURlExample="postgres://:/xraydb?sslmode=disable" + ;; + esac + if [ -z "$databaseURlExample" ]; then + echo -n "$POSTGRES_LABEL URL" # For consistency with username and password + return + fi + echo -n "$POSTGRES_LABEL url. Example: [$databaseURlExample]" +} +REGEX_DATABASE_URL(){ + local databaseURlExample= + case "$PRODUCT_NAME" in + $ARTIFACTORY_LABEL) + databaseURlExample="jdbc:postgresql://.*/artifactory.*" + ;; + $JFMC_LABEL) + databaseURlExample="postgresql://.*/mission_control.*" + ;; + $DISTRIBUTION_LABEL) + databaseURlExample="jdbc:postgresql://.*/distribution.*" + ;; + $XRAY_LABEL) + databaseURlExample="postgres://.*/xraydb.*" + ;; + esac + echo -n "^$databaseURlExample\$" +} +ERROR_MESSAGE_DATABASE_URL="Invalid $POSTGRES_LABEL URL" +KEY_DATABASE_URL="$SYS_KEY_SHARED_DATABASE_URL" +#NOTE: It is important to display the label. Since the message may be hidden if URL is known +PROMPT_DATABASE_USERNAME="$POSTGRES_LABEL username" +KEY_DATABASE_USERNAME="$SYS_KEY_SHARED_DATABASE_USERNAME" +#NOTE: It is important to display the label. Since the message may be hidden if URL is known +PROMPT_DATABASE_PASSWORD="$POSTGRES_LABEL password" +KEY_DATABASE_PASSWORD="$SYS_KEY_SHARED_DATABASE_PASSWORD" +IS_SENSITIVE_DATABASE_PASSWORD="$FLAG_Y" + +MESSAGE_STANDALONE_ELASTICSEARCH_INSTALL="The installer can install a $ELASTICSEARCH_LABEL database or you can connect to an existing compatible $ELASTICSEARCH_LABEL database" +PROMPT_STANDALONE_ELASTICSEARCH_INSTALL="Do you want to install $ELASTICSEARCH_LABEL?" +KEY_STANDALONE_ELASTICSEARCH_INSTALL="installer.install_elasticsearch" +VALID_VALUES_STANDALONE_ELASTICSEARCH_INSTALL="$FLAGS_Y_N" + +# Elasticsearch connection details +MESSAGE_ELASTICSEARCH_DETAILS="Provide the $ELASTICSEARCH_LABEL connection details" +PROMPT_ELASTICSEARCH_URL="$ELASTICSEARCH_LABEL URL" +KEY_ELASTICSEARCH_URL="$SYS_KEY_SHARED_ELASTICSEARCH_URL" + +PROMPT_ELASTICSEARCH_USERNAME="$ELASTICSEARCH_LABEL username" +KEY_ELASTICSEARCH_USERNAME="$SYS_KEY_SHARED_ELASTICSEARCH_USERNAME" + +PROMPT_ELASTICSEARCH_PASSWORD="$ELASTICSEARCH_LABEL password" +KEY_ELASTICSEARCH_PASSWORD="$SYS_KEY_SHARED_ELASTICSEARCH_PASSWORD" +IS_SENSITIVE_ELASTICSEARCH_PASSWORD="$FLAG_Y" + +# Cluster related questions +MESSAGE_CLUSTER_MASTER_KEY="Provide the cluster's master key. It can be found in the data directory of the first node under /etc/security/master.key" +PROMPT_CLUSTER_MASTER_KEY="Master Key" +KEY_CLUSTER_MASTER_KEY="$SYS_KEY_SHARED_SECURITY_MASTERKEY" +IS_SENSITIVE_CLUSTER_MASTER_KEY="$FLAG_Y" + +MESSAGE_JOIN_KEY="The Join key is the secret key used to establish trust between services in the JFrog Platform.\n(You can copy the Join Key from Admin > User Management > Settings)" +PROMPT_JOIN_KEY="Join Key" +KEY_JOIN_KEY="$SYS_KEY_SHARED_SECURITY_JOINKEY" +IS_SENSITIVE_JOIN_KEY="$FLAG_Y" +REGEX_JOIN_KEY="^[a-zA-Z0-9]{16,}\$" +ERROR_MESSAGE_JOIN_KEY="Invalid Join Key" + +# Rabbitmq related cluster information +MESSAGE_RABBITMQ_ACTIVE_NODE_NAME="Provide an active ${RABBITMQ_LABEL} node name. Run the command [ hostname -s ] on any of the existing nodes in the product cluster to get this" +PROMPT_RABBITMQ_ACTIVE_NODE_NAME="${RABBITMQ_LABEL} active node name" +KEY_RABBITMQ_ACTIVE_NODE_NAME="$SYS_KEY_RABBITMQ_ACTIVE_NODE_NAME" + +# Rabbitmq related cluster information (necessary only for docker-compose) +PROMPT_RABBITMQ_ACTIVE_NODE_IP="${RABBITMQ_LABEL} active node ip" +KEY_RABBITMQ_ACTIVE_NODE_IP="$SYS_KEY_RABBITMQ_ACTIVE_NODE_IP" + +MESSAGE_JFROGURL(){ + echo -e "The JFrog URL allows ${PRODUCT_NAME} to connect to a JFrog Platform Instance.\n(You can copy the JFrog URL from Administration > User Management > Settings > Connection details)" +} +PROMPT_JFROGURL="JFrog URL" +KEY_JFROGURL="$SYS_KEY_SHARED_JFROGURL" +REGEX_JFROGURL="^https?://.*:{0,}[0-9]{0,4}\$" +ERROR_MESSAGE_JFROGURL="Invalid JFrog URL" + + +# Set this to FLAG_Y on upgrade +IS_UPGRADE="${FLAG_N}" + +# This belongs in JFMC but is the ONLY one that needs it so keeping it here for now. Can be made into a method and overridden if necessary +MESSAGE_MULTIPLE_PG_SCHEME="Please setup $POSTGRES_LABEL with schema as described in https://www.jfrog.com/confluence/display/JFROG/Installing+Mission+Control" + +_getMethodOutputOrVariableValue() { + unset EFFECTIVE_MESSAGE + local keyToSearch=$1 + local effectiveMessage= + local result="0" + # logSilly "Searching for method: [$keyToSearch]" + LC_ALL=C type "$keyToSearch" > /dev/null 2>&1 || result="$?" + if [[ "$result" == "0" ]]; then + # logSilly "Found method for [$keyToSearch]" + EFFECTIVE_MESSAGE="$($keyToSearch)" + return + fi + eval EFFECTIVE_MESSAGE=\${$keyToSearch} + if [ ! -z "$EFFECTIVE_MESSAGE" ]; then + return + fi + # logSilly "Didn't find method or variable for [$keyToSearch]" +} + + +# REF https://misc.flogisoft.com/bash/tip_colors_and_formatting +cClear="\e[0m" +cBlue="\e[38;5;69m" +cRedDull="\e[1;31m" +cYellow="\e[1;33m" +cRedBright="\e[38;5;197m" +cBold="\e[1m" + + +_loggerGetModeRaw() { + local MODE="$1" + case $MODE in + INFO) + printf "" + ;; + DEBUG) + printf "%s" "[${MODE}] " + ;; + WARN) + printf "${cRedDull}%s%s${cClear}" "[" "${MODE}" "] " + ;; + ERROR) + printf "${cRedBright}%s%s${cClear}" "[" "${MODE}" "] " + ;; + esac +} + + +_loggerGetMode() { + local MODE="$1" + case $MODE in + INFO) + printf "${cBlue}%s%-5s%s${cClear}" "[" "${MODE}" "]" + ;; + DEBUG) + printf "%-7s" "[${MODE}]" + ;; + WARN) + printf "${cRedDull}%s%-5s%s${cClear}" "[" "${MODE}" "]" + ;; + ERROR) + printf "${cRedBright}%s%-5s%s${cClear}" "[" "${MODE}" "]" + ;; + esac +} + +# Capitalises the first letter of the message +_loggerGetMessage() { + local originalMessage="$*" + local firstChar=$(echo "${originalMessage:0:1}" | awk '{ print toupper($0) }') + local resetOfMessage="${originalMessage:1}" + echo "$firstChar$resetOfMessage" +} + +# The spec also says content should be left-trimmed but this is not necessary in our case. We don't reach the limit. +_loggerGetStackTrace() { + printf "%s%-30s%s" "[" "$1:$2" "]" +} + +_loggerGetThread() { + printf "%s" "[main]" +} + +_loggerGetServiceType() { + printf "%s%-5s%s" "[" "shell" "]" +} + +#Trace ID is not applicable to scripts +_loggerGetTraceID() { + printf "%s" "[]" +} + +logRaw() { + echo "" + printf "$1" + echo "" +} + +logBold(){ + echo "" + printf "${cBold}$1${cClear}" + echo "" +} + +# The date binary works differently based on whether it is GNU/BSD +is_date_supported=0 +date --version > /dev/null 2>&1 || is_date_supported=1 +IS_GNU=$(echo $is_date_supported) + +_loggerGetTimestamp() { + if [ "${IS_GNU}" == "0" ]; then + echo -n $(date -u +%FT%T.%3NZ) + else + echo -n $(date -u +%FT%T.000Z) + fi +} + +# https://www.shellscript.sh/tips/spinner/ +_spin() +{ + spinner="/|\\-/|\\-" + while : + do + for i in `seq 0 7` + do + echo -n "${spinner:$i:1}" + echo -en "\010" + sleep 1 + done + done +} + +showSpinner() { + # Start the Spinner: + _spin & + # Make a note of its Process ID (PID): + SPIN_PID=$! + # Kill the spinner on any signal, including our own exit. + trap "kill -9 $SPIN_PID" `seq 0 15` &> /dev/null || return 0 +} + +stopSpinner() { + local occurrences=$(ps -ef | grep -wc "${SPIN_PID}") + let "occurrences+=0" + # validate that it is present (2 since this search itself will show up in the results) + if [ $occurrences -gt 1 ]; then + kill -9 $SPIN_PID &>/dev/null || return 0 + wait $SPIN_ID &>/dev/null + fi +} + +_getEffectiveMessage(){ + local MESSAGE="$1" + local MODE=${2-"INFO"} + + if [ -z "$CONTEXT" ]; then + CONTEXT=$(caller) + fi + + _EFFECTIVE_MESSAGE= + if [ -z "$LOG_BEHAVIOR_ADD_META" ]; then + _EFFECTIVE_MESSAGE="$(_loggerGetModeRaw $MODE)$(_loggerGetMessage $MESSAGE)" + else + local SERVICE_TYPE="script" + local TRACE_ID="" + local THREAD="main" + + local CONTEXT_LINE=$(echo "$CONTEXT" | awk '{print $1}') + local CONTEXT_FILE=$(echo "$CONTEXT" | awk -F"/" '{print $NF}') + + _EFFECTIVE_MESSAGE="$(_loggerGetTimestamp) $(_loggerGetServiceType) $(_loggerGetMode $MODE) $(_loggerGetTraceID) $(_loggerGetStackTrace $CONTEXT_FILE $CONTEXT_LINE) $(_loggerGetThread) - $(_loggerGetMessage $MESSAGE)" + fi + CONTEXT= +} + +# Important - don't call any log method from this method. Will become an infinite loop. Use echo to debug +_logToFile() { + local MODE=${1-"INFO"} + local targetFile="$LOG_BEHAVIOR_ADD_REDIRECTION" + # IF the file isn't passed, abort + if [ -z "$targetFile" ]; then + return + fi + # IF this is not being run in verbose mode and mode is debug or lower, abort + if [ "${VERBOSE_MODE}" != "$FLAG_Y" ] && [ "${VERBOSE_MODE}" != "true" ] && [ "${VERBOSE_MODE}" != "debug" ]; then + if [ "$MODE" == "DEBUG" ] || [ "$MODE" == "SILLY" ]; then + return + fi + fi + + # Create the file if it doesn't exist + if [ ! -f "${targetFile}" ]; then + return + # touch $targetFile > /dev/null 2>&1 || true + fi + # # Make it readable + # chmod 640 $targetFile > /dev/null 2>&1 || true + + # Log contents + printf "%s\n" "$_EFFECTIVE_MESSAGE" >> "$targetFile" || true +} + +logger() { + if [ "$LOG_BEHAVIOR_ADD_NEW_LINE" == "$FLAG_Y" ]; then + echo "" + fi + _getEffectiveMessage "$@" + local MODE=${2-"INFO"} + printf "%s\n" "$_EFFECTIVE_MESSAGE" + _logToFile "$MODE" +} + +logDebug(){ + VERBOSE_MODE=${VERBOSE_MODE-"false"} + CONTEXT=$(caller) + if [ "${VERBOSE_MODE}" == "$FLAG_Y" ] || [ "${VERBOSE_MODE}" == "true" ] || [ "${VERBOSE_MODE}" == "debug" ];then + logger "$1" "DEBUG" + else + logger "$1" "DEBUG" >&6 + fi + CONTEXT= +} + +logSilly(){ + VERBOSE_MODE=${VERBOSE_MODE-"false"} + CONTEXT=$(caller) + if [ "${VERBOSE_MODE}" == "silly" ];then + logger "$1" "DEBUG" + else + logger "$1" "DEBUG" >&6 + fi + CONTEXT= +} + +logError() { + CONTEXT=$(caller) + logger "$1" "ERROR" + CONTEXT= +} + +errorExit () { + CONTEXT=$(caller) + logger "$1" "ERROR" + CONTEXT= + exit 1 +} + +warn () { + CONTEXT=$(caller) + logger "$1" "WARN" + CONTEXT= +} + +note () { + CONTEXT=$(caller) + logger "$1" "NOTE" + CONTEXT= +} + +bannerStart() { + title=$1 + echo + echo -e "\033[1m${title}\033[0m" + echo +} + +bannerSection() { + title=$1 + echo + echo -e "******************************** ${title} ********************************" + echo +} + +bannerSubSection() { + title=$1 + echo + echo -e "************** ${title} *******************" + echo +} + +bannerMessge() { + title=$1 + echo + echo -e "********************************" + echo -e "${title}" + echo -e "********************************" + echo +} + +setRed () { + local input="$1" + echo -e \\033[31m${input}\\033[0m +} +setGreen () { + local input="$1" + echo -e \\033[32m${input}\\033[0m +} +setYellow () { + local input="$1" + echo -e \\033[33m${input}\\033[0m +} + +logger_addLinebreak () { + echo -e "---\n" +} + +bannerImportant() { + title=$1 + local bold="\033[1m" + local noColour="\033[0m" + echo + echo -e "${bold}######################################## IMPORTANT ########################################${noColour}" + echo -e "${bold}${title}${noColour}" + echo -e "${bold}###########################################################################################${noColour}" + echo +} + +bannerEnd() { + #TODO pass a title and calculate length dynamically so that start and end look alike + echo + echo "*****************************************************************************" + echo +} + +banner() { + title=$1 + content=$2 + bannerStart "${title}" + echo -e "$content" +} + +# The logic below helps us redirect content we'd normally hide to the log file. + # + # We have several commands which clutter the console with output and so use + # `cmd > /dev/null` - this redirects the command's output to null. + # + # However, the information we just hid maybe useful for support. Using the code pattern + # `cmd >&6` (instead of `cmd> >/dev/null` ), the command's output is hidden from the console + # but redirected to the installation log file + # + +#Default value of 6 is just null +exec 6>>/dev/null +redirectLogsToFile() { + echo "" + # local file=$1 + + # [ ! -z "${file}" ] || return 0 + + # local logDir=$(dirname "$file") + + # if [ ! -f "${file}" ]; then + # [ -d "${logDir}" ] || mkdir -p ${logDir} || \ + # ( echo "WARNING : Could not create parent directory (${logDir}) to redirect console log : ${file}" ; return 0 ) + # fi + + # #6 now points to the log file + # exec 6>>${file} + # #reference https://unix.stackexchange.com/questions/145651/using-exec-and-tee-to-redirect-logs-to-stdout-and-a-log-file-in-the-same-time + # exec 2>&1 > >(tee -a "${file}") +} + +# Check if a give key contains any sensitive string as part of it +# Based on the result, the caller can decide its value can be displayed or not +# Sample usage : isKeySensitive "${key}" && displayValue="******" || displayValue=${value} +isKeySensitive(){ + local key=$1 + local sensitiveKeys="password|secret|key|token" + + if [ -z "${key}" ]; then + return 1 + else + local lowercaseKey=$(echo "${key}" | tr '[:upper:]' '[:lower:]' 2>/dev/null) + [[ "${lowercaseKey}" =~ ${sensitiveKeys} ]] && return 0 || return 1 + fi +} + +getPrintableValueOfKey(){ + local displayValue= + local key="$1" + if [ -z "$key" ]; then + # This is actually an incorrect usage of this method but any logging will cause unexpected content in the caller + echo -n "" + return + fi + + local value="$2" + isKeySensitive "${key}" && displayValue="$SENSITIVE_KEY_VALUE" || displayValue="${value}" + echo -n $displayValue +} + +_createConsoleLog(){ + if [ -z "${JF_PRODUCT_HOME}" ]; then + return + fi + local targetFile="${JF_PRODUCT_HOME}/var/log/console.log" + mkdir -p "${JF_PRODUCT_HOME}/var/log" || true + if [ ! -f ${targetFile} ]; then + touch $targetFile > /dev/null 2>&1 || true + fi + chmod 640 $targetFile > /dev/null 2>&1 || true +} + +# Output from application's logs are piped to this method. It checks a configuration variable to determine if content should be logged to +# the common console.log file +redirectServiceLogsToFile() { + + local result="0" + # check if the function getSystemValue exists + LC_ALL=C type getSystemValue > /dev/null 2>&1 || result="$?" + if [[ "$result" != "0" ]]; then + warn "Couldn't find the systemYamlHelper. Skipping log redirection" + return 0 + fi + + getSystemValue "shared.consoleLog" "NOT_SET" + if [[ "${YAML_VALUE}" == "false" ]]; then + logger "Redirection is set to false. Skipping log redirection" + return 0; + fi + + if [ -z "${JF_PRODUCT_HOME}" ] || [ "${JF_PRODUCT_HOME}" == "" ]; then + warn "JF_PRODUCT_HOME is unavailable. Skipping log redirection" + return 0 + fi + + local targetFile="${JF_PRODUCT_HOME}/var/log/console.log" + + _createConsoleLog + + while read -r line; do + printf '%s\n' "${line}" >> $targetFile || return 0 # Don't want to log anything - might clutter the screen + done +} + +## Display environment variables starting with JF_ along with its value +## Value of sensitive keys will be displayed as "******" +## +## Sample Display : +## +## ======================== +## JF Environment variables +## ======================== +## +## JF_SHARED_NODE_ID : locahost +## JF_SHARED_JOINKEY : ****** +## +## +displayEnv() { + local JFEnv=$(printenv | grep ^JF_ 2>/dev/null) + local key= + local value= + + if [ -z "${JFEnv}" ]; then + return + fi + + cat << ENV_START_MESSAGE + +======================== +JF Environment variables +======================== +ENV_START_MESSAGE + + for entry in ${JFEnv}; do + key=$(echo "${entry}" | awk -F'=' '{print $1}') + value=$(echo "${entry}" | awk -F'=' '{print $2}') + + isKeySensitive "${key}" && value="******" || value=${value} + + printf "\n%-35s%s" "${key}" " : ${value}" + done + echo; +} + +_addLogRotateConfiguration() { + logDebug "Method ${FUNCNAME[0]}" + # mandatory inputs + local confFile="$1" + local logFile="$2" + + # Method available in _ioOperations.sh + LC_ALL=C type io_setYQPath > /dev/null 2>&1 || return 1 + + io_setYQPath + + # Method available in _systemYamlHelper.sh + LC_ALL=C type getSystemValue > /dev/null 2>&1 || return 1 + + local frequency="daily" + local archiveFolder="archived" + + local compressLogFiles= + getSystemValue "shared.logging.rotation.compress" "true" + if [[ "${YAML_VALUE}" == "true" ]]; then + compressLogFiles="compress" + fi + + getSystemValue "shared.logging.rotation.maxFiles" "10" + local noOfBackupFiles="${YAML_VALUE}" + + getSystemValue "shared.logging.rotation.maxSizeMb" "25" + local sizeOfFile="${YAML_VALUE}M" + + logDebug "Adding logrotate configuration for [$logFile] to [$confFile]" + + # Add configuration to file + local confContent=$(cat << LOGROTATECONF +$logFile { + $frequency + missingok + rotate $noOfBackupFiles + $compressLogFiles + notifempty + olddir $archiveFolder + dateext + extension .log + dateformat -%Y-%m-%d + size ${sizeOfFile} +} +LOGROTATECONF +) + echo "${confContent}" > ${confFile} || return 1 +} + +_operationIsBySameUser() { + local targetUser="$1" + local currentUserID=$(id -u) + local currentUserName=$(id -un) + + if [ $currentUserID == $targetUser ] || [ $currentUserName == $targetUser ]; then + echo -n "yes" + else + echo -n "no" + fi +} + +_addCronJobForLogrotate() { + logDebug "Method ${FUNCNAME[0]}" + + # Abort if logrotate is not available + [ "$(io_commandExists 'crontab')" != "yes" ] && warn "cron is not available" && return 1 + + # mandatory inputs + local productHome="$1" + local confFile="$2" + local cronJobOwner="$3" + + # We want to use our binary if possible. It may be more recent than the one in the OS + local logrotateBinary="$productHome/app/third-party/logrotate/logrotate" + + if [ ! -f "$logrotateBinary" ]; then + logrotateBinary="logrotate" + [ "$(io_commandExists 'logrotate')" != "yes" ] && warn "logrotate is not available" && return 1 + fi + local cmd="$logrotateBinary ${confFile} --state $productHome/var/etc/logrotate/logrotate-state" #--verbose + + id -u $cronJobOwner > /dev/null 2>&1 || { warn "User $cronJobOwner does not exist. Aborting logrotate configuration" && return 1; } + + # Remove the existing line + removeLogRotation "$productHome" "$cronJobOwner" || true + + # Run logrotate daily at 23:55 hours + local cronInterval="55 23 * * * $cmd" + + local standaloneMode=$(_operationIsBySameUser "$cronJobOwner") + + # If this is standalone mode, we cannot use -u - the user running this process may not have the necessary privileges + if [ "$standaloneMode" == "no" ]; then + (crontab -l -u $cronJobOwner 2>/dev/null; echo "$cronInterval") | crontab -u $cronJobOwner - + else + (crontab -l 2>/dev/null; echo "$cronInterval") | crontab - + fi +} + +## Configure logrotate for a product +## Failure conditions: +## If logrotation could not be setup for some reason +## Parameters: +## $1: The product name +## $2: The product home +## Depends on global: none +## Updates global: none +## Returns: NA + +configureLogRotation() { + logDebug "Method ${FUNCNAME[0]}" + + # mandatory inputs + local productName="$1" + if [ -z $productName ]; then + warn "Incorrect usage. A product name is necessary for configuring log rotation" && return 1 + fi + + local productHome="$2" + if [ -z $productHome ]; then + warn "Incorrect usage. A product home folder is necessary for configuring log rotation" && return 1 + fi + + local logFile="${productHome}/var/log/console.log" + if [[ $(uname) == "Darwin" ]]; then + logger "Log rotation for [$logFile] has not been configured. Please setup manually" + return 0 + fi + + local userID="$3" + if [ -z $userID ]; then + warn "Incorrect usage. A userID is necessary for configuring log rotation" && return 1 + fi + + local groupID=${4:-$userID} + local logConfigOwner=${5:-$userID} + + logDebug "Configuring log rotation as user [$userID], group [$groupID], effective cron User [$logConfigOwner]" + + local errorMessage="Could not configure logrotate. Please configure log rotation of the file: [$logFile] manually" + + local confFile="${productHome}/var/etc/logrotate/logrotate.conf" + + # TODO move to recursive method + createDir "${productHome}" "$userID" "$groupID" || { warn "${errorMessage}" && return 1; } + createDir "${productHome}/var" "$userID" "$groupID" || { warn "${errorMessage}" && return 1; } + createDir "${productHome}/var/log" "$userID" "$groupID" || { warn "${errorMessage}" && return 1; } + createDir "${productHome}/var/log/archived" "$userID" "$groupID" || { warn "${errorMessage}" && return 1; } + + # TODO move to recursive method + createDir "${productHome}/var/etc" "$userID" "$groupID" || { warn "${errorMessage}" && return 1; } + createDir "${productHome}/var/etc/logrotate" "$logConfigOwner" || { warn "${errorMessage}" && return 1; } + + # conf file should be owned by the user running the script + createFile "${confFile}" "${logConfigOwner}" || { warn "Could not create configuration file [$confFile]" return 1; } + + _addLogRotateConfiguration "${confFile}" "${logFile}" "$userID" "$groupID" || { warn "${errorMessage}" && return 1; } + _addCronJobForLogrotate "${productHome}" "${confFile}" "${logConfigOwner}" || { warn "${errorMessage}" && return 1; } +} + +_pauseExecution() { + if [ "${VERBOSE_MODE}" == "debug" ]; then + + local breakPoint="$1" + if [ ! -z "$breakPoint" ]; then + printf "${cBlue}Breakpoint${cClear} [$breakPoint] " + echo "" + fi + printf "${cBlue}Press enter once you are ready to continue${cClear}" + read -s choice + echo "" + fi +} + +# removeLogRotation "$productHome" "$cronJobOwner" || true +removeLogRotation() { + logDebug "Method ${FUNCNAME[0]}" + if [[ $(uname) == "Darwin" ]]; then + logDebug "Not implemented for Darwin." + return 0 + fi + local productHome="$1" + local cronJobOwner="$2" + local standaloneMode=$(_operationIsBySameUser "$cronJobOwner") + + local confFile="${productHome}/var/etc/logrotate/logrotate.conf" + + if [ "$standaloneMode" == "no" ]; then + crontab -l -u $cronJobOwner 2>/dev/null | grep -v "$confFile" | crontab -u $cronJobOwner - + else + crontab -l 2>/dev/null | grep -v "$confFile" | crontab - + fi +} + +# NOTE: This method does not check the configuration to see if redirection is necessary. +# This is intentional. If we don't redirect, tomcat logs might get redirected to a folder/file +# that does not exist, causing the service itself to not start +setupTomcatRedirection() { + logDebug "Method ${FUNCNAME[0]}" + local consoleLog="${JF_PRODUCT_HOME}/var/log/console.log" + _createConsoleLog + export CATALINA_OUT="${consoleLog}" +} + +setupScriptLogsRedirection() { + logDebug "Method ${FUNCNAME[0]}" + if [ -z "${JF_PRODUCT_HOME}" ]; then + logDebug "No JF_PRODUCT_HOME. Returning" + return + fi + # Create the console.log file if it is not already present + # _createConsoleLog || true + # # Ensure any logs (logger/logError/warn) also get redirected to the console.log + # # Using installer.log as a temparory fix. Please change this to console.log once INST-291 is fixed + export LOG_BEHAVIOR_ADD_REDIRECTION="${JF_PRODUCT_HOME}/var/log/console.log" + export LOG_BEHAVIOR_ADD_META="$FLAG_Y" +} + +# Returns Y if this method is run inside a container +isRunningInsideAContainer() { + local check1=$(grep -sq 'docker\|kubepods' /proc/1/cgroup; echo $?) + local check2=$(grep -sq 'containers' /proc/self/mountinfo; echo $?) + if [[ $check1 == 0 || $check2 == 0 || -f "/.dockerenv" ]]; then + echo -n "$FLAG_Y" + else + echo -n "$FLAG_N" + fi +} + +POSTGRES_USER=999 +NGINX_USER=104 +NGINX_GROUP=107 +ES_USER=1000 +REDIS_USER=999 +MONGO_USER=999 +RABBITMQ_USER=999 +LOG_FILE_PERMISSION=640 +PID_FILE_PERMISSION=644 + +# Copy file +copyFile(){ + local source=$1 + local target=$2 + local mode=${3:-overwrite} + local enableVerbose=${4:-"${FLAG_N}"} + local verboseFlag="" + + if [ ! -z "${enableVerbose}" ] && [ "${enableVerbose}" == "${FLAG_Y}" ]; then + verboseFlag="-v" + fi + + if [[ ! ( $source && $target ) ]]; then + warn "Source and target is mandatory to copy file" + return 1 + fi + + if [[ -f "${target}" ]]; then + [[ "$mode" = "overwrite" ]] && ( cp ${verboseFlag} -f "$source" "$target" || errorExit "Unable to copy file, command : cp -f ${source} ${target}") || true + else + cp ${verboseFlag} -f "$source" "$target" || errorExit "Unable to copy file, command : cp -f ${source} ${target}" + fi +} + +# Copy files recursively from given source directory to destination directory +# This method wil copy but will NOT overwrite +# Destination will be created if its not available +copyFilesNoOverwrite(){ + local src=$1 + local dest=$2 + local enableVerboseCopy="${3:-${FLAG_Y}}" + + if [[ -z "${src}" || -z "${dest}" ]]; then + return + fi + + if [ -d "${src}" ] && [ "$(ls -A ${src})" ]; then + local relativeFilePath="" + local targetFilePath="" + + for file in $(find ${src} -type f 2>/dev/null) ; do + # Derive relative path and attach it to destination + # Example : + # src=/extra_config + # dest=/var/opt/jfrog/artifactory/etc + # file=/extra_config/config.xml + # relativeFilePath=config.xml + # targetFilePath=/var/opt/jfrog/artifactory/etc/config.xml + relativeFilePath=${file/${src}/} + targetFilePath=${dest}${relativeFilePath} + + createDir "$(dirname "$targetFilePath")" + copyFile "${file}" "${targetFilePath}" "no_overwrite" "${enableVerboseCopy}" + done + fi +} + +# TODO : WINDOWS ? +# Check the max open files and open processes set on the system +checkULimits () { + local minMaxOpenFiles=${1:-32000} + local minMaxOpenProcesses=${2:-1024} + local setValue=${3:-true} + local warningMsgForFiles=${4} + local warningMsgForProcesses=${5} + + logger "Checking open files and processes limits" + + local currentMaxOpenFiles=$(ulimit -n) + logger "Current max open files is $currentMaxOpenFiles" + if [ ${currentMaxOpenFiles} != "unlimited" ] && [ "$currentMaxOpenFiles" -lt "$minMaxOpenFiles" ]; then + if [ "${setValue}" ]; then + ulimit -n "${minMaxOpenFiles}" >/dev/null 2>&1 || warn "Max number of open files $currentMaxOpenFiles is low!" + [ -z "${warningMsgForFiles}" ] || warn "${warningMsgForFiles}" + else + errorExit "Max number of open files $currentMaxOpenFiles, is too low. Cannot run the application!" + fi + fi + + local currentMaxOpenProcesses=$(ulimit -u) + logger "Current max open processes is $currentMaxOpenProcesses" + if [ "$currentMaxOpenProcesses" != "unlimited" ] && [ "$currentMaxOpenProcesses" -lt "$minMaxOpenProcesses" ]; then + if [ "${setValue}" ]; then + ulimit -u "${minMaxOpenProcesses}" >/dev/null 2>&1 || warn "Max number of open files $currentMaxOpenFiles is low!" + [ -z "${warningMsgForProcesses}" ] || warn "${warningMsgForProcesses}" + else + errorExit "Max number of open files $currentMaxOpenProcesses, is too low. Cannot run the application!" + fi + fi +} + +createDirs() { + local appDataDir=$1 + local serviceName=$2 + local folders="backup bootstrap data etc logs work" + + [ -z "${appDataDir}" ] && errorExit "An application directory is mandatory to create its data structure" || true + [ -z "${serviceName}" ] && errorExit "A service name is mandatory to create service data structure" || true + + for folder in ${folders} + do + folder=${appDataDir}/${folder}/${serviceName} + if [ ! -d "${folder}" ]; then + logger "Creating folder : ${folder}" + mkdir -p "${folder}" || errorExit "Failed to create ${folder}" + fi + done +} + + +testReadWritePermissions () { + local dir_to_check=$1 + local error=false + + [ -d ${dir_to_check} ] || errorExit "'${dir_to_check}' is not a directory" + + local test_file=${dir_to_check}/test-permissions + + # Write file + if echo test > ${test_file} 1> /dev/null 2>&1; then + # Write succeeded. Testing read... + if cat ${test_file} > /dev/null; then + rm -f ${test_file} + else + error=true + fi + else + error=true + fi + + if [ ${error} == true ]; then + return 1 + else + return 0 + fi +} + +# Test directory has read/write permissions for current user +testDirectoryPermissions () { + local dir_to_check=$1 + local error=false + + [ -d ${dir_to_check} ] || errorExit "'${dir_to_check}' is not a directory" + + local u_id=$(id -u) + local id_str="id ${u_id}" + + logger "Testing directory ${dir_to_check} has read/write permissions for user ${id_str}" + + if ! testReadWritePermissions ${dir_to_check}; then + error=true + fi + + if [ "${error}" == true ]; then + local stat_data=$(stat -Lc "Directory: %n, permissions: %a, owner: %U, group: %G" ${dir_to_check}) + logger "###########################################################" + logger "${dir_to_check} DOES NOT have proper permissions for user ${id_str}" + logger "${stat_data}" + logger "Mounted directory must have read/write permissions for user ${id_str}" + logger "###########################################################" + errorExit "Directory ${dir_to_check} has bad permissions for user ${id_str}" + fi + logger "Permissions for ${dir_to_check} are good" +} + +# Utility method to create a directory path recursively with chown feature as +# Failure conditions: +## Exits if unable to create a directory +# Parameters: +## $1: Root directory from where the path can be created +## $2: List of recursive child directories separated by space +## $3: user who should own the directory. Optional +## $4: group who should own the directory. Optional +# Depends on global: none +# Updates global: none +# Returns: NA +# +# Usage: +# createRecursiveDir "/opt/jfrog/product/var" "bootstrap tomcat lib" "user_name" "group_name" +createRecursiveDir(){ + local rootDir=$1 + local pathDirs=$2 + local user=$3 + local group=${4:-${user}} + local fullPath= + + [ ! -z "${rootDir}" ] || return 0 + + createDir "${rootDir}" "${user}" "${group}" + + [ ! -z "${pathDirs}" ] || return 0 + + fullPath=${rootDir} + + for dir in ${pathDirs}; do + fullPath=${fullPath}/${dir} + createDir "${fullPath}" "${user}" "${group}" + done +} + +# Utility method to create a directory +# Failure conditions: +## Exits if unable to create a directory +# Parameters: +## $1: directory to create +## $2: user who should own the directory. Optional +## $3: group who should own the directory. Optional +# Depends on global: none +# Updates global: none +# Returns: NA + +createDir(){ + local dirName="$1" + local printMessage=no + logSilly "Method ${FUNCNAME[0]} invoked with [$dirName]" + [ -z "${dirName}" ] && return + + logDebug "Attempting to create ${dirName}" + mkdir -p "${dirName}" || errorExit "Unable to create directory: [${dirName}]" + local userID="$2" + local groupID=${3:-$userID} + + # If UID/GID is passed, chown the folder + if [ ! -z "$userID" ] && [ ! -z "$groupID" ]; then + # Earlier, this line would have returned 1 if it failed. Now it just warns. + # This is intentional. Earlier, this line would NOT be reached if the folder already existed. + # Since it will always come to this line and the script may be running as a non-root user, this method will just warn if + # setting permissions fails (so as to not affect any existing flows) + io_setOwnershipNonRecursive "$dirName" "$userID" "$groupID" || warn "Could not set owner of [$dirName] to [$userID:$groupID]" + fi + # logging message to print created dir with user and group + local logMessage=${4:-$printMessage} + if [[ "${logMessage}" == "yes" ]]; then + logger "Successfully created directory [${dirName}]. Owner: [${userID}:${groupID}]" + fi +} + +removeSoftLinkAndCreateDir () { + local dirName="$1" + local userID="$2" + local groupID="$3" + local logMessage="$4" + removeSoftLink "${dirName}" + createDir "${dirName}" "${userID}" "${groupID}" "${logMessage}" +} + +# Utility method to remove a soft link +removeSoftLink () { + local dirName="$1" + if [[ -L "${dirName}" ]]; then + targetLink=$(readlink -f "${dirName}") + logger "Removing the symlink [${dirName}] pointing to [${targetLink}]" + rm -f "${dirName}" + fi +} + +# Check Directory exist in the path +checkDirExists () { + local directoryPath="$1" + + [[ -d "${directoryPath}" ]] && echo -n "true" || echo -n "false" +} + + +# Utility method to create a file +# Failure conditions: +# Parameters: +## $1: file to create +# Depends on global: none +# Updates global: none +# Returns: NA + +createFile(){ + local fileName="$1" + logSilly "Method ${FUNCNAME[0]} [$fileName]" + [ -f "${fileName}" ] && return 0 + touch "${fileName}" || return 1 + + local userID="$2" + local groupID=${3:-$userID} + + # If UID/GID is passed, chown the folder + if [ ! -z "$userID" ] && [ ! -z "$groupID" ]; then + io_setOwnership "$fileName" "$userID" "$groupID" || return 1 + fi +} + +# Check File exist in the filePath +# IMPORTANT- DON'T ADD LOGGING to this method +checkFileExists () { + local filePath="$1" + + [[ -f "${filePath}" ]] && echo -n "true" || echo -n "false" +} + +# Check for directories contains any (files or sub directories) +# IMPORTANT- DON'T ADD LOGGING to this method +checkDirContents () { + local directoryPath="$1" + if [[ "$(ls -1 "${directoryPath}" | wc -l)" -gt 0 ]]; then + echo -n "true" + else + echo -n "false" + fi +} + +# Check contents exist in directory +# IMPORTANT- DON'T ADD LOGGING to this method +checkContentExists () { + local source="$1" + + if [[ "$(checkDirContents "${source}")" != "true" ]]; then + echo -n "false" + else + echo -n "true" + fi +} + +# Resolve the variable +# IMPORTANT- DON'T ADD LOGGING to this method +evalVariable () { + local output="$1" + local input="$2" + + eval "${output}"=\${"${input}"} + eval echo \${"${output}"} +} + +# Usage: if [ "$(io_commandExists 'curl')" == "yes" ] +# IMPORTANT- DON'T ADD LOGGING to this method +io_commandExists() { + local commandToExecute="$1" + hash "${commandToExecute}" 2>/dev/null + local rt=$? + if [ "$rt" == 0 ]; then echo -n "yes"; else echo -n "no"; fi +} + +# Usage: if [ "$(io_curlExists)" != "yes" ] +# IMPORTANT- DON'T ADD LOGGING to this method +io_curlExists() { + io_commandExists "curl" +} + + +io_hasMatch() { + logSilly "Method ${FUNCNAME[0]}" + local result=0 + logDebug "Executing [echo \"$1\" | grep \"$2\" >/dev/null 2>&1]" + echo "$1" | grep "$2" >/dev/null 2>&1 || result=1 + return $result +} + +# Utility method to check if the string passed (usually a connection url) corresponds to this machine itself +# Failure conditions: None +# Parameters: +## $1: string to check against +# Depends on global: none +# Updates global: IS_LOCALHOST with value "yes/no" +# Returns: NA + +io_getIsLocalhost() { + logSilly "Method ${FUNCNAME[0]}" + IS_LOCALHOST="$FLAG_N" + local inputString="$1" + logDebug "Parsing [$inputString] to check if we are dealing with this machine itself" + + io_hasMatch "$inputString" "localhost" && { + logDebug "Found localhost. Returning [$FLAG_Y]" + IS_LOCALHOST="$FLAG_Y" && return; + } || logDebug "Did not find match for localhost" + + local hostIP=$(io_getPublicHostIP) + io_hasMatch "$inputString" "$hostIP" && { + logDebug "Found $hostIP. Returning [$FLAG_Y]" + IS_LOCALHOST="$FLAG_Y" && return; + } || logDebug "Did not find match for $hostIP" + + local hostID=$(io_getPublicHostID) + io_hasMatch "$inputString" "$hostID" && { + logDebug "Found $hostID. Returning [$FLAG_Y]" + IS_LOCALHOST="$FLAG_Y" && return; + } || logDebug "Did not find match for $hostID" + + local hostName=$(io_getPublicHostName) + io_hasMatch "$inputString" "$hostName" && { + logDebug "Found $hostName. Returning [$FLAG_Y]" + IS_LOCALHOST="$FLAG_Y" && return; + } || logDebug "Did not find match for $hostName" + +} + +# Usage: if [ "$(io_tarExists)" != "yes" ] +# IMPORTANT- DON'T ADD LOGGING to this method +io_tarExists() { + io_commandExists "tar" +} + +# IMPORTANT- DON'T ADD LOGGING to this method +io_getPublicHostIP() { + local OS_TYPE=$(uname) + local publicHostIP= + if [ "${OS_TYPE}" == "Darwin" ]; then + ipStatus=$(ifconfig en0 | grep "status" | awk '{print$2}') + if [ "${ipStatus}" == "active" ]; then + publicHostIP=$(ifconfig en0 | grep inet | grep -v inet6 | awk '{print $2}') + else + errorExit "Host IP could not be resolved!" + fi + elif [ "${OS_TYPE}" == "Linux" ]; then + publicHostIP=$(hostname -i 2>/dev/null || echo "127.0.0.1") + fi + publicHostIP=$(echo "${publicHostIP}" | awk '{print $1}') + echo -n "${publicHostIP}" +} + +# Will return the short host name (up to the first dot) +# IMPORTANT- DON'T ADD LOGGING to this method +io_getPublicHostName() { + echo -n "$(hostname -s)" +} + +# Will return the full host name (use this as much as possible) +# IMPORTANT- DON'T ADD LOGGING to this method +io_getPublicHostID() { + echo -n "$(hostname)" +} + +# Utility method to backup a file +# Failure conditions: NA +# Parameters: filePath +# Depends on global: none, +# Updates global: none +# Returns: NA +io_backupFile() { + logSilly "Method ${FUNCNAME[0]}" + fileName="$1" + if [ ! -f "${filePath}" ]; then + logDebug "No file: [${filePath}] to backup" + return + fi + dateTime=$(date +"%Y-%m-%d-%H-%M-%S") + targetFileName="${fileName}.backup.${dateTime}" + yes | \cp -f "$fileName" "${targetFileName}" + logger "File [${fileName}] backedup as [${targetFileName}]" +} + +# Reference https://stackoverflow.com/questions/4023830/how-to-compare-two-strings-in-dot-separated-version-format-in-bash/4025065#4025065 +is_number() { + case "$BASH_VERSION" in + 3.1.*) + PATTERN='\^\[0-9\]+\$' + ;; + *) + PATTERN='^[0-9]+$' + ;; + esac + + [[ "$1" =~ $PATTERN ]] +} + +io_compareVersions() { + if [[ $# != 2 ]] + then + echo "Usage: min_version current minimum" + return + fi + + A="${1%%.*}" + B="${2%%.*}" + + if [[ "$A" != "$1" && "$B" != "$2" && "$A" == "$B" ]] + then + io_compareVersions "${1#*.}" "${2#*.}" + else + if is_number "$A" && is_number "$B" + then + if [[ "$A" -eq "$B" ]]; then + echo "0" + elif [[ "$A" -gt "$B" ]]; then + echo "1" + elif [[ "$A" -lt "$B" ]]; then + echo "-1" + fi + fi + fi +} + +# Reference https://stackoverflow.com/questions/369758/how-to-trim-whitespace-from-a-bash-variable +# Strip all leading and trailing spaces +# IMPORTANT- DON'T ADD LOGGING to this method +io_trim() { + local var="$1" + # remove leading whitespace characters + var="${var#"${var%%[![:space:]]*}"}" + # remove trailing whitespace characters + var="${var%"${var##*[![:space:]]}"}" + echo -n "$var" +} + +# temporary function will be removing it ASAP +# search for string and replace text in file +replaceText_migration_hook () { + local regexString="$1" + local replaceText="$2" + local file="$3" + + if [[ "$(checkFileExists "${file}")" != "true" ]]; then + return + fi + if [[ $(uname) == "Darwin" ]]; then + sed -i '' -e "s/${regexString}/${replaceText}/" "${file}" || warn "Failed to replace the text in ${file}" + else + sed -i -e "s/${regexString}/${replaceText}/" "${file}" || warn "Failed to replace the text in ${file}" + fi +} + +# search for string and replace text in file +replaceText () { + local regexString="$1" + local replaceText="$2" + local file="$3" + + if [[ "$(checkFileExists "${file}")" != "true" ]]; then + return + fi + if [[ $(uname) == "Darwin" ]]; then + sed -i '' -e "s#${regexString}#${replaceText}#" "${file}" || warn "Failed to replace the text in ${file}" + else + sed -i -e "s#${regexString}#${replaceText}#" "${file}" || warn "Failed to replace the text in ${file}" + logDebug "Replaced [$regexString] with [$replaceText] in [$file]" + fi +} + +# search for string and prepend text in file +prependText () { + local regexString="$1" + local text="$2" + local file="$3" + + if [[ "$(checkFileExists "${file}")" != "true" ]]; then + return + fi + if [[ $(uname) == "Darwin" ]]; then + sed -i '' -e '/'"${regexString}"'/i\'$'\n\\'"${text}"''$'\n' "${file}" || warn "Failed to prepend the text in ${file}" + else + sed -i -e '/'"${regexString}"'/i\'$'\n\\'"${text}"''$'\n' "${file}" || warn "Failed to prepend the text in ${file}" + fi +} + +# add text to beginning of the file +addText () { + local text="$1" + local file="$2" + + if [[ "$(checkFileExists "${file}")" != "true" ]]; then + return + fi + if [[ $(uname) == "Darwin" ]]; then + sed -i '' -e '1s/^/'"${text}"'\'$'\n/' "${file}" || warn "Failed to add the text in ${file}" + else + sed -i -e '1s/^/'"${text}"'\'$'\n/' "${file}" || warn "Failed to add the text in ${file}" + fi +} + +io_replaceString () { + local value="$1" + local firstString="$2" + local secondString="$3" + local separator=${4:-"/"} + local updateValue= + if [[ $(uname) == "Darwin" ]]; then + updateValue=$(echo "${value}" | sed "s${separator}${firstString}${separator}${secondString}${separator}") + else + updateValue=$(echo "${value}" | sed "s${separator}${firstString}${separator}${secondString}${separator}") + fi + echo -n "${updateValue}" +} + +_findYQ() { + # logSilly "Method ${FUNCNAME[0]}" (Intentionally not logging. Does not add value) + local parentDir="$1" + if [ -z "$parentDir" ]; then + return + fi + logDebug "Executing command [find "${parentDir}" -name third-party -type d]" + local yq=$(find "${parentDir}" -name third-party -type d) + if [ -d "${yq}/yq" ]; then + export YQ_PATH="${yq}/yq" + fi +} + + +io_setYQPath() { + # logSilly "Method ${FUNCNAME[0]}" (Intentionally not logging. Does not add value) + if [ "$(io_commandExists 'yq')" == "yes" ]; then + return + fi + + if [ ! -z "${JF_PRODUCT_HOME}" ] && [ -d "${JF_PRODUCT_HOME}" ]; then + _findYQ "${JF_PRODUCT_HOME}" + fi + + if [ -z "${YQ_PATH}" ] && [ ! -z "${COMPOSE_HOME}" ] && [ -d "${COMPOSE_HOME}" ]; then + _findYQ "${COMPOSE_HOME}" + fi + # TODO We can remove this block after all the code is restructured. + if [ -z "${YQ_PATH}" ] && [ ! -z "${SCRIPT_HOME}" ] && [ -d "${SCRIPT_HOME}" ]; then + _findYQ "${SCRIPT_HOME}" + fi + +} + +io_getLinuxDistribution() { + LINUX_DISTRIBUTION= + + # Make sure running on Linux + [ $(uname -s) != "Linux" ] && return + + # Find out what Linux distribution we are on + + cat /etc/*-release | grep -i Red >/dev/null 2>&1 && LINUX_DISTRIBUTION=RedHat || true + + # OS 6.x + cat /etc/issue.net | grep Red >/dev/null 2>&1 && LINUX_DISTRIBUTION=RedHat || true + + # OS 7.x + cat /etc/*-release | grep -i centos >/dev/null 2>&1 && LINUX_DISTRIBUTION=CentOS && LINUX_DISTRIBUTION_VER="7" || true + + # OS 8.x + grep -q -i "release 8" /etc/redhat-release >/dev/null 2>&1 && LINUX_DISTRIBUTION_VER="8" || true + + # OS 7.x + grep -q -i "release 7" /etc/redhat-release >/dev/null 2>&1 && LINUX_DISTRIBUTION_VER="7" || true + + # OS 6.x + grep -q -i "release 6" /etc/redhat-release >/dev/null 2>&1 && LINUX_DISTRIBUTION_VER="6" || true + + cat /etc/*-release | grep -i Red | grep -i 'VERSION=7' >/dev/null 2>&1 && LINUX_DISTRIBUTION=RedHat && LINUX_DISTRIBUTION_VER="7" || true + + cat /etc/*-release | grep -i debian >/dev/null 2>&1 && LINUX_DISTRIBUTION=Debian || true + + cat /etc/*-release | grep -i ubuntu >/dev/null 2>&1 && LINUX_DISTRIBUTION=Ubuntu || true +} + +## Utility method to check ownership of folders/files +## Failure conditions: + ## If invoked with incorrect inputs - FATAL + ## If file is not owned by the user & group +## Parameters: + ## user + ## group + ## folder to chown +## Globals: none +## Returns: none +## NOTE: The method does NOTHING if the OS is Mac +io_checkOwner () { + logSilly "Method ${FUNCNAME[0]}" + local osType=$(uname) + + if [ "${osType}" != "Linux" ]; then + logDebug "Unsupported OS. Skipping check" + return 0 + fi + + local file_to_check=$1 + local user_id_to_check=$2 + + + if [ -z "$user_id_to_check" ] || [ -z "$file_to_check" ]; then + errorExit "Invalid invocation of method. Missing mandatory inputs" + fi + + local group_id_to_check=${3:-$user_id_to_check} + local check_user_name=${4:-"no"} + + logDebug "Checking permissions on [$file_to_check] for user [$user_id_to_check] & group [$group_id_to_check]" + + local stat= + + if [ "${check_user_name}" == "yes" ]; then + stat=( $(stat -Lc "%U %G" ${file_to_check}) ) + else + stat=( $(stat -Lc "%u %g" ${file_to_check}) ) + fi + + local user_id=${stat[0]} + local group_id=${stat[1]} + + if [[ "${user_id}" != "${user_id_to_check}" ]] || [[ "${group_id}" != "${group_id_to_check}" ]] ; then + logDebug "Ownership mismatch. [${file_to_check}] is not owned by [${user_id_to_check}:${group_id_to_check}]" + return 1 + else + return 0 + fi +} + +## Utility method to change ownership of a file/folder - NON recursive +## Failure conditions: + ## If invoked with incorrect inputs - FATAL + ## If chown operation fails - returns 1 +## Parameters: + ## user + ## group + ## file to chown +## Globals: none +## Returns: none +## NOTE: The method does NOTHING if the OS is Mac + +io_setOwnershipNonRecursive() { + + local osType=$(uname) + if [ "${osType}" != "Linux" ]; then + return + fi + + local targetFile=$1 + local user=$2 + + if [ -z "$user" ] || [ -z "$targetFile" ]; then + errorExit "Invalid invocation of method. Missing mandatory inputs" + fi + + local group=${3:-$user} + logDebug "Method ${FUNCNAME[0]}. Executing [chown ${user}:${group} ${targetFile}]" + chown ${user}:${group} ${targetFile} || return 1 +} + +## Utility method to change ownership of a file. +## IMPORTANT +## If being called on a folder, should ONLY be called for fresh folders or may cause performance issues +## Failure conditions: + ## If invoked with incorrect inputs - FATAL + ## If chown operation fails - returns 1 +## Parameters: + ## user + ## group + ## file to chown +## Globals: none +## Returns: none +## NOTE: The method does NOTHING if the OS is Mac + +io_setOwnership() { + + local osType=$(uname) + if [ "${osType}" != "Linux" ]; then + return + fi + + local targetFile=$1 + local user=$2 + + if [ -z "$user" ] || [ -z "$targetFile" ]; then + errorExit "Invalid invocation of method. Missing mandatory inputs" + fi + + local group=${3:-$user} + logDebug "Method ${FUNCNAME[0]}. Executing [chown -R ${user}:${group} ${targetFile}]" + chown -R ${user}:${group} ${targetFile} || return 1 +} + +## Utility method to create third party folder structure necessary for Postgres +## Failure conditions: +## If creation of directory or assigning permissions fails +## Parameters: none +## Globals: +## POSTGRESQL_DATA_ROOT +## Returns: none +## NOTE: The method does NOTHING if the folder already exists +io_createPostgresDir() { + logDebug "Method ${FUNCNAME[0]}" + [ -z "${POSTGRESQL_DATA_ROOT}" ] && return 0 + + logDebug "Property [${POSTGRESQL_DATA_ROOT}] exists. Proceeding" + + createDir "${POSTGRESQL_DATA_ROOT}/data" + io_setOwnership "${POSTGRESQL_DATA_ROOT}" "${POSTGRES_USER}" "${POSTGRES_USER}" || errorExit "Setting ownership of [${POSTGRESQL_DATA_ROOT}] to [${POSTGRES_USER}:${POSTGRES_USER}] failed" +} + +## Utility method to create third party folder structure necessary for Nginx +## Failure conditions: +## If creation of directory or assigning permissions fails +## Parameters: none +## Globals: +## NGINX_DATA_ROOT +## Returns: none +## NOTE: The method does NOTHING if the folder already exists +io_createNginxDir() { + logDebug "Method ${FUNCNAME[0]}" + [ -z "${NGINX_DATA_ROOT}" ] && return 0 + + logDebug "Property [${NGINX_DATA_ROOT}] exists. Proceeding" + + createDir "${NGINX_DATA_ROOT}" + io_setOwnership "${NGINX_DATA_ROOT}" "${NGINX_USER}" "${NGINX_GROUP}" || errorExit "Setting ownership of [${NGINX_DATA_ROOT}] to [${NGINX_USER}:${NGINX_GROUP}] failed" +} + +## Utility method to create third party folder structure necessary for ElasticSearch +## Failure conditions: +## If creation of directory or assigning permissions fails +## Parameters: none +## Globals: +## ELASTIC_DATA_ROOT +## Returns: none +## NOTE: The method does NOTHING if the folder already exists +io_createElasticSearchDir() { + logDebug "Method ${FUNCNAME[0]}" + [ -z "${ELASTIC_DATA_ROOT}" ] && return 0 + + logDebug "Property [${ELASTIC_DATA_ROOT}] exists. Proceeding" + + createDir "${ELASTIC_DATA_ROOT}/data" + io_setOwnership "${ELASTIC_DATA_ROOT}" "${ES_USER}" "${ES_USER}" || errorExit "Setting ownership of [${ELASTIC_DATA_ROOT}] to [${ES_USER}:${ES_USER}] failed" +} + +## Utility method to create third party folder structure necessary for Redis +## Failure conditions: +## If creation of directory or assigning permissions fails +## Parameters: none +## Globals: +## REDIS_DATA_ROOT +## Returns: none +## NOTE: The method does NOTHING if the folder already exists +io_createRedisDir() { + logDebug "Method ${FUNCNAME[0]}" + [ -z "${REDIS_DATA_ROOT}" ] && return 0 + + logDebug "Property [${REDIS_DATA_ROOT}] exists. Proceeding" + + createDir "${REDIS_DATA_ROOT}" + io_setOwnership "${REDIS_DATA_ROOT}" "${REDIS_USER}" "${REDIS_USER}" || errorExit "Setting ownership of [${REDIS_DATA_ROOT}] to [${REDIS_USER}:${REDIS_USER}] failed" +} + +## Utility method to create third party folder structure necessary for Mongo +## Failure conditions: +## If creation of directory or assigning permissions fails +## Parameters: none +## Globals: +## MONGODB_DATA_ROOT +## Returns: none +## NOTE: The method does NOTHING if the folder already exists +io_createMongoDir() { + logDebug "Method ${FUNCNAME[0]}" + [ -z "${MONGODB_DATA_ROOT}" ] && return 0 + + logDebug "Property [${MONGODB_DATA_ROOT}] exists. Proceeding" + + createDir "${MONGODB_DATA_ROOT}/logs" + createDir "${MONGODB_DATA_ROOT}/configdb" + createDir "${MONGODB_DATA_ROOT}/db" + io_setOwnership "${MONGODB_DATA_ROOT}" "${MONGO_USER}" "${MONGO_USER}" || errorExit "Setting ownership of [${MONGODB_DATA_ROOT}] to [${MONGO_USER}:${MONGO_USER}] failed" +} + +## Utility method to create third party folder structure necessary for RabbitMQ +## Failure conditions: +## If creation of directory or assigning permissions fails +## Parameters: none +## Globals: +## RABBITMQ_DATA_ROOT +## Returns: none +## NOTE: The method does NOTHING if the folder already exists +io_createRabbitMQDir() { + logDebug "Method ${FUNCNAME[0]}" + [ -z "${RABBITMQ_DATA_ROOT}" ] && return 0 + + logDebug "Property [${RABBITMQ_DATA_ROOT}] exists. Proceeding" + + createDir "${RABBITMQ_DATA_ROOT}" + io_setOwnership "${RABBITMQ_DATA_ROOT}" "${RABBITMQ_USER}" "${RABBITMQ_USER}" || errorExit "Setting ownership of [${RABBITMQ_DATA_ROOT}] to [${RABBITMQ_USER}:${RABBITMQ_USER}] failed" +} + +# Add or replace a property in provided properties file +addOrReplaceProperty() { + local propertyName=$1 + local propertyValue=$2 + local propertiesPath=$3 + local delimiter=${4:-"="} + + # Return if any of the inputs are empty + [[ -z "$propertyName" || "$propertyName" == "" ]] && return + [[ -z "$propertyValue" || "$propertyValue" == "" ]] && return + [[ -z "$propertiesPath" || "$propertiesPath" == "" ]] && return + + grep "^${propertyName}\s*${delimiter}.*$" ${propertiesPath} > /dev/null 2>&1 + [ $? -ne 0 ] && echo -e "\n${propertyName}${delimiter}${propertyValue}" >> ${propertiesPath} + sed -i -e "s|^${propertyName}\s*${delimiter}.*$|${propertyName}${delimiter}${propertyValue}|g;" ${propertiesPath} +} + +# Set property only if its not set +io_setPropertyNoOverride(){ + local propertyName=$1 + local propertyValue=$2 + local propertiesPath=$3 + + # Return if any of the inputs are empty + [[ -z "$propertyName" || "$propertyName" == "" ]] && return + [[ -z "$propertyValue" || "$propertyValue" == "" ]] && return + [[ -z "$propertiesPath" || "$propertiesPath" == "" ]] && return + + grep "^${propertyName}:" ${propertiesPath} > /dev/null 2>&1 + if [ $? -ne 0 ]; then + echo -e "${propertyName}: ${propertyValue}" >> ${propertiesPath} || warn "Setting property ${propertyName}: ${propertyValue} in [ ${propertiesPath} ] failed" + else + logger "Skipping update of property : ${propertyName}" >&6 + fi +} + +# Add a line to a file if it doesn't already exist +addLine() { + local line_to_add=$1 + local target_file=$2 + logger "Trying to add line $1 to $2" >&6 2>&1 + cat "$target_file" | grep -F "$line_to_add" -wq >&6 2>&1 + if [ $? != 0 ]; then + logger "Line does not exist and will be added" >&6 2>&1 + echo $line_to_add >> $target_file || errorExit "Could not update $target_file" + fi +} + +# Utility method to check if a value (first parameter) exists in an array (2nd parameter) +# 1st parameter "value to find" +# 2nd parameter "The array to search in. Please pass a string with each value separated by space" +# Example: containsElement "y" "y Y n N" +containsElement () { + local searchElement=$1 + local searchArray=($2) + local found=1 + for elementInIndex in "${searchArray[@]}";do + if [[ $elementInIndex == $searchElement ]]; then + found=0 + fi + done + return $found +} + +# Utility method to get user's choice +# 1st parameter "what to ask the user" +# 2nd parameter "what choices to accept, separated by spaces" +# 3rd parameter "what is the default choice (to use if the user simply presses Enter)" +# Example 'getUserChoice "Are you feeling lucky? Punk!" "y n Y N" "y"' +getUserChoice(){ + configureLogOutput + read_timeout=${read_timeout:-0.5} + local choice="na" + local text_to_display=$1 + local choices=$2 + local default_choice=$3 + users_choice= + + until containsElement "$choice" "$choices"; do + echo "";echo ""; + sleep $read_timeout #This ensures correct placement of the question. + read -p "$text_to_display :" choice + : ${choice:=$default_choice} + done + users_choice=$choice + echo -e "\n$text_to_display: $users_choice" >&6 + sleep $read_timeout #This ensures correct logging +} + +setFilePermission () { + local permission=$1 + local file=$2 + chmod "${permission}" "${file}" || warn "Setting permission ${permission} to file [ ${file} ] failed" +} + + +#setting required paths +setAppDir (){ + SCRIPT_DIR=$(dirname $0) + SCRIPT_HOME="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" + APP_DIR="`cd "${SCRIPT_HOME}";pwd`" +} + +ZIP_TYPE="zip" +COMPOSE_TYPE="compose" +HELM_TYPE="helm" +RPM_TYPE="rpm" +DEB_TYPE="debian" + +sourceScript () { + local file="$1" + + [ ! -z "${file}" ] || errorExit "target file is not passed to source a file" + + if [ ! -f "${file}" ]; then + errorExit "${file} file is not found" + else + source "${file}" || errorExit "Unable to source ${file}, please check if the user ${USER} has permissions to perform this action" + fi +} +# Source required helpers +initHelpers () { + local systemYamlHelper="${APP_DIR}/systemYamlHelper.sh" + local thirdPartyDir=$(find ${APP_DIR}/.. -name third-party -type d) + export YQ_PATH="${thirdPartyDir}/yq" + LIBXML2_PATH="${thirdPartyDir}/libxml2/bin/xmllint" + export LD_LIBRARY_PATH="${thirdPartyDir}/libxml2/lib" + sourceScript "${systemYamlHelper}" +} +# Check migration info yaml file available in the path +checkMigrationInfoYaml () { + + if [[ -f "${APP_DIR}/migrationHelmInfo.yaml" ]]; then + MIGRATION_SYSTEM_YAML_INFO="${APP_DIR}/migrationHelmInfo.yaml" + INSTALLER="${HELM_TYPE}" + elif [[ -f "${APP_DIR}/migrationZipInfo.yaml" ]]; then + MIGRATION_SYSTEM_YAML_INFO="${APP_DIR}/migrationZipInfo.yaml" + INSTALLER="${ZIP_TYPE}" + elif [[ -f "${APP_DIR}/migrationRpmInfo.yaml" ]]; then + MIGRATION_SYSTEM_YAML_INFO="${APP_DIR}/migrationRpmInfo.yaml" + INSTALLER="${RPM_TYPE}" + elif [[ -f "${APP_DIR}/migrationDebInfo.yaml" ]]; then + MIGRATION_SYSTEM_YAML_INFO="${APP_DIR}/migrationDebInfo.yaml" + INSTALLER="${DEB_TYPE}" + elif [[ -f "${APP_DIR}/migrationComposeInfo.yaml" ]]; then + MIGRATION_SYSTEM_YAML_INFO="${APP_DIR}/migrationComposeInfo.yaml" + INSTALLER="${COMPOSE_TYPE}" + else + errorExit "File migration Info yaml does not exist in [${APP_DIR}]" + fi +} + +retrieveYamlValue () { + local yamlPath="$1" + local value="$2" + local output="$3" + local message="$4" + + [[ -z "${yamlPath}" ]] && errorExit "yamlPath is mandatory to get value from ${MIGRATION_SYSTEM_YAML_INFO}" + + getYamlValue "${yamlPath}" "${MIGRATION_SYSTEM_YAML_INFO}" "false" + value="${YAML_VALUE}" + if [[ -z "${value}" ]]; then + if [[ "${output}" == "Warning" ]]; then + warn "Empty value for ${yamlPath} in [${MIGRATION_SYSTEM_YAML_INFO}]" + elif [[ "${output}" == "Skip" ]]; then + return + else + errorExit "${message}" + fi + fi +} + +checkEnv () { + + if [[ "${INSTALLER}" == "${ZIP_TYPE}" ]]; then + # check Environment JF_PRODUCT_HOME is set before migration + NEW_DATA_DIR="$(evalVariable "NEW_DATA_DIR" "JF_PRODUCT_HOME")" + if [[ -z "${NEW_DATA_DIR}" ]]; then + errorExit "Environment variable JF_PRODUCT_HOME is not set, this is required to perform Migration" + fi + # appending var directory to $JF_PRODUCT_HOME + NEW_DATA_DIR="${NEW_DATA_DIR}/var" + elif [[ "${INSTALLER}" == "${HELM_TYPE}" ]]; then + getCustomDataDir_hook + NEW_DATA_DIR="${OLD_DATA_DIR}" + if [[ -z "${NEW_DATA_DIR}" ]] && [[ -z "${OLD_DATA_DIR}" ]]; then + errorExit "Could not find ${PROMPT_DATA_DIR_LOCATION} to perform Migration" + fi + else + # check Environment JF_ROOT_DATA_DIR is set before migration + OLD_DATA_DIR="$(evalVariable "OLD_DATA_DIR" "JF_ROOT_DATA_DIR")" + # check Environment JF_ROOT_DATA_DIR is set before migration + NEW_DATA_DIR="$(evalVariable "NEW_DATA_DIR" "JF_ROOT_DATA_DIR")" + if [[ -z "${NEW_DATA_DIR}" ]] && [[ -z "${OLD_DATA_DIR}" ]]; then + errorExit "Could not find ${PROMPT_DATA_DIR_LOCATION} to perform Migration" + fi + # appending var directory to $JF_PRODUCT_HOME + NEW_DATA_DIR="${NEW_DATA_DIR}/var" + fi + +} + +getDataDir () { + + if [[ "${INSTALLER}" == "${ZIP_TYPE}" || "${INSTALLER}" == "${COMPOSE_TYPE}"|| "${INSTALLER}" == "${HELM_TYPE}" ]]; then + checkEnv + else + getCustomDataDir_hook + NEW_DATA_DIR="`cd "${APP_DIR}"/../../;pwd`" + NEW_DATA_DIR="${NEW_DATA_DIR}/var" + fi +} + +# Retrieve Product name from MIGRATION_SYSTEM_YAML_INFO +getProduct () { + retrieveYamlValue "migration.product" "${YAML_VALUE}" "Fail" "Empty value under ${yamlPath} in [${MIGRATION_SYSTEM_YAML_INFO}]" + PRODUCT="${YAML_VALUE}" + PRODUCT=$(echo "${PRODUCT}" | tr '[:upper:]' '[:lower:]' 2>/dev/null) + if [[ "${PRODUCT}" != "artifactory" && "${PRODUCT}" != "distribution" && "${PRODUCT}" != "xray" ]]; then + errorExit "migration.product in [${MIGRATION_SYSTEM_YAML_INFO}] is not correct, please set based on product as ARTIFACTORY or DISTRIBUTION" + fi + if [[ "${INSTALLER}" == "${HELM_TYPE}" ]]; then + JF_USER="${PRODUCT}" + fi +} +# Compare product version with minProductVersion and maxProductVersion +migrateCheckVersion () { + local productVersion="$1" + local minProductVersion="$2" + local maxProductVersion="$3" + local productVersion618="6.18.0" + local unSupportedProductVersions7=("7.2.0 7.2.1") + + if [[ "$(io_compareVersions "${productVersion}" "${maxProductVersion}")" -eq 0 || "$(io_compareVersions "${productVersion}" "${maxProductVersion}")" -eq 1 ]]; then + logger "Migration not necessary. ${PRODUCT} is already ${productVersion}" + exit 11 + elif [[ "$(io_compareVersions "${productVersion}" "${minProductVersion}")" -eq 0 || "$(io_compareVersions "${productVersion}" "${minProductVersion}")" -eq 1 ]]; then + if [[ ("$(io_compareVersions "${productVersion}" "${productVersion618}")" -eq 0 || "$(io_compareVersions "${productVersion}" "${productVersion618}")" -eq 1) && " ${unSupportedProductVersions7[@]} " =~ " ${CURRENT_VERSION} " ]]; then + touch /tmp/error; + errorExit "Current ${PRODUCT} version (${productVersion}) does not support migration to ${CURRENT_VERSION}" + else + bannerStart "Detected ${PRODUCT} ${productVersion}, initiating migration" + fi + else + logger "Current ${PRODUCT} ${productVersion} version is not supported for migration" + exit 1 + fi +} + +getProductVersion () { + local minProductVersion="$1" + local maxProductVersion="$2" + local newfilePath="$3" + local oldfilePath="$4" + local propertyInDocker="$5" + local property="$6" + local productVersion= + local status= + + if [[ "$INSTALLER" == "${COMPOSE_TYPE}" ]]; then + if [[ -f "${oldfilePath}" ]]; then + if [[ "${PRODUCT}" == "artifactory" ]]; then + productVersion="$(readKey "${property}" "${oldfilePath}")" + else + productVersion="$(cat "${oldfilePath}")" + fi + status="success" + elif [[ -f "${newfilePath}" ]]; then + productVersion="$(readKey "${propertyInDocker}" "${newfilePath}")" + status="fail" + else + logger "File [${oldfilePath}] or [${newfilePath}] not found to get current version." + exit 0 + fi + elif [[ "$INSTALLER" == "${HELM_TYPE}" ]]; then + if [[ -f "${oldfilePath}" ]]; then + if [[ "${PRODUCT}" == "artifactory" ]]; then + productVersion="$(readKey "${property}" "${oldfilePath}")" + else + productVersion="$(cat "${oldfilePath}")" + fi + status="success" + else + productVersion="${CURRENT_VERSION}" + [[ -z "${productVersion}" || "${productVersion}" == "" ]] && logger "${PRODUCT} CURRENT_VERSION is not set" && exit 0 + fi + else + if [[ -f "${newfilePath}" ]]; then + productVersion="$(readKey "${property}" "${newfilePath}")" + status="fail" + elif [[ -f "${oldfilePath}" ]]; then + productVersion="$(readKey "${property}" "${oldfilePath}")" + status="success" + else + if [[ "${INSTALLER}" == "${ZIP_TYPE}" ]]; then + logger "File [${newfilePath}] not found to get current version." + else + logger "File [${oldfilePath}] or [${newfilePath}] not found to get current version." + fi + exit 0 + fi + fi + if [[ -z "${productVersion}" || "${productVersion}" == "" ]]; then + [[ "${status}" == "success" ]] && logger "No version found in file [${oldfilePath}]." + [[ "${status}" == "fail" ]] && logger "No version found in file [${newfilePath}]." + exit 0 + fi + + migrateCheckVersion "${productVersion}" "${minProductVersion}" "${maxProductVersion}" +} + +readKey () { + local property="$1" + local file="$2" + local version= + + while IFS='=' read -r key value || [ -n "${key}" ]; + do + [[ ! "${key}" =~ \#.* && ! -z "${key}" && ! -z "${value}" ]] + key="$(io_trim "${key}")" + if [[ "${key}" == "${property}" ]]; then + version="${value}" && check=true && break + else + check=false + fi + done < "${file}" + if [[ "${check}" == "false" ]]; then + return + fi + echo "${version}" +} + +# create Log directory +createLogDir () { + if [[ "${INSTALLER}" == "${DEB_TYPE}" || "${INSTALLER}" == "${RPM_TYPE}" ]]; then + getUserAndGroupFromFile + removeSoftLinkAndCreateDir "${NEW_DATA_DIR}/log" "${USER_TO_CHECK}" "${GROUP_TO_CHECK}" + fi +} + +# Creating migration log file +creationMigrateLog () { + local LOG_FILE_NAME="migration.log" + createLogDir + local MIGRATION_LOG_FILE="${NEW_DATA_DIR}/log/${LOG_FILE_NAME}" + if [[ "${INSTALLER}" == "${COMPOSE_TYPE}" || "${INSTALLER}" == "${HELM_TYPE}" ]]; then + MIGRATION_LOG_FILE="${SCRIPT_HOME}/${LOG_FILE_NAME}" + fi + touch "${MIGRATION_LOG_FILE}" + setFilePermission "${LOG_FILE_PERMISSION}" "${MIGRATION_LOG_FILE}" + exec &> >(tee -a "${MIGRATION_LOG_FILE}") +} +# Set path where system.yaml should create +setSystemYamlPath () { + SYSTEM_YAML_PATH="${NEW_DATA_DIR}/etc/system.yaml" + if [[ "${INSTALLER}" != "${HELM_TYPE}" ]]; then + logger "system.yaml will be created in path [${SYSTEM_YAML_PATH}]" + fi +} +# Create directory +createDirectory () { + local directory="$1" + local output="$2" + local check=false + local message="Could not create directory ${directory}, please check if the user ${USER} has permissions to perform this action" + removeSoftLink "${directory}" + mkdir -p "${directory}" && check=true || check=false + if [[ "${check}" == "false" ]]; then + if [[ "${output}" == "Warning" ]]; then + warn "${message}" + else + errorExit "${message}" + fi + fi + setOwnershipBasedOnInstaller "${directory}" +} + +setOwnershipBasedOnInstaller () { + local directory="$1" + if [[ "${INSTALLER}" == "${DEB_TYPE}" || "${INSTALLER}" == "${RPM_TYPE}" ]]; then + getUserAndGroupFromFile + chown -R ${USER_TO_CHECK}:${GROUP_TO_CHECK} "${directory}" || warn "Setting ownership on $directory failed" + elif [[ "${INSTALLER}" == "${COMPOSE_TYPE}" || "${INSTALLER}" == "${HELM_TYPE}" ]]; then + io_setOwnership "${directory}" "${JF_USER}" "${JF_USER}" + fi +} + +getUserAndGroup () { + local file="$1" + read uid gid <<<$(stat -c '%U %G' ${file}) + USER_TO_CHECK="${uid}" + GROUP_TO_CHECK="${gid}" +} + +# set ownership +getUserAndGroupFromFile () { + case $PRODUCT in + artifactory) + getUserAndGroup "/etc/opt/jfrog/artifactory/artifactory.properties" + ;; + distribution) + getUserAndGroup "${OLD_DATA_DIR}/etc/versions.properties" + ;; + xray) + getUserAndGroup "${OLD_DATA_DIR}/security/master.key" + ;; + esac +} + +# creating required directories +createRequiredDirs () { + bannerSubSection "CREATING REQUIRED DIRECTORIES" + if [[ "${INSTALLER}" == "${COMPOSE_TYPE}" || "${INSTALLER}" == "${HELM_TYPE}" ]]; then + removeSoftLinkAndCreateDir "${NEW_DATA_DIR}/etc/security" "${JF_USER}" "${JF_USER}" "yes" + removeSoftLinkAndCreateDir "${NEW_DATA_DIR}/data" "${JF_USER}" "${JF_USER}" "yes" + removeSoftLinkAndCreateDir "${NEW_DATA_DIR}/log/archived" "${JF_USER}" "${JF_USER}" "yes" + removeSoftLinkAndCreateDir "${NEW_DATA_DIR}/work" "${JF_USER}" "${JF_USER}" "yes" + removeSoftLinkAndCreateDir "${NEW_DATA_DIR}/backup" "${JF_USER}" "${JF_USER}" "yes" + io_setOwnership "${NEW_DATA_DIR}" "${JF_USER}" "${JF_USER}" + if [[ "${INSTALLER}" == "${COMPOSE_TYPE}" ]]; then + removeSoftLinkAndCreateDir "${NEW_DATA_DIR}/data/postgres" "${POSTGRES_USER}" "${POSTGRES_USER}" "yes" + fi + elif [[ "${INSTALLER}" == "${DEB_TYPE}" || "${INSTALLER}" == "${RPM_TYPE}" ]]; then + getUserAndGroupFromFile + removeSoftLinkAndCreateDir "${NEW_DATA_DIR}/etc" "${USER_TO_CHECK}" "${GROUP_TO_CHECK}" "yes" + removeSoftLinkAndCreateDir "${NEW_DATA_DIR}/etc/security" "${USER_TO_CHECK}" "${GROUP_TO_CHECK}" "yes" + removeSoftLinkAndCreateDir "${NEW_DATA_DIR}/data" "${USER_TO_CHECK}" "${GROUP_TO_CHECK}" "yes" + removeSoftLinkAndCreateDir "${NEW_DATA_DIR}/log/archived" "${USER_TO_CHECK}" "${GROUP_TO_CHECK}" "yes" + removeSoftLinkAndCreateDir "${NEW_DATA_DIR}/work" "${USER_TO_CHECK}" "${GROUP_TO_CHECK}" "yes" + removeSoftLinkAndCreateDir "${NEW_DATA_DIR}/backup" "${USER_TO_CHECK}" "${GROUP_TO_CHECK}" "yes" + fi +} + +# Check entry in map is format +checkMapEntry () { + local entry="$1" + + [[ "${entry}" != *"="* ]] && echo -n "false" || echo -n "true" +} +# Check value Empty and warn +warnIfEmpty () { + local filePath="$1" + local yamlPath="$2" + local check= + + if [[ -z "${filePath}" ]]; then + warn "Empty value in yamlpath [${yamlPath} in [${MIGRATION_SYSTEM_YAML_INFO}]" + check=false + else + check=true + fi + echo "${check}" +} + +logCopyStatus () { + local status="$1" + local logMessage="$2" + local warnMessage="$3" + + [[ "${status}" == "success" ]] && logger "${logMessage}" + [[ "${status}" == "fail" ]] && warn "${warnMessage}" +} +# copy contents from source to destination +copyCmd () { + local source="$1" + local target="$2" + local mode="$3" + local status= + + case $mode in + unique) + cp -up "${source}"/* "${target}"/ && status="success" || status="fail" + logCopyStatus "${status}" "Successfully copied directory contents from [${source}] to [${target}]" "Failed to copy directory contents from [${source}] to [${target}]" + ;; + specific) + cp -pf "${source}" "${target}"/ && status="success" || status="fail" + logCopyStatus "${status}" "Successfully copied file [${source}] to [${target}]" "Failed to copy file [${source}] to [${target}]" + ;; + patternFiles) + cp -pf "${source}"* "${target}"/ && status="success" || status="fail" + logCopyStatus "${status}" "Successfully copied files matching [${source}*] to [${target}]" "Failed to copy files matching [${source}*] to [${target}]" + ;; + full) + cp -prf "${source}"/* "${target}"/ && status="success" || status="fail" + logCopyStatus "${status}" "Successfully copied directory contents from [${source}] to [${target}]" "Failed to copy directory contents from [${source}] to [${target}]" + ;; + esac +} +# Check contents exist in source before copying +copyOnContentExist () { + local source="$1" + local target="$2" + local mode="$3" + + if [[ "$(checkContentExists "${source}")" == "true" ]]; then + copyCmd "${source}" "${target}" "${mode}" + else + logger "No contents to copy from [${source}]" + fi +} + +# move source to destination +moveCmd () { + local source="$1" + local target="$2" + local status= + + mv -f "${source}" "${target}" && status="success" || status="fail" + [[ "${status}" == "success" ]] && logger "Successfully moved directory [${source}] to [${target}]" + [[ "${status}" == "fail" ]] && warn "Failed to move directory [${source}] to [${target}]" +} + +# symlink target to source +symlinkCmd () { + local source="$1" + local target="$2" + local symlinkSubDir="$3" + local check=false + + if [[ "${symlinkSubDir}" == "subDir" ]]; then + ln -sf "${source}"/* "${target}" && check=true || check=false + else + ln -sf "${source}" "${target}" && check=true || check=false + fi + + [[ "${check}" == "true" ]] && logger "Successfully symlinked directory [${target}] to old [${source}]" + [[ "${check}" == "false" ]] && warn "Symlink operation failed" +} +# Check contents exist in source before symlinking +symlinkOnExist () { + local source="$1" + local target="$2" + local symlinkSubDir="$3" + + if [[ "$(checkContentExists "${source}")" == "true" ]]; then + if [[ "${symlinkSubDir}" == "subDir" ]]; then + symlinkCmd "${source}" "${target}" "subDir" + else + symlinkCmd "${source}" "${target}" + fi + else + logger "No contents to symlink from [${source}]" + fi +} + +prependDir () { + local absolutePath="$1" + local fullPath="$2" + local sourcePath= + + if [[ "${absolutePath}" = \/* ]]; then + sourcePath="${absolutePath}" + else + sourcePath="${fullPath}" + fi + echo "${sourcePath}" +} + +getFirstEntry (){ + local entry="$1" + + [[ -z "${entry}" ]] && return + echo "${entry}" | awk -F"=" '{print $1}' +} + +getSecondEntry () { + local entry="$1" + + [[ -z "${entry}" ]] && return + echo "${entry}" | awk -F"=" '{print $2}' +} +# To get absolutePath +pathResolver () { + local directoryPath="$1" + local dataDir= + + if [[ "${INSTALLER}" == "${COMPOSE_TYPE}" || "${INSTALLER}" == "${HELM_TYPE}" ]]; then + retrieveYamlValue "migration.oldDataDir" "oldDataDir" "Warning" + dataDir="${YAML_VALUE}" + cd "${dataDir}" + else + cd "${OLD_DATA_DIR}" + fi + absoluteDir="`cd "${directoryPath}";pwd`" + echo "${absoluteDir}" +} + +checkPathResolver () { + local value="$1" + + if [[ "${value}" == \/* ]]; then + value="${value}" + else + value="$(pathResolver "${value}")" + fi + echo "${value}" +} + +propertyMigrate () { + local entry="$1" + local filePath="$2" + local fileName="$3" + local check=false + + local yamlPath="$(getFirstEntry "${entry}")" + local property="$(getSecondEntry "${entry}")" + if [[ -z "${property}" ]]; then + warn "Property is empty in map [${entry}] in the file [${MIGRATION_SYSTEM_YAML_INFO}]" + return + fi + if [[ -z "${yamlPath}" ]]; then + warn "yamlPath is empty for [${property}] in [${MIGRATION_SYSTEM_YAML_INFO}]" + return + fi + local keyValues=$(cat "${NEW_DATA_DIR}/${filePath}/${fileName}" | grep "^[^#]" | grep "[*=*]") + for i in ${keyValues}; do + key=$(echo "${i}" | awk -F"=" '{print $1}') + value=$(echo "${i}" | cut -f 2- -d '=') + [ -z "${key}" ] && continue + [ -z "${value}" ] && continue + if [[ "${key}" == "${property}" ]]; then + if [[ "${PRODUCT}" == "artifactory" ]]; then + value="$(migrateResolveDerbyPath "${key}" "${value}")" + value="$(migrateResolveHaDirPath "${key}" "${value}")" + if [[ "${INSTALLER}" != "${DOCKER_TYPE}" ]]; then + value="$(updatePostgresUrlString_Hook "${yamlPath}" "${value}")" + fi + fi + if [[ "${key}" == "context.url" ]]; then + local ip=$(echo "${value}" | awk -F/ '{print $3}' | sed 's/:.*//') + setSystemValue "shared.node.ip" "${ip}" "${SYSTEM_YAML_PATH}" + logger "Setting [shared.node.ip] with [${ip}] in system.yaml" + fi + setSystemValue "${yamlPath}" "${value}" "${SYSTEM_YAML_PATH}" && logger "Setting [${yamlPath}] with value of the property [${property}] in system.yaml" && check=true && break || check=false + fi + done + [[ "${check}" == "false" ]] && logger "Property [${property}] not found in file [${fileName}]" +} + +setHaEnabled_hook () { + echo "" +} + +migratePropertiesFiles () { + local fileList= + local filePath= + local fileName= + local map= + + retrieveYamlValue "migration.propertyFiles.files" "fileList" "Skip" + fileList="${YAML_VALUE}" + if [[ -z "${fileList}" ]]; then + return + fi + bannerSection "PROCESSING MIGRATION OF PROPERTY FILES" + for file in ${fileList}; + do + bannerSubSection "Processing Migration of $file" + retrieveYamlValue "migration.propertyFiles.$file.filePath" "filePath" "Warning" + filePath="${YAML_VALUE}" + retrieveYamlValue "migration.propertyFiles.$file.fileName" "fileName" "Warning" + fileName="${YAML_VALUE}" + [[ -z "${filePath}" && -z "${fileName}" ]] && continue + if [[ "$(checkFileExists "${NEW_DATA_DIR}/${filePath}/${fileName}")" == "true" ]]; then + logger "File [${fileName}] found in path [${NEW_DATA_DIR}/${filePath}]" + # setting haEnabled with true only if ha-node.properties is present + setHaEnabled_hook "${filePath}" + retrieveYamlValue "migration.propertyFiles.$file.map" "map" "Warning" + map="${YAML_VALUE}" + [[ -z "${map}" ]] && continue + for entry in $map; + do + if [[ "$(checkMapEntry "${entry}")" == "true" ]]; then + propertyMigrate "${entry}" "${filePath}" "${fileName}" + else + warn "map entry [${entry}] in [${MIGRATION_SYSTEM_YAML_INFO}] is not in correct format, correct format i.e yamlPath=property" + fi + done + else + logger "File [${fileName}] was not found in path [${NEW_DATA_DIR}/${filePath}] to migrate" + fi + done +} + +createTargetDir () { + local mountDir="$1" + local target="$2" + + logger "Target directory not found [${mountDir}/${target}], creating it" + createDirectoryRecursive "${mountDir}" "${target}" "Warning" +} + +createDirectoryRecursive () { + local mountDir="$1" + local target="$2" + local output="$3" + local check=false + local message="Could not create directory ${directory}, please check if the user ${USER} has permissions to perform this action" + removeSoftLink "${mountDir}/${target}" + local directory=$(echo "${target}" | tr '/' ' ' ) + local targetDir="${mountDir}" + for dir in ${directory}; + do + targetDir="${targetDir}/${dir}" + mkdir -p "${targetDir}" && check=true || check=false + setOwnershipBasedOnInstaller "${targetDir}" + done + if [[ "${check}" == "false" ]]; then + if [[ "${output}" == "Warning" ]]; then + warn "${message}" + else + errorExit "${message}" + fi + fi +} + +copyOperation () { + local source="$1" + local target="$2" + local mode="$3" + local check=false + local targetDataDir= + local targetLink= + local date= + + # prepend OLD_DATA_DIR only if source is relative path + source="$(prependDir "${source}" "${OLD_DATA_DIR}/${source}")" + if [[ "${INSTALLER}" == "${HELM_TYPE}" ]]; then + targetDataDir="${NEW_DATA_DIR}" + else + targetDataDir="`cd "${NEW_DATA_DIR}"/../;pwd`" + fi + copyLogMessage "${mode}" + #remove source if it is a symlink + if [[ -L "${source}" ]]; then + targetLink=$(readlink -f "${source}") + logger "Removing the symlink [${source}] pointing to [${targetLink}]" + rm -f "${source}" + source=${targetLink} + fi + if [[ "$(checkDirExists "${source}")" != "true" ]]; then + logger "Source [${source}] directory not found in path" + return + fi + if [[ "$(checkDirContents "${source}")" != "true" ]]; then + logger "No contents to copy from [${source}]" + return + fi + if [[ "$(checkDirExists "${targetDataDir}/${target}")" != "true" ]]; then + createTargetDir "${targetDataDir}" "${target}" + fi + copyOnContentExist "${source}" "${targetDataDir}/${target}" "${mode}" +} + +copySpecificFiles () { + local source="$1" + local target="$2" + local mode="$3" + + # prepend OLD_DATA_DIR only if source is relative path + source="$(prependDir "${source}" "${OLD_DATA_DIR}/${source}")" + if [[ "${INSTALLER}" == "${HELM_TYPE}" ]]; then + targetDataDir="${NEW_DATA_DIR}" + else + targetDataDir="`cd "${NEW_DATA_DIR}"/../;pwd`" + fi + copyLogMessage "${mode}" + if [[ "$(checkFileExists "${source}")" != "true" ]]; then + logger "Source file [${source}] does not exist in path" + return + fi + if [[ "$(checkDirExists "${targetDataDir}/${target}")" != "true" ]]; then + createTargetDir "${targetDataDir}" "${target}" + fi + copyCmd "${source}" "${targetDataDir}/${target}" "${mode}" +} + +copyPatternMatchingFiles () { + local source="$1" + local target="$2" + local mode="$3" + local sourcePath="${4}" + + # prepend OLD_DATA_DIR only if source is relative path + sourcePath="$(prependDir "${sourcePath}" "${OLD_DATA_DIR}/${sourcePath}")" + if [[ "${INSTALLER}" == "${HELM_TYPE}" ]]; then + targetDataDir="${NEW_DATA_DIR}" + else + targetDataDir="`cd "${NEW_DATA_DIR}"/../;pwd`" + fi + copyLogMessage "${mode}" + if [[ "$(checkDirExists "${sourcePath}")" != "true" ]]; then + logger "Source [${sourcePath}] directory not found in path" + return + fi + if ls "${sourcePath}/${source}"* 1> /dev/null 2>&1; then + if [[ "$(checkDirExists "${targetDataDir}/${target}")" != "true" ]]; then + createTargetDir "${targetDataDir}" "${target}" + fi + copyCmd "${sourcePath}/${source}" "${targetDataDir}/${target}" "${mode}" + else + logger "Source file [${sourcePath}/${source}*] does not exist in path" + fi +} + +copyLogMessage () { + local mode="$1" + case $mode in + specific) + logger "Copy file [${source}] to target [${targetDataDir}/${target}]" + ;; + patternFiles) + logger "Copy files matching [${sourcePath}/${source}*] to target [${targetDataDir}/${target}]" + ;; + full) + logger "Copy directory contents from source [${source}] to target [${targetDataDir}/${target}]" + ;; + unique) + logger "Copy directory contents from source [${source}] to target [${targetDataDir}/${target}]" + ;; + esac +} + +copyBannerMessages () { + local mode="$1" + local textMode="$2" + case $mode in + specific) + bannerSection "COPY ${textMode} FILES" + ;; + patternFiles) + bannerSection "COPY MATCHING ${textMode}" + ;; + full) + bannerSection "COPY ${textMode} DIRECTORIES CONTENTS" + ;; + unique) + bannerSection "COPY ${textMode} DIRECTORIES CONTENTS" + ;; + esac +} + +invokeCopyFunctions () { + local mode="$1" + local source="$2" + local target="$3" + + case $mode in + specific) + copySpecificFiles "${source}" "${target}" "${mode}" + ;; + patternFiles) + retrieveYamlValue "migration.${copyFormat}.sourcePath" "map" "Warning" + local sourcePath="${YAML_VALUE}" + copyPatternMatchingFiles "${source}" "${target}" "${mode}" "${sourcePath}" + ;; + full) + copyOperation "${source}" "${target}" "${mode}" + ;; + unique) + copyOperation "${source}" "${target}" "${mode}" + ;; + esac +} +# Copies contents from source directory and target directory +copyDataDirectories () { + local copyFormat="$1" + local mode="$2" + local map= + local source= + local target= + local textMode= + local targetDataDir= + local copyFormatValue= + + retrieveYamlValue "migration.${copyFormat}" "${copyFormat}" "Skip" + copyFormatValue="${YAML_VALUE}" + if [[ -z "${copyFormatValue}" ]]; then + return + fi + textMode=$(echo "${mode}" | tr '[:lower:]' '[:upper:]' 2>/dev/null) + copyBannerMessages "${mode}" "${textMode}" + retrieveYamlValue "migration.${copyFormat}.map" "map" "Warning" + map="${YAML_VALUE}" + if [[ "${INSTALLER}" == "${HELM_TYPE}" ]]; then + targetDataDir="${NEW_DATA_DIR}" + else + targetDataDir="`cd "${NEW_DATA_DIR}"/../;pwd`" + fi + for entry in $map; + do + if [[ "$(checkMapEntry "${entry}")" == "true" ]]; then + source="$(getSecondEntry "${entry}")" + target="$(getFirstEntry "${entry}")" + [[ -z "${source}" ]] && warn "source value is empty for [${entry}] in [${MIGRATION_SYSTEM_YAML_INFO}]" && continue + [[ -z "${target}" ]] && warn "target value is empty for [${entry}] in [${MIGRATION_SYSTEM_YAML_INFO}]" && continue + invokeCopyFunctions "${mode}" "${source}" "${target}" + else + warn "map entry [${entry}] in [${MIGRATION_SYSTEM_YAML_INFO}] is not in correct format, correct format i.e target=source" + fi + echo ""; + done +} + +invokeMoveFunctions () { + local source="$1" + local target="$2" + local sourceDataDir= + local targetBasename= + # prepend OLD_DATA_DIR only if source is relative path + sourceDataDir=$(prependDir "${source}" "${OLD_DATA_DIR}/${source}") + targetBasename=$(dirname "${target}") + logger "Moving directory source [${sourceDataDir}] to target [${NEW_DATA_DIR}/${target}]" + if [[ "$(checkDirExists "${sourceDataDir}")" != "true" ]]; then + logger "Directory [${sourceDataDir}] not found in path to move" + return + fi + if [[ "$(checkDirExists "${NEW_DATA_DIR}/${targetBasename}")" != "true" ]]; then + createTargetDir "${NEW_DATA_DIR}" "${targetBasename}" + moveCmd "${sourceDataDir}" "${NEW_DATA_DIR}/${target}" + else + moveCmd "${sourceDataDir}" "${NEW_DATA_DIR}/tempDir" + moveCmd "${NEW_DATA_DIR}/tempDir" "${NEW_DATA_DIR}/${target}" + fi +} + +# Move source directory and target directory +moveDirectories () { + local moveDataDirectories= + local map= + local source= + local target= + + retrieveYamlValue "migration.moveDirectories" "moveDirectories" "Skip" + moveDirectories="${YAML_VALUE}" + if [[ -z "${moveDirectories}" ]]; then + return + fi + bannerSection "MOVE DIRECTORIES" + retrieveYamlValue "migration.moveDirectories.map" "map" "Warning" + map="${YAML_VALUE}" + for entry in $map; + do + if [[ "$(checkMapEntry "${entry}")" == "true" ]]; then + source="$(getSecondEntry "${entry}")" + target="$(getFirstEntry "${entry}")" + [[ -z "${source}" ]] && warn "source value is empty for [${entry}] in [${MIGRATION_SYSTEM_YAML_INFO}]" && continue + [[ -z "${target}" ]] && warn "target value is empty for [${entry}] in [${MIGRATION_SYSTEM_YAML_INFO}]" && continue + invokeMoveFunctions "${source}" "${target}" + else + warn "map entry [${entry}] in [${MIGRATION_SYSTEM_YAML_INFO}] is not in correct format, correct format i.e target=source" + fi + echo ""; + done +} + +# Trim masterKey if its generated using hex 32 +trimMasterKey () { + local masterKeyDir=/opt/jfrog/artifactory/var/etc/security + local oldMasterKey=$(<${masterKeyDir}/master.key) + local oldMasterKey_Length=$(echo ${#oldMasterKey}) + local newMasterKey= + if [[ ${oldMasterKey_Length} -gt 32 ]]; then + bannerSection "TRIM MASTERKEY" + newMasterKey=$(echo ${oldMasterKey:0:32}) + cp ${masterKeyDir}/master.key ${masterKeyDir}/backup_master.key + logger "Original masterKey is backed up : ${masterKeyDir}/backup_master.key" + rm -rf ${masterKeyDir}/master.key + echo ${newMasterKey} > ${masterKeyDir}/master.key + logger "masterKey is trimmed : ${masterKeyDir}/master.key" + fi +} + +copyDirectories () { + + copyDataDirectories "copyFiles" "full" + copyDataDirectories "copyUniqueFiles" "unique" + copyDataDirectories "copySpecificFiles" "specific" + copyDataDirectories "copyPatternMatchingFiles" "patternFiles" +} + +symlinkDir () { + local source="$1" + local target="$2" + local targetDir= + local basename= + local targetParentDir= + + targetDir="$(dirname "${target}")" + if [[ "${targetDir}" == "${source}" ]]; then + # symlink the sub directories + createDirectory "${NEW_DATA_DIR}/${target}" "Warning" + if [[ "$(checkDirExists "${NEW_DATA_DIR}/${target}")" == "true" ]]; then + symlinkOnExist "${OLD_DATA_DIR}/${source}" "${NEW_DATA_DIR}/${target}" "subDir" + basename="$(basename "${target}")" + cd "${NEW_DATA_DIR}/${target}" && rm -f "${basename}" + fi + else + targetParentDir="$(dirname "${NEW_DATA_DIR}/${target}")" + createDirectory "${targetParentDir}" "Warning" + if [[ "$(checkDirExists "${targetParentDir}")" == "true" ]]; then + symlinkOnExist "${OLD_DATA_DIR}/${source}" "${NEW_DATA_DIR}/${target}" + fi + fi +} + +symlinkOperation () { + local source="$1" + local target="$2" + local check=false + local targetLink= + local date= + + # Check if source is a link and do symlink + if [[ -L "${OLD_DATA_DIR}/${source}" ]]; then + targetLink=$(readlink -f "${OLD_DATA_DIR}/${source}") + symlinkOnExist "${targetLink}" "${NEW_DATA_DIR}/${target}" + else + # check if source is directory and do symlink + if [[ "$(checkDirExists "${OLD_DATA_DIR}/${source}")" != "true" ]]; then + logger "Source [${source}] directory not found in path to symlink" + return + fi + if [[ "$(checkDirContents "${OLD_DATA_DIR}/${source}")" != "true" ]]; then + logger "No contents found in [${OLD_DATA_DIR}/${source}] to symlink" + return + fi + if [[ "$(checkDirExists "${NEW_DATA_DIR}/${target}")" != "true" ]]; then + logger "Target directory [${NEW_DATA_DIR}/${target}] does not exist to create symlink, creating it" + symlinkDir "${source}" "${target}" + else + rm -rf "${NEW_DATA_DIR}/${target}" && check=true || check=false + [[ "${check}" == "false" ]] && warn "Failed to remove contents in [${NEW_DATA_DIR}/${target}/]" + symlinkDir "${source}" "${target}" + fi + fi +} +# Creates a symlink path - Source directory to which the symbolic link should point. +symlinkDirectories () { + local linkFiles= + local map= + local source= + local target= + + retrieveYamlValue "migration.linkFiles" "linkFiles" "Skip" + linkFiles="${YAML_VALUE}" + if [[ -z "${linkFiles}" ]]; then + return + fi + bannerSection "SYMLINK DIRECTORIES" + retrieveYamlValue "migration.linkFiles.map" "map" "Warning" + map="${YAML_VALUE}" + for entry in $map; + do + if [[ "$(checkMapEntry "${entry}")" == "true" ]]; then + source="$(getSecondEntry "${entry}")" + target="$(getFirstEntry "${entry}")" + logger "Symlink directory [${NEW_DATA_DIR}/${target}] to old [${OLD_DATA_DIR}/${source}]" + [[ -z "${source}" ]] && warn "source value is empty for [${entry}] in [${MIGRATION_SYSTEM_YAML_INFO}]" && continue + [[ -z "${target}" ]] && warn "target value is empty for [${entry}] in [${MIGRATION_SYSTEM_YAML_INFO}]" && continue + symlinkOperation "${source}" "${target}" + else + warn "map entry [${entry}] in [${MIGRATION_SYSTEM_YAML_INFO}] is not in correct format, correct format i.e target=source" + fi + echo ""; + done +} + +updateConnectionString () { + local yamlPath="$1" + local value="$2" + local mongoPath="shared.mongo.url" + local rabbitmqPath="shared.rabbitMq.url" + local postgresPath="shared.database.url" + local redisPath="shared.redis.connectionString" + local mongoConnectionString="mongo.connectionString" + local sourceKey= + local hostIp=$(io_getPublicHostIP) + local hostKey= + + if [[ "${INSTALLER}" == "${COMPOSE_TYPE}" || "${INSTALLER}" == "${HELM_TYPE}" ]]; then + # Replace @postgres:,@mongodb:,@rabbitmq:,@redis: to @{hostIp}: (Compose Installer) + hostKey="@${hostIp}:" + case $yamlPath in + ${postgresPath}) + sourceKey="@postgres:" + value=$(io_replaceString "${value}" "${sourceKey}" "${hostKey}") + ;; + ${mongoPath}) + sourceKey="@mongodb:" + value=$(io_replaceString "${value}" "${sourceKey}" "${hostKey}") + ;; + ${rabbitmqPath}) + sourceKey="@rabbitmq:" + value=$(io_replaceString "${value}" "${sourceKey}" "${hostKey}") + ;; + ${redisPath}) + sourceKey="@redis:" + value=$(io_replaceString "${value}" "${sourceKey}" "${hostKey}") + ;; + ${mongoConnectionString}) + sourceKey="@mongodb:" + value=$(io_replaceString "${value}" "${sourceKey}" "${hostKey}") + ;; + esac + fi + echo -n "${value}" +} + +yamlMigrate () { + local entry="$1" + local sourceFile="$2" + local value= + local yamlPath= + local key= + yamlPath="$(getFirstEntry "${entry}")" + key="$(getSecondEntry "${entry}")" + if [[ -z "${key}" ]]; then + warn "key is empty in map [${entry}] in the file [${MIGRATION_SYSTEM_YAML_INFO}]" + return + fi + if [[ -z "${yamlPath}" ]]; then + warn "yamlPath is empty for [${key}] in [${MIGRATION_SYSTEM_YAML_INFO}]" + return + fi + getYamlValue "${key}" "${sourceFile}" "false" + value="${YAML_VALUE}" + if [[ ! -z "${value}" ]]; then + value=$(updateConnectionString "${yamlPath}" "${value}") + fi + if [[ -z "${value}" ]]; then + logger "No value for [${key}] in [${sourceFile}]" + else + setSystemValue "${yamlPath}" "${value}" "${SYSTEM_YAML_PATH}" + logger "Setting [${yamlPath}] with value of the key [${key}] in system.yaml" + fi +} + +migrateYamlFile () { + local files= + local filePath= + local fileName= + local sourceFile= + local map= + retrieveYamlValue "migration.yaml.files" "files" "Skip" + files="${YAML_VALUE}" + if [[ -z "${files}" ]]; then + return + fi + bannerSection "MIGRATION OF YAML FILES" + for file in $files; + do + bannerSubSection "Processing Migration of $file" + retrieveYamlValue "migration.yaml.$file.filePath" "filePath" "Warning" + filePath="${YAML_VALUE}" + retrieveYamlValue "migration.yaml.$file.fileName" "fileName" "Warning" + fileName="${YAML_VALUE}" + [[ -z "${filePath}" && -z "${fileName}" ]] && continue + sourceFile="${NEW_DATA_DIR}/${filePath}/${fileName}" + if [[ "$(checkFileExists "${sourceFile}")" == "true" ]]; then + logger "File [${fileName}] found in path [${NEW_DATA_DIR}/${filePath}]" + retrieveYamlValue "migration.yaml.$file.map" "map" "Warning" + map="${YAML_VALUE}" + [[ -z "${map}" ]] && continue + for entry in $map; + do + if [[ "$(checkMapEntry "${entry}")" == "true" ]]; then + yamlMigrate "${entry}" "${sourceFile}" + else + warn "map entry [${entry}] in [${MIGRATION_SYSTEM_YAML_INFO}] is not in correct format, correct format i.e yamlPath=key" + fi + done + else + logger "File [${fileName}] is not found in path [${NEW_DATA_DIR}/${filePath}] to migrate" + fi + done +} +# updates the key and value in system.yaml +updateYamlKeyValue () { + local entry="$1" + local value= + local yamlPath= + local key= + + yamlPath="$(getFirstEntry "${entry}")" + value="$(getSecondEntry "${entry}")" + if [[ -z "${value}" ]]; then + warn "value is empty in map [${entry}] in the file [${MIGRATION_SYSTEM_YAML_INFO}]" + return + fi + if [[ -z "${yamlPath}" ]]; then + warn "yamlPath is empty for [${key}] in [${MIGRATION_SYSTEM_YAML_INFO}]" + return + fi + setSystemValue "${yamlPath}" "${value}" "${SYSTEM_YAML_PATH}" + logger "Setting [${yamlPath}] with value [${value}] in system.yaml" +} + +updateSystemYamlFile () { + local updateYaml= + local map= + + retrieveYamlValue "migration.updateSystemYaml" "updateYaml" "Skip" + updateSystemYaml="${YAML_VALUE}" + if [[ -z "${updateSystemYaml}" ]]; then + return + fi + bannerSection "UPDATE SYSTEM YAML FILE WITH KEY AND VALUES" + retrieveYamlValue "migration.updateSystemYaml.map" "map" "Warning" + map="${YAML_VALUE}" + if [[ -z "${map}" ]]; then + return + fi + for entry in $map; + do + if [[ "$(checkMapEntry "${entry}")" == "true" ]]; then + updateYamlKeyValue "${entry}" + else + warn "map entry [${entry}] in [${MIGRATION_SYSTEM_YAML_INFO}] is not in correct format, correct format i.e yamlPath=key" + fi + done +} + +backupFiles_hook () { + logSilly "Method ${FUNCNAME[0]}" +} + +backupDirectory () { + local backupDir="$1" + local dir="$2" + local targetDir="$3" + local effectiveUser= + local effectiveGroup= + + if [[ "${dir}" = \/* ]]; then + dir=$(echo "${dir/\//}") + fi + + if [[ "${INSTALLER}" == "${COMPOSE_TYPE}" || "${INSTALLER}" == "${HELM_TYPE}" ]]; then + effectiveUser="${JF_USER}" + effectiveGroup="${JF_USER}" + elif [[ "${INSTALLER}" == "${DEB_TYPE}" || "${INSTALLER}" == "${RPM_TYPE}" ]]; then + effectiveUser="${USER_TO_CHECK}" + effectiveGroup="${GROUP_TO_CHECK}" + fi + + removeSoftLinkAndCreateDir "${backupDir}" "${effectiveUser}" "${effectiveGroup}" "yes" + local backupDirectory="${backupDir}/${PRODUCT}" + removeSoftLinkAndCreateDir "${backupDirectory}" "${effectiveUser}" "${effectiveGroup}" "yes" + removeSoftLinkAndCreateDir "${backupDirectory}/${dir}" "${effectiveUser}" "${effectiveGroup}" "yes" + local outputCheckDirExists="$(checkDirExists "${backupDirectory}/${dir}")" + if [[ "${outputCheckDirExists}" == "true" ]]; then + copyOnContentExist "${targetDir}" "${backupDirectory}/${dir}" "full" + fi +} + +removeOldDirectory () { + local backupDir="$1" + local entry="$2" + local check=false + + # prepend OLD_DATA_DIR only if entry is relative path + local targetDir="$(prependDir "${entry}" "${OLD_DATA_DIR}/${entry}")" + local outputCheckDirExists="$(checkDirExists "${targetDir}")" + if [[ "${outputCheckDirExists}" != "true" ]]; then + logger "No [${targetDir}] directory found to delete" + echo ""; + return + fi + backupDirectory "${backupDir}" "${entry}" "${targetDir}" + rm -rf "${targetDir}" && check=true || check=false + [[ "${check}" == "true" ]] && logger "Successfully removed directory [${targetDir}]" + [[ "${check}" == "false" ]] && warn "Failed to remove directory [${targetDir}]" + echo ""; +} + +cleanUpOldDataDirectories () { + local cleanUpOldDataDir= + local map= + local entry= + + retrieveYamlValue "migration.cleanUpOldDataDir" "cleanUpOldDataDir" "Skip" + cleanUpOldDataDir="${YAML_VALUE}" + if [[ -z "${cleanUpOldDataDir}" ]]; then + return + fi + bannerSection "CLEAN UP OLD DATA DIRECTORIES" + retrieveYamlValue "migration.cleanUpOldDataDir.map" "map" "Warning" + map="${YAML_VALUE}" + [[ -z "${map}" ]] && continue + date="$(date +%Y%m%d%H%M)" + backupDir="${NEW_DATA_DIR}/backup/backup-${date}" + bannerImportant "****** Old data configurations are backedup in [${backupDir}] directory ******" + backupFiles_hook "${backupDir}/${PRODUCT}" + for entry in $map; + do + removeOldDirectory "${backupDir}" "${entry}" + done +} + +backupFiles () { + local backupDir="$1" + local dir="$2" + local targetDir="$3" + local fileName="$4" + local effectiveUser= + local effectiveGroup= + + if [[ "${dir}" = \/* ]]; then + dir=$(echo "${dir/\//}") + fi + + if [[ "${INSTALLER}" == "${COMPOSE_TYPE}" || "${INSTALLER}" == "${HELM_TYPE}" ]]; then + effectiveUser="${JF_USER}" + effectiveGroup="${JF_USER}" + elif [[ "${INSTALLER}" == "${DEB_TYPE}" || "${INSTALLER}" == "${RPM_TYPE}" ]]; then + effectiveUser="${USER_TO_CHECK}" + effectiveGroup="${GROUP_TO_CHECK}" + fi + + removeSoftLinkAndCreateDir "${backupDir}" "${effectiveUser}" "${effectiveGroup}" "yes" + local backupDirectory="${backupDir}/${PRODUCT}" + removeSoftLinkAndCreateDir "${backupDirectory}" "${effectiveUser}" "${effectiveGroup}" "yes" + removeSoftLinkAndCreateDir "${backupDirectory}/${dir}" "${effectiveUser}" "${effectiveGroup}" "yes" + local outputCheckDirExists="$(checkDirExists "${backupDirectory}/${dir}")" + if [[ "${outputCheckDirExists}" == "true" ]]; then + copyCmd "${targetDir}/${fileName}" "${backupDirectory}/${dir}" "specific" + fi +} + +removeOldFiles () { + local backupDir="$1" + local directoryName="$2" + local fileName="$3" + local check=false + + # prepend OLD_DATA_DIR only if entry is relative path + local targetDir="$(prependDir "${directoryName}" "${OLD_DATA_DIR}/${directoryName}")" + local outputCheckFileExists="$(checkFileExists "${targetDir}/${fileName}")" + if [[ "${outputCheckFileExists}" != "true" ]]; then + logger "No [${targetDir}/${fileName}] file found to delete" + return + fi + backupFiles "${backupDir}" "${directoryName}" "${targetDir}" "${fileName}" + rm -f "${targetDir}/${fileName}" && check=true || check=false + [[ "${check}" == "true" ]] && logger "Successfully removed file [${targetDir}/${fileName}]" + [[ "${check}" == "false" ]] && warn "Failed to remove file [${targetDir}/${fileName}]" + echo ""; +} + +cleanUpOldFiles () { + local cleanUpFiles= + local map= + local entry= + + retrieveYamlValue "migration.cleanUpOldFiles" "cleanUpOldFiles" "Skip" + cleanUpOldFiles="${YAML_VALUE}" + if [[ -z "${cleanUpOldFiles}" ]]; then + return + fi + bannerSection "CLEAN UP OLD FILES" + retrieveYamlValue "migration.cleanUpOldFiles.map" "map" "Warning" + map="${YAML_VALUE}" + [[ -z "${map}" ]] && continue + date="$(date +%Y%m%d%H%M)" + backupDir="${NEW_DATA_DIR}/backup/backup-${date}" + bannerImportant "****** Old files are backedup in [${backupDir}] directory ******" + for entry in $map; + do + local outputCheckMapEntry="$(checkMapEntry "${entry}")" + if [[ "${outputCheckMapEntry}" != "true" ]]; then + warn "map entry [${entry}] in [${MIGRATION_SYSTEM_YAML_INFO}] is not in correct format, correct format i.e directoryName=fileName" + fi + local fileName="$(getSecondEntry "${entry}")" + local directoryName="$(getFirstEntry "${entry}")" + [[ -z "${fileName}" ]] && warn "File name value is empty for [${entry}] in [${MIGRATION_SYSTEM_YAML_INFO}]" && continue + [[ -z "${directoryName}" ]] && warn "Directory name value is empty for [${entry}] in [${MIGRATION_SYSTEM_YAML_INFO}]" && continue + removeOldFiles "${backupDir}" "${directoryName}" "${fileName}" + echo ""; + done +} + +startMigration () { + bannerSection "STARTING MIGRATION" +} + +endMigration () { + bannerSection "MIGRATION COMPLETED SUCCESSFULLY" +} + +initialize () { + setAppDir + _pauseExecution "setAppDir" + initHelpers + _pauseExecution "initHelpers" + checkMigrationInfoYaml + _pauseExecution "checkMigrationInfoYaml" + getProduct + _pauseExecution "getProduct" + getDataDir + _pauseExecution "getDataDir" +} + +main () { + case $PRODUCT in + artifactory) + migrateArtifactory + ;; + distribution) + migrateDistribution + ;; + xray) + migrationXray + ;; + esac + exit 0 +} + +# Ensures meta data is logged +LOG_BEHAVIOR_ADD_META="$FLAG_Y" + + +migrateResolveDerbyPath () { + local key="$1" + local value="$2" + + if [[ "${key}" == "url" && "${value}" == *"db.home"* ]]; then + if [[ "${INSTALLER}" == "${COMPOSE_TYPE}" ]]; then + derbyPath="/opt/jfrog/artifactory/var/data/artifactory/derby" + value=$(echo "${value}" | sed "s|{db.home}|$derbyPath|") + else + derbyPath="${NEW_DATA_DIR}/data/artifactory/derby" + value=$(echo "${value}" | sed "s|{db.home}|$derbyPath|") + fi + fi + echo "${value}" +} + +migrateResolveHaDirPath () { + local key="$1" + local value="$2" + + if [[ "${INSTALLER}" == "${RPM_TYPE}" || "${INSTALLER}" == "${COMPOSE_TYPE}" || "${INSTALLER}" == "${HELM_TYPE}" || "${INSTALLER}" == "${DEB_TYPE}" ]]; then + if [[ "${key}" == "artifactory.ha.data.dir" || "${key}" == "artifactory.ha.backup.dir" ]]; then + value=$(checkPathResolver "${value}") + fi + fi + echo "${value}" +} +updatePostgresUrlString_Hook () { + local yamlPath="$1" + local value="$2" + local hostIp=$(io_getPublicHostIP) + local sourceKey="//postgresql:" + if [[ "${yamlPath}" == "shared.database.url" ]]; then + value=$(io_replaceString "${value}" "${sourceKey}" "//${hostIp}:" "#") + fi + echo "${value}" +} +# Check Artifactory product version +checkArtifactoryVersion () { + local minProductVersion="6.0.0" + local maxProductVersion="7.0.0" + local propertyInDocker="ARTIFACTORY_VERSION" + local property="artifactory.version" + + if [[ "${INSTALLER}" == "${COMPOSE_TYPE}" ]]; then + local newfilePath="${APP_DIR}/../.env" + local oldfilePath="${OLD_DATA_DIR}/etc/artifactory.properties" + elif [[ "${INSTALLER}" == "${HELM_TYPE}" ]]; then + local oldfilePath="${OLD_DATA_DIR}/etc/artifactory.properties" + elif [[ "${INSTALLER}" == "${ZIP_TYPE}" ]]; then + local newfilePath="${NEW_DATA_DIR}/etc/artifactory/artifactory.properties" + local oldfilePath="${OLD_DATA_DIR}/etc/artifactory.properties" + else + local newfilePath="${NEW_DATA_DIR}/etc/artifactory/artifactory.properties" + local oldfilePath="/etc/opt/jfrog/artifactory/artifactory.properties" + fi + + getProductVersion "${minProductVersion}" "${maxProductVersion}" "${newfilePath}" "${oldfilePath}" "${propertyInDocker}" "${property}" +} + +getCustomDataDir_hook () { + retrieveYamlValue "migration.oldDataDir" "oldDataDir" "Fail" + OLD_DATA_DIR="${YAML_VALUE}" +} + +# Get protocol value of connector +getXmlConnectorProtocol () { + local i="$1" + local filePath="$2" + local fileName="$3" + local protocolValue=$($LIBXML2_PATH --xpath '//Server/Service/Connector['$i']/@protocol' ${filePath}/${fileName} 2>/dev/null |awk -F"=" '{print $2}' | tr -d '"') + echo -e "${protocolValue}" +} + +# Get all attributes of connector +getXmlConnectorAttributes () { + local i="$1" + local filePath="$2" + local fileName="$3" + local connectorAttributes=$($LIBXML2_PATH --xpath '//Server/Service/Connector['$i']/@*' ${filePath}/${fileName} 2>/dev/null) + # strip leading and trailing spaces + connectorAttributes=$(io_trim "${connectorAttributes}") + echo "${connectorAttributes}" +} + +# Get port value of connector +getXmlConnectorPort () { + local i="$1" + local filePath="$2" + local fileName="$3" + local portValue=$($LIBXML2_PATH --xpath '//Server/Service/Connector['$i']/@port' ${filePath}/${fileName} 2>/dev/null | awk -F"=" '{print $2}' | tr -d '"') + echo -e "${portValue}" +} + +# Get maxThreads value of connector +getXmlConnectorMaxThreads () { + local i="$1" + local filePath="$2" + local fileName="$3" + local maxThreadValue=$($LIBXML2_PATH --xpath '//Server/Service/Connector['$i']/@maxThreads' ${filePath}/${fileName} 2>/dev/null | awk -F"=" '{print $2}' | tr -d '"') + echo -e "${maxThreadValue}" +} +# Get sendReasonPhrase value of connector +getXmlConnectorSendReasonPhrase () { + local i="$1" + local filePath="$2" + local fileName="$3" + local sendReasonPhraseValue=$($LIBXML2_PATH --xpath '//Server/Service/Connector['$i']/@sendReasonPhrase' ${filePath}/${fileName} 2>/dev/null | awk -F"=" '{print $2}' | tr -d '"') + echo -e "${sendReasonPhraseValue}" +} +# Get relaxedPathChars value of connector +getXmlConnectorRelaxedPathChars () { + local i="$1" + local filePath="$2" + local fileName="$3" + local relaxedPathCharsValue=$($LIBXML2_PATH --xpath '//Server/Service/Connector['$i']/@relaxedPathChars' ${filePath}/${fileName} 2>/dev/null | awk -F"=" '{print $2}' | tr -d '"') + # strip leading and trailing spaces + relaxedPathCharsValue=$(io_trim "${relaxedPathCharsValue}") + echo -e "${relaxedPathCharsValue}" +} +# Get relaxedQueryChars value of connector +getXmlConnectorRelaxedQueryChars () { + local i="$1" + local filePath="$2" + local fileName="$3" + local relaxedQueryCharsValue=$($LIBXML2_PATH --xpath '//Server/Service/Connector['$i']/@relaxedQueryChars' ${filePath}/${fileName} 2>/dev/null | awk -F"=" '{print $2}' | tr -d '"') + # strip leading and trailing spaces + relaxedQueryCharsValue=$(io_trim "${relaxedQueryCharsValue}") + echo -e "${relaxedQueryCharsValue}" +} + +# Updating system.yaml with Connector port +setConnectorPort () { + local yamlPath="$1" + local valuePort="$2" + local portYamlPath= + if [[ -z "${yamlPath}" ]]; then + return + fi + if [[ -z "${valuePort}" ]]; then + warn "port value is empty, could not migrate to system.yaml" + return + fi + ## Getting port yaml path from migration info yaml + retrieveYamlValue "${yamlPath}" portYamlPath "Warning" + portYamlPath="${YAML_VALUE}" + if [[ -z "${portYamlPath}" ]]; then + return + fi + setSystemValue "${portYamlPath}" "${valuePort}" "${SYSTEM_YAML_PATH}" + logger "Setting [${portYamlPath}] with value [${valuePort}] in system.yaml" +} + +# Updating system.yaml with Connector maxThreads +setConnectorMaxThread () { + local yamlPath="$1" + local threadValue="$2" + local maxThreadYamlPath= + if [[ -z "${yamlPath}" ]]; then + return + fi + if [[ -z "${threadValue}" ]]; then + return + fi + ## Getting max Threads yaml path from migration info yaml + retrieveYamlValue "${yamlPath}" maxThreadYamlPath "Warning" + maxThreadYamlPath="${YAML_VALUE}" + if [[ -z "${maxThreadYamlPath}" ]]; then + return + fi + setSystemValue "${maxThreadYamlPath}" "${threadValue}" "${SYSTEM_YAML_PATH}" + logger "Setting [${maxThreadYamlPath}] with value [${threadValue}] in system.yaml" +} + +# Updating system.yaml with Connector sendReasonPhrase +setConnectorSendReasonPhrase () { + local yamlPath="$1" + local sendReasonPhraseValue="$2" + local sendReasonPhraseYamlPath= + if [[ -z "${yamlPath}" ]]; then + return + fi + if [[ -z "${sendReasonPhraseValue}" ]]; then + return + fi + ## Getting sendReasonPhrase yaml path from migration info yaml + retrieveYamlValue "${yamlPath}" sendReasonPhraseYamlPath "Warning" + sendReasonPhraseYamlPath="${YAML_VALUE}" + if [[ -z "${sendReasonPhraseYamlPath}" ]]; then + return + fi + setSystemValue "${sendReasonPhraseYamlPath}" "${sendReasonPhraseValue}" "${SYSTEM_YAML_PATH}" + logger "Setting [${sendReasonPhraseYamlPath}] with value [${sendReasonPhraseValue}] in system.yaml" +} + +# Updating system.yaml with Connector relaxedPathChars +setConnectorRelaxedPathChars () { + local yamlPath="$1" + local relaxedPathCharsValue="$2" + local relaxedPathCharsYamlPath= + if [[ -z "${yamlPath}" ]]; then + return + fi + if [[ -z "${relaxedPathCharsValue}" ]]; then + return + fi + ## Getting relaxedPathChars yaml path from migration info yaml + retrieveYamlValue "${yamlPath}" relaxedPathCharsYamlPath "Warning" + relaxedPathCharsYamlPath="${YAML_VALUE}" + if [[ -z "${relaxedPathCharsYamlPath}" ]]; then + return + fi + setSystemValue "${relaxedPathCharsYamlPath}" "${relaxedPathCharsValue}" "${SYSTEM_YAML_PATH}" + logger "Setting [${relaxedPathCharsYamlPath}] with value [${relaxedPathCharsValue}] in system.yaml" +} + +# Updating system.yaml with Connector relaxedQueryChars +setConnectorRelaxedQueryChars () { + local yamlPath="$1" + local relaxedQueryCharsValue="$2" + local relaxedQueryCharsYamlPath= + if [[ -z "${yamlPath}" ]]; then + return + fi + if [[ -z "${relaxedQueryCharsValue}" ]]; then + return + fi + ## Getting relaxedQueryChars yaml path from migration info yaml + retrieveYamlValue "${yamlPath}" relaxedQueryCharsYamlPath "Warning" + relaxedQueryCharsYamlPath="${YAML_VALUE}" + if [[ -z "${relaxedQueryCharsYamlPath}" ]]; then + return + fi + setSystemValue "${relaxedQueryCharsYamlPath}" "${relaxedQueryCharsValue}" "${SYSTEM_YAML_PATH}" + logger "Setting [${relaxedQueryCharsYamlPath}] with value [${relaxedQueryCharsValue}] in system.yaml" +} + +# Updating system.yaml with Connectors configurations +setConnectorExtraConfig () { + local yamlPath="$1" + local connectorAttributes="$2" + local extraConfigPath= + if [[ -z "${yamlPath}" ]]; then + return + fi + if [[ -z "${connectorAttributes}" ]]; then + return + fi + ## Getting extraConfig yaml path from migration info yaml + retrieveYamlValue "${yamlPath}" extraConfig "Warning" + extraConfigPath="${YAML_VALUE}" + if [[ -z "${extraConfigPath}" ]]; then + return + fi + # strip leading and trailing spaces + connectorAttributes=$(io_trim "${connectorAttributes}") + setSystemValue "${extraConfigPath}" "${connectorAttributes}" "${SYSTEM_YAML_PATH}" + logger "Setting [${extraConfigPath}] with connector attributes in system.yaml" +} + +# Updating system.yaml with extra Connectors +setExtraConnector () { + local yamlPath="$1" + local extraConnector="$2" + local extraConnectorYamlPath= + if [[ -z "${yamlPath}" ]]; then + return + fi + if [[ -z "${extraConnector}" ]]; then + return + fi + ## Getting extraConnecotr yaml path from migration info yaml + retrieveYamlValue "${yamlPath}" extraConnectorYamlPath "Warning" + extraConnectorYamlPath="${YAML_VALUE}" + if [[ -z "${extraConnectorYamlPath}" ]]; then + return + fi + getYamlValue "${extraConnectorYamlPath}" "${SYSTEM_YAML_PATH}" "false" + local connectorExtra="${YAML_VALUE}" + if [[ -z "${connectorExtra}" ]]; then + setSystemValue "${extraConnectorYamlPath}" "${extraConnector}" "${SYSTEM_YAML_PATH}" + logger "Setting [${extraConnectorYamlPath}] with extra connectors in system.yaml" + else + setSystemValue "${extraConnectorYamlPath}" "\"${connectorExtra} ${extraConnector}\"" "${SYSTEM_YAML_PATH}" + logger "Setting [${extraConnectorYamlPath}] with extra connectors in system.yaml" + fi +} + +# Migrate extra connectors to system.yaml +migrateExtraConnectors () { + local filePath="$1" + local fileName="$2" + local connectorCount="$3" + local excludeDefaultPort="$4" + local i="$5" + local extraConfig= + local extraConnector= + if [[ "${excludeDefaultPort}" == "yes" ]]; then + for ((i = 1 ; i <= "${connectorCount}" ; i++)); + do + local portValue=$(getXmlConnectorPort "$i" "${filePath}" "${fileName}") + [[ "${portValue}" != "${DEFAULT_ACCESS_PORT}" && "${portValue}" != "${DEFAULT_RT_PORT}" ]] || continue + extraConnector=$($LIBXML2_PATH --xpath '//Server/Service/Connector['$i']' ${filePath}/${fileName} 2>/dev/null) + setExtraConnector "${EXTRA_CONFIG_YAMLPATH}" "${extraConnector}" + done + else + extraConnector=$($LIBXML2_PATH --xpath '//Server/Service/Connector['$i']' ${filePath}/${fileName} 2>/dev/null) + setExtraConnector "${EXTRA_CONFIG_YAMLPATH}" "${extraConnector}" + fi +} + +# Migrate connector configurations +migrateConnectorConfig () { + local i="$1" + local protocolType="$2" + local portValue="$3" + local connectorPortYamlPath="$4" + local connectorMaxThreadYamlPath="$5" + local connectorAttributesYamlPath="$6" + local filePath="$7" + local fileName="$8" + local connectorSendReasonPhraseYamlPath="$9" + local connectorRelaxedPathCharsYamlPath="${10}" + local connectorRelaxedQueryCharsYamlPath="${11}" + + # migrate port + setConnectorPort "${connectorPortYamlPath}" "${portValue}" + + # migrate maxThreads + local maxThreadValue=$(getXmlConnectorMaxThreads "$i" "${filePath}" "${fileName}") + setConnectorMaxThread "${connectorMaxThreadYamlPath}" "${maxThreadValue}" + + # migrate sendReasonPhrase + local sendReasonPhraseValue=$(getXmlConnectorSendReasonPhrase "$i" "${filePath}" "${fileName}") + setConnectorSendReasonPhrase "${connectorSendReasonPhraseYamlPath}" "${sendReasonPhraseValue}" + + # migrate relaxedPathChars + local relaxedPathCharsValue=$(getXmlConnectorRelaxedPathChars "$i" "${filePath}" "${fileName}") + setConnectorRelaxedPathChars "${connectorRelaxedPathCharsYamlPath}" "\"${relaxedPathCharsValue}\"" + # migrate relaxedQueryChars + local relaxedQueryCharsValue=$(getXmlConnectorRelaxedQueryChars "$i" "${filePath}" "${fileName}") + setConnectorRelaxedQueryChars "${connectorRelaxedQueryCharsYamlPath}" "\"${relaxedQueryCharsValue}\"" + + # migrate all attributes to extra config except port , maxThread , sendReasonPhrase ,relaxedPathChars and relaxedQueryChars + local connectorAttributes=$(getXmlConnectorAttributes "$i" "${filePath}" "${fileName}") + connectorAttributes=$(echo "${connectorAttributes}" | sed 's/port="'${portValue}'"//g' | sed 's/maxThreads="'${maxThreadValue}'"//g' | sed 's/sendReasonPhrase="'${sendReasonPhraseValue}'"//g' | sed 's/relaxedPathChars="\'${relaxedPathCharsValue}'\"//g' | sed 's/relaxedQueryChars="\'${relaxedQueryCharsValue}'\"//g') + # strip leading and trailing spaces + connectorAttributes=$(io_trim "${connectorAttributes}") + setConnectorExtraConfig "${connectorAttributesYamlPath}" "${connectorAttributes}" +} + +# Check for default port 8040 and 8081 in connectors and migrate +migrateConnectorPort () { + local filePath="$1" + local fileName="$2" + local connectorCount="$3" + local defaultPort="$4" + local connectorPortYamlPath="$5" + local connectorMaxThreadYamlPath="$6" + local connectorAttributesYamlPath="$7" + local connectorSendReasonPhraseYamlPath="$8" + local connectorRelaxedPathCharsYamlPath="$9" + local connectorRelaxedQueryCharsYamlPath="${10}" + local portYamlPath= + local maxThreadYamlPath= + local status= + for ((i = 1 ; i <= "${connectorCount}" ; i++)); + do + local portValue=$(getXmlConnectorPort "$i" "${filePath}" "${fileName}") + local protocolType=$(getXmlConnectorProtocol "$i" "${filePath}" "${fileName}") + [[ "${protocolType}" == *AJP* ]] && continue + [[ "${portValue}" != "${defaultPort}" ]] && continue + if [[ "${portValue}" == "${DEFAULT_RT_PORT}" ]]; then + RT_DEFAULTPORT_STATUS=success + else + AC_DEFAULTPORT_STATUS=success + fi + migrateConnectorConfig "${i}" "${protocolType}" "${portValue}" "${connectorPortYamlPath}" "${connectorMaxThreadYamlPath}" "${connectorAttributesYamlPath}" "${filePath}" "${fileName}" "${connectorSendReasonPhraseYamlPath}" "${connectorRelaxedPathCharsYamlPath}" "${connectorRelaxedQueryCharsYamlPath}" + done +} + +# migrate to extra, connector having default port and protocol is AJP +migrateDefaultPortIfAjp () { + local filePath="$1" + local fileName="$2" + local connectorCount="$3" + local defaultPort="$4" + + for ((i = 1 ; i <= "${connectorCount}" ; i++)); + do + local portValue=$(getXmlConnectorPort "$i" "${filePath}" "${fileName}") + local protocolType=$(getXmlConnectorProtocol "$i" "${filePath}" "${fileName}") + [[ "${protocolType}" != *AJP* ]] && continue + [[ "${portValue}" != "${defaultPort}" ]] && continue + migrateExtraConnectors "${filePath}" "${fileName}" "${connectorCount}" "no" "${i}" + done + +} + +# Comparing max threads in connectors +compareMaxThreads () { + local firstConnectorMaxThread="$1" + local firstConnectorNode="$2" + local secondConnectorMaxThread="$3" + local secondConnectorNode="$4" + local filePath="$5" + local fileName="$6" + + # choose higher maxThreads connector as Artifactory. + if [[ "${firstConnectorMaxThread}" -gt ${secondConnectorMaxThread} || "${firstConnectorMaxThread}" -eq ${secondConnectorMaxThread} ]]; then + # maxThread is higher in firstConnector, + # Taking firstConnector as Artifactory and SecondConnector as Access + # maxThread is equal in both connector,considering firstConnector as Artifactory and SecondConnector as Access + local rtPortValue=$(getXmlConnectorPort "${firstConnectorNode}" "${filePath}" "${fileName}") + migrateConnectorConfig "${firstConnectorNode}" "${protocolType}" "${rtPortValue}" "${RT_PORT_YAMLPATH}" "${RT_MAXTHREADS_YAMLPATH}" "${RT_EXTRACONFIG_YAMLPATH}" "${filePath}" "${fileName}" "${RT_SENDREASONPHRASE_YAMLPATH}" "${RT_RELAXEDPATHCHARS_YAMLPATH}" "${RT_RELAXEDQUERYCHARS_YAMLPATH}" + local acPortValue=$(getXmlConnectorPort "${secondConnectorNode}" "${filePath}" "${fileName}") + migrateConnectorConfig "${secondConnectorNode}" "${protocolType}" "${acPortValue}" "${AC_PORT_YAMLPATH}" "${AC_MAXTHREADS_YAMLPATH}" "${AC_EXTRACONFIG_YAMLPATH}" "${filePath}" "${fileName}" "${AC_SENDREASONPHRASE_YAMLPATH}" + else + # maxThread is higher in SecondConnector, + # Taking SecondConnector as Artifactory and firstConnector as Access + local rtPortValue=$(getXmlConnectorPort "${secondConnectorNode}" "${filePath}" "${fileName}") + migrateConnectorConfig "${secondConnectorNode}" "${protocolType}" "${rtPortValue}" "${RT_PORT_YAMLPATH}" "${RT_MAXTHREADS_YAMLPATH}" "${RT_EXTRACONFIG_YAMLPATH}" "${filePath}" "${fileName}" "${RT_SENDREASONPHRASE_YAMLPATH}" "${RT_RELAXEDPATHCHARS_YAMLPATH}" "${RT_RELAXEDQUERYCHARS_YAMLPATH}" + local acPortValue=$(getXmlConnectorPort "${firstConnectorNode}" "${filePath}" "${fileName}") + migrateConnectorConfig "${firstConnectorNode}" "${protocolType}" "${acPortValue}" "${AC_PORT_YAMLPATH}" "${AC_MAXTHREADS_YAMLPATH}" "${AC_EXTRACONFIG_YAMLPATH}" "${filePath}" "${fileName}" "${AC_SENDREASONPHRASE_YAMLPATH}" + fi +} + +# Check max threads exist to compare +maxThreadsExistToCompare () { + local filePath="$1" + local fileName="$2" + local connectorCount="$3" + local firstConnectorMaxThread= + local secondConnectorMaxThread= + local firstConnectorNode= + local secondConnectorNode= + local status=success + local firstnode=fail + + for ((i = 1 ; i <= "${connectorCount}" ; i++)); + do + local protocolType=$(getXmlConnectorProtocol "$i" "${filePath}" "${fileName}") + if [[ ${protocolType} == *AJP* ]]; then + # Migrate Connectors + migrateExtraConnectors "${filePath}" "${fileName}" "${connectorCount}" "no" "${i}" + continue + fi + # store maxthreads value of each connector + if [[ ${firstnode} == "fail" ]]; then + firstConnectorMaxThread=$(getXmlConnectorMaxThreads "${i}" "${filePath}" "${fileName}") + firstConnectorNode="${i}" + firstnode=success + else + secondConnectorMaxThread=$(getXmlConnectorMaxThreads "${i}" "${filePath}" "${fileName}") + secondConnectorNode="${i}" + fi + done + [[ -z "${firstConnectorMaxThread}" ]] && status=fail + [[ -z "${secondConnectorMaxThread}" ]] && status=fail + # maxThreads is set, now compare MaxThreads + if [[ "${status}" == "success" ]]; then + compareMaxThreads "${firstConnectorMaxThread}" "${firstConnectorNode}" "${secondConnectorMaxThread}" "${secondConnectorNode}" "${filePath}" "${fileName}" + else + # Assume first connector is RT, maxThreads is not set in both connectors + local rtPortValue=$(getXmlConnectorPort "${firstConnectorNode}" "${filePath}" "${fileName}") + migrateConnectorConfig "${firstConnectorNode}" "${protocolType}" "${rtPortValue}" "${RT_PORT_YAMLPATH}" "${RT_MAXTHREADS_YAMLPATH}" "${RT_EXTRACONFIG_YAMLPATH}" "${filePath}" "${fileName}" "${RT_SENDREASONPHRASE_YAMLPATH}" "${RT_RELAXEDPATHCHARS_YAMLPATH}" "${RT_RELAXEDQUERYCHARS_YAMLPATH}" + local acPortValue=$(getXmlConnectorPort "${secondConnectorNode}" "${filePath}" "${fileName}") + migrateConnectorConfig "${secondConnectorNode}" "${protocolType}" "${acPortValue}" "${AC_PORT_YAMLPATH}" "${AC_MAXTHREADS_YAMLPATH}" "${AC_EXTRACONFIG_YAMLPATH}" "${filePath}" "${fileName}" "${AC_SENDREASONPHRASE_YAMLPATH}" + fi +} + +migrateExtraBasedOnNonAjpCount () { + local nonAjpCount="$1" + local filePath="$2" + local fileName="$3" + local connectorCount="$4" + local i="$5" + + local protocolType=$(getXmlConnectorProtocol "$i" "${filePath}" "${fileName}") + if [[ "${protocolType}" == *AJP* ]]; then + if [[ "${nonAjpCount}" -eq 1 ]]; then + # migrateExtraConnectors + migrateExtraConnectors "${filePath}" "${fileName}" "${connectorCount}" "no" "${i}" + continue + else + # migrateExtraConnectors + migrateExtraConnectors "${filePath}" "${fileName}" "${connectorCount}" "yes" + continue + fi + fi +} + +# find RT and AC Connector +findRtAndAcConnector () { + local filePath="$1" + local fileName="$2" + local connectorCount="$3" + local initialAjpCount=0 + local nonAjpCount=0 + + # get the count of non AJP + for ((i = 1 ; i <= "${connectorCount}" ; i++)); + do + local portValue=$(getXmlConnectorPort "$i" "${filePath}" "${fileName}") + local protocolType=$(getXmlConnectorProtocol "$i" "${filePath}" "${fileName}") + [[ "${protocolType}" != *AJP* ]] || continue + nonAjpCount=$((initialAjpCount+1)) + initialAjpCount="${nonAjpCount}" + done + if [[ "${nonAjpCount}" -eq 1 ]]; then + # Add the connector found as access and artifactory connectors + # Mark port as 8040 for access + for ((i = 1 ; i <= "${connectorCount}" ; i++)) + do + migrateExtraBasedOnNonAjpCount "${nonAjpCount}" "${filePath}" "${fileName}" "${connectorCount}" "$i" + local portValue=$(getXmlConnectorPort "$i" "${filePath}" "${fileName}") + migrateConnectorConfig "$i" "${protocolType}" "${portValue}" "${RT_PORT_YAMLPATH}" "${RT_MAXTHREADS_YAMLPATH}" "${RT_EXTRACONFIG_YAMLPATH}" "${filePath}" "${fileName}" "${RT_SENDREASONPHRASE_YAMLPATH}" "${RT_RELAXEDPATHCHARS_YAMLPATH}" "${RT_RELAXEDQUERYCHARS_YAMLPATH}" + migrateConnectorConfig "$i" "${protocolType}" "${portValue}" "${AC_PORT_YAMLPATH}" "${AC_MAXTHREADS_YAMLPATH}" "${AC_EXTRACONFIG_YAMLPATH}" "${filePath}" "${fileName}" "${AC_SENDREASONPHRASE_YAMLPATH}" + setConnectorPort "${AC_PORT_YAMLPATH}" "${DEFAULT_ACCESS_PORT}" + done + elif [[ "${nonAjpCount}" -eq 2 ]]; then + # compare maxThreads in both connectors + maxThreadsExistToCompare "${filePath}" "${fileName}" "${connectorCount}" + elif [[ "${nonAjpCount}" -gt 2 ]]; then + # migrateExtraConnectors + migrateExtraConnectors "${filePath}" "${fileName}" "${connectorCount}" "yes" + elif [[ "${nonAjpCount}" -eq 0 ]]; then + # setting with default port in system.yaml + setConnectorPort "${RT_PORT_YAMLPATH}" "${DEFAULT_RT_PORT}" + setConnectorPort "${AC_PORT_YAMLPATH}" "${DEFAULT_ACCESS_PORT}" + # migrateExtraConnectors + migrateExtraConnectors "${filePath}" "${fileName}" "${connectorCount}" "yes" + fi +} + +# get the count of non AJP +getCountOfNonAjp () { + local port="$1" + local connectorCount="$2" + local filePath=$3 + local fileName=$4 + local initialNonAjpCount=0 + + for ((i = 1 ; i <= "${connectorCount}" ; i++)); + do + local portValue=$(getXmlConnectorPort "$i" "${filePath}" "${fileName}") + local protocolType=$(getXmlConnectorProtocol "$i" "${filePath}" "${fileName}") + [[ "${portValue}" != "${port}" ]] || continue + [[ "${protocolType}" != *AJP* ]] || continue + local nonAjpCount=$((initialNonAjpCount+1)) + initialNonAjpCount="${nonAjpCount}" + done + echo -e "${nonAjpCount}" +} + +# Find for access connector +findAcConnector () { + local filePath="$1" + local fileName="$2" + local connectorCount="$3" + + # get the count of non AJP + local nonAjpCount=$(getCountOfNonAjp "${DEFAULT_RT_PORT}" "${connectorCount}" "${filePath}" "${fileName}") + if [[ "${nonAjpCount}" -eq 1 ]]; then + # Add the connector found as access connector and mark port as that of connector + for ((i = 1 ; i <= "${connectorCount}" ; i++)) + do + migrateExtraBasedOnNonAjpCount "${nonAjpCount}" "${filePath}" "${fileName}" "${connectorCount}" "$i" + local portValue=$(getXmlConnectorPort "$i" "${filePath}" "${fileName}") + if [[ "${portValue}" != "${DEFAULT_RT_PORT}" ]]; then + migrateConnectorConfig "$i" "${protocolType}" "${portValue}" "${AC_PORT_YAMLPATH}" "${AC_MAXTHREADS_YAMLPATH}" "${AC_EXTRACONFIG_YAMLPATH}" "${filePath}" "${fileName}" "${AC_SENDREASONPHRASE_YAMLPATH}" + fi + done + elif [[ "${nonAjpCount}" -gt 1 ]]; then + # Take RT properties into access with 8040 + for ((i = 1 ; i <= "${connectorCount}" ; i++)) + do + migrateExtraBasedOnNonAjpCount "${nonAjpCount}" "${filePath}" "${fileName}" "${connectorCount}" "$i" + local portValue=$(getXmlConnectorPort "$i" "${filePath}" "${fileName}") + if [[ "${portValue}" == "${DEFAULT_RT_PORT}" ]]; then + migrateConnectorConfig "$i" "${protocolType}" "${portValue}" "${AC_PORT_YAMLPATH}" "${AC_MAXTHREADS_YAMLPATH}" "${AC_EXTRACONFIG_YAMLPATH}" "${filePath}" "${fileName}" "${AC_SENDREASONPHRASE_YAMLPATH}" + setConnectorPort "${AC_PORT_YAMLPATH}" "${DEFAULT_ACCESS_PORT}" + fi + done + elif [[ "${nonAjpCount}" -eq 0 ]]; then + # Add RT connector details as access connector and mark port as 8040 + migrateConnectorPort "${filePath}" "${fileName}" "${connectorCount}" "${DEFAULT_RT_PORT}" "${AC_PORT_YAMLPATH}" "${AC_MAXTHREADS_YAMLPATH}" "${AC_EXTRACONFIG_YAMLPATH}" "${AC_SENDREASONPHRASE_YAMLPATH}" + setConnectorPort "${AC_PORT_YAMLPATH}" "${DEFAULT_ACCESS_PORT}" + # migrateExtraConnectors + migrateExtraConnectors "${filePath}" "${fileName}" "${connectorCount}" "yes" + fi +} + +# Find for artifactory connector +findRtConnector () { + local filePath="$1" + local fileName="$2" + local connectorCount="$3" + + # get the count of non AJP + local nonAjpCount=$(getCountOfNonAjp "${DEFAULT_ACCESS_PORT}" "${connectorCount}" "${filePath}" "${fileName}") + if [[ "${nonAjpCount}" -eq 1 ]]; then + # Add the connector found as RT connector + for ((i = 1 ; i <= "${connectorCount}" ; i++)) + do + migrateExtraBasedOnNonAjpCount "${nonAjpCount}" "${filePath}" "${fileName}" "${connectorCount}" "$i" + local portValue=$(getXmlConnectorPort "$i" "${filePath}" "${fileName}") + if [[ "${portValue}" != "${DEFAULT_ACCESS_PORT}" ]]; then + migrateConnectorConfig "$i" "${protocolType}" "${portValue}" "${RT_PORT_YAMLPATH}" "${RT_MAXTHREADS_YAMLPATH}" "${RT_EXTRACONFIG_YAMLPATH}" "${filePath}" "${fileName}" "${RT_SENDREASONPHRASE_YAMLPATH}" "${RT_RELAXEDPATHCHARS_YAMLPATH}" "${RT_RELAXEDQUERYCHARS_YAMLPATH}" + fi + done + elif [[ "${nonAjpCount}" -gt 1 ]]; then + # Take access properties into artifactory with 8081 + for ((i = 1 ; i <= "${connectorCount}" ; i++)) + do + migrateExtraBasedOnNonAjpCount "${nonAjpCount}" "${filePath}" "${fileName}" "${connectorCount}" "$i" + local portValue=$(getXmlConnectorPort "$i" "${filePath}" "${fileName}") + if [[ "${portValue}" == "${DEFAULT_ACCESS_PORT}" ]]; then + migrateConnectorConfig "$i" "${protocolType}" "${portValue}" "${RT_PORT_YAMLPATH}" "${RT_MAXTHREADS_YAMLPATH}" "${RT_EXTRACONFIG_YAMLPATH}" "${filePath}" "${fileName}" "${RT_SENDREASONPHRASE_YAMLPATH}" "${RT_RELAXEDPATHCHARS_YAMLPATH}" "${RT_RELAXEDQUERYCHARS_YAMLPATH}" + setConnectorPort "${RT_PORT_YAMLPATH}" "${DEFAULT_RT_PORT}" + fi + done + elif [[ "${nonAjpCount}" -eq 0 ]]; then + # Add access connector details as RT connector and mark as ${DEFAULT_RT_PORT} + migrateConnectorPort "${filePath}" "${fileName}" "${connectorCount}" "${DEFAULT_ACCESS_PORT}" "${RT_PORT_YAMLPATH}" "${RT_MAXTHREADS_YAMLPATH}" "${RT_EXTRACONFIG_YAMLPATH}" "${RT_SENDREASONPHRASE_YAMLPATH}" "${RT_RELAXEDPATHCHARS_YAMLPATH}" "${RT_RELAXEDQUERYCHARS_YAMLPATH}" + setConnectorPort "${RT_PORT_YAMLPATH}" "${DEFAULT_RT_PORT}" + # migrateExtraConnectors + migrateExtraConnectors "${filePath}" "${fileName}" "${connectorCount}" "yes" + fi +} + +checkForTlsConnector () { + local filePath="$1" + local fileName="$2" + local connectorCount="$3" + for ((i = 1 ; i <= "${connectorCount}" ; i++)) + do + local sslProtocolValue=$($LIBXML2_PATH --xpath '//Server/Service/Connector['$i']/@sslProtocol' ${filePath}/${fileName} 2>/dev/null | awk -F"=" '{print $2}' | tr -d '"') + if [[ "${sslProtocolValue}" == "TLS" ]]; then + bannerImportant "NOTE: Ignoring TLS connector during migration, modify the system yaml to enable TLS. Original server.xml is saved in path [${filePath}/${fileName}]" + TLS_CONNECTOR_EXISTS=${FLAG_Y} + continue + fi + done +} + +# set custom tomcat server Listeners to system.yaml +setListenerConnector () { + local filePath="$1" + local fileName="$2" + local listenerCount="$3" + for ((i = 1 ; i <= "${listenerCount}" ; i++)) + do + local listenerConnector=$($LIBXML2_PATH --xpath '//Server/Listener['$i']' ${filePath}/${fileName} 2>/dev/null) + local listenerClassName=$($LIBXML2_PATH --xpath '//Server/Listener['$i']/@className' ${filePath}/${fileName} 2>/dev/null | awk -F"=" '{print $2}' | tr -d '"') + if [[ "${listenerClassName}" == *Apr* ]]; then + setExtraConnector "${EXTRA_LISTENER_CONFIG_YAMLPATH}" "${listenerConnector}" + fi + done +} +# add custom tomcat server Listeners +addTomcatServerListeners () { + local filePath="$1" + local fileName="$2" + local listenerCount="$3" + if [[ "${listenerCount}" == "0" ]]; then + logger "No listener connectors found in the [${filePath}/${fileName}],skipping migration of listener connectors" + else + setListenerConnector "${filePath}" "${fileName}" "${listenerCount}" + setSystemValue "${RT_TOMCAT_HTTPSCONNECTOR_ENABLED}" "true" "${SYSTEM_YAML_PATH}" + logger "Setting [${RT_TOMCAT_HTTPSCONNECTOR_ENABLED}] with value [true] in system.yaml" + fi +} + +# server.xml migration operations +xmlMigrateOperation () { + local filePath="$1" + local fileName="$2" + local connectorCount="$3" + local listenerCount="$4" + RT_DEFAULTPORT_STATUS=fail + AC_DEFAULTPORT_STATUS=fail + TLS_CONNECTOR_EXISTS=${FLAG_N} + + # Check for connector with TLS , if found ignore migrating it + checkForTlsConnector "${filePath}" "${fileName}" "${connectorCount}" + if [[ "${TLS_CONNECTOR_EXISTS}" == "${FLAG_Y}" ]]; then + return + fi + addTomcatServerListeners "${filePath}" "${fileName}" "${listenerCount}" + # Migrate RT default port from connectors + migrateConnectorPort "${filePath}" "${fileName}" "${connectorCount}" "${DEFAULT_RT_PORT}" "${RT_PORT_YAMLPATH}" "${RT_MAXTHREADS_YAMLPATH}" "${RT_EXTRACONFIG_YAMLPATH}" "${RT_SENDREASONPHRASE_YAMLPATH}" "${RT_RELAXEDPATHCHARS_YAMLPATH}" "${RT_RELAXEDQUERYCHARS_YAMLPATH}" + # Migrate to extra if RT default ports are AJP + migrateDefaultPortIfAjp "${filePath}" "${fileName}" "${connectorCount}" "${DEFAULT_RT_PORT}" + # Migrate AC default port from connectors + migrateConnectorPort "${filePath}" "${fileName}" "${connectorCount}" "${DEFAULT_ACCESS_PORT}" "${AC_PORT_YAMLPATH}" "${AC_MAXTHREADS_YAMLPATH}" "${AC_EXTRACONFIG_YAMLPATH}" "${AC_SENDREASONPHRASE_YAMLPATH}" + # Migrate to extra if access default ports are AJP + migrateDefaultPortIfAjp "${filePath}" "${fileName}" "${connectorCount}" "${DEFAULT_ACCESS_PORT}" + + if [[ "${AC_DEFAULTPORT_STATUS}" == "success" && "${RT_DEFAULTPORT_STATUS}" == "success" ]]; then + # RT and AC default port found + logger "Artifactory 8081 and Access 8040 default port are found" + migrateExtraConnectors "${filePath}" "${fileName}" "${connectorCount}" "yes" + elif [[ "${AC_DEFAULTPORT_STATUS}" == "success" && "${RT_DEFAULTPORT_STATUS}" == "fail" ]]; then + # Only AC default port found,find RT connector + logger "Found Access default 8040 port" + findRtConnector "${filePath}" "${fileName}" "${connectorCount}" + elif [[ "${AC_DEFAULTPORT_STATUS}" == "fail" && "${RT_DEFAULTPORT_STATUS}" == "success" ]]; then + # Only RT default port found,find AC connector + logger "Found Artifactory default 8081 port" + findAcConnector "${filePath}" "${fileName}" "${connectorCount}" + elif [[ "${AC_DEFAULTPORT_STATUS}" == "fail" && "${RT_DEFAULTPORT_STATUS}" == "fail" ]]; then + # RT and AC default port not found, find connector + logger "Artifactory 8081 and Access 8040 default port are not found" + findRtAndAcConnector "${filePath}" "${fileName}" "${connectorCount}" + fi +} + +# get count of connectors +getXmlConnectorCount () { + local filePath="$1" + local fileName="$2" + local count=$($LIBXML2_PATH --xpath 'count(/Server/Service/Connector)' ${filePath}/${fileName}) + echo -e "${count}" +} + +# get count of listener connectors +getTomcatServerListenersCount () { + local filePath="$1" + local fileName="$2" + local count=$($LIBXML2_PATH --xpath 'count(/Server/Listener)' ${filePath}/${fileName}) + echo -e "${count}" +} + +# Migrate server.xml configuration to system.yaml +migrateXmlFile () { + local xmlFiles= + local fileName= + local filePath= + local sourceFilePath= + DEFAULT_ACCESS_PORT="8040" + DEFAULT_RT_PORT="8081" + AC_PORT_YAMLPATH="migration.xmlFiles.serverXml.access.port" + AC_MAXTHREADS_YAMLPATH="migration.xmlFiles.serverXml.access.maxThreads" + AC_SENDREASONPHRASE_YAMLPATH="migration.xmlFiles.serverXml.access.sendReasonPhrase" + AC_EXTRACONFIG_YAMLPATH="migration.xmlFiles.serverXml.access.extraConfig" + RT_PORT_YAMLPATH="migration.xmlFiles.serverXml.artifactory.port" + RT_MAXTHREADS_YAMLPATH="migration.xmlFiles.serverXml.artifactory.maxThreads" + RT_SENDREASONPHRASE_YAMLPATH='migration.xmlFiles.serverXml.artifactory.sendReasonPhrase' + RT_RELAXEDPATHCHARS_YAMLPATH='migration.xmlFiles.serverXml.artifactory.relaxedPathChars' + RT_RELAXEDQUERYCHARS_YAMLPATH='migration.xmlFiles.serverXml.artifactory.relaxedQueryChars' + RT_EXTRACONFIG_YAMLPATH="migration.xmlFiles.serverXml.artifactory.extraConfig" + ROUTER_PORT_YAMLPATH="migration.xmlFiles.serverXml.router.port" + EXTRA_CONFIG_YAMLPATH="migration.xmlFiles.serverXml.extra.config" + EXTRA_LISTENER_CONFIG_YAMLPATH="migration.xmlFiles.serverXml.extra.listener" + RT_TOMCAT_HTTPSCONNECTOR_ENABLED="artifactory.tomcat.httpsConnector.enabled" + + retrieveYamlValue "migration.xmlFiles" "xmlFiles" "Skip" + xmlFiles="${YAML_VALUE}" + if [[ -z "${xmlFiles}" ]]; then + return + fi + bannerSection "PROCESSING MIGRATION OF XML FILES" + retrieveYamlValue "migration.xmlFiles.serverXml.fileName" "fileName" "Warning" + fileName="${YAML_VALUE}" + if [[ -z "${fileName}" ]]; then + return + fi + bannerSubSection "Processing Migration of $fileName" + retrieveYamlValue "migration.xmlFiles.serverXml.filePath" "filePath" "Warning" + filePath="${YAML_VALUE}" + if [[ -z "${filePath}" ]]; then + return + fi + # prepend NEW_DATA_DIR only if filePath is relative path + sourceFilePath=$(prependDir "${filePath}" "${NEW_DATA_DIR}/${filePath}") + if [[ "$(checkFileExists "${sourceFilePath}/${fileName}")" == "true" ]]; then + logger "File [${fileName}] is found in path [${sourceFilePath}]" + local connectorCount=$(getXmlConnectorCount "${sourceFilePath}" "${fileName}") + if [[ "${connectorCount}" == "0" ]]; then + logger "No connectors found in the [${filePath}/${fileName}],skipping migration of xml configuration" + return + fi + local listenerCount=$(getTomcatServerListenersCount "${sourceFilePath}" "${fileName}") + xmlMigrateOperation "${sourceFilePath}" "${fileName}" "${connectorCount}" "${listenerCount}" + else + logger "File [${fileName}] is not found in path [${sourceFilePath}] to migrate" + fi +} + +compareArtifactoryUser () { + local property="$1" + local oldPropertyValue="$2" + local newPropertyValue="$3" + local yamlPath="$4" + local sourceFile="$5" + + if [[ "${oldPropertyValue}" != "${newPropertyValue}" ]]; then + setSystemValue "${yamlPath}" "${oldPropertyValue}" "${SYSTEM_YAML_PATH}" + logger "Setting [${yamlPath}] with value of the property [${property}] in system.yaml" + else + logger "No change in property [${property}] value in [${sourceFile}] to migrate" + fi +} + +migrateReplicator () { + local property="$1" + local oldPropertyValue="$2" + local yamlPath="$3" + + setSystemValue "${yamlPath}" "${oldPropertyValue}" "${SYSTEM_YAML_PATH}" + logger "Setting [${yamlPath}] with value of the property [${property}] in system.yaml" +} + +compareJavaOptions () { + local property="$1" + local oldPropertyValue="$2" + local newPropertyValue="$3" + local yamlPath="$4" + local sourceFile="$5" + local oldJavaOption= + local newJavaOption= + local extraJavaOption= + local check=false + local success=true + local status=true + + oldJavaOption=$(echo "${oldPropertyValue}" | awk 'BEGIN{FS=OFS="\""}{for(i=2;i.+)\.{{ include "artifactory-ha.fullname" . }} {{ include "artifactory-ha.fullname" . }} +{{ tpl (include "artifactory.nginx.hosts" .) . }}; + +if ($http_x_forwarded_proto = '') { + set $http_x_forwarded_proto $scheme; +} +set $host_port {{ .Values.nginx.https.externalPort }}; +if ( $scheme = "http" ) { + set $host_port {{ .Values.nginx.http.externalPort }}; +} +## Application specific logs +## access_log /var/log/nginx/artifactory-access.log timing; +## error_log /var/log/nginx/artifactory-error.log; +rewrite ^/artifactory/?$ / redirect; +if ( $repo != "" ) { + rewrite ^/(v1|v2)/(.*) /artifactory/api/docker/$repo/$1/$2 break; +} +chunked_transfer_encoding on; +client_max_body_size 0; + +location / { + proxy_read_timeout 900; + proxy_pass_header Server; + proxy_cookie_path ~*^/.* /; + proxy_pass {{ include "artifactory-ha.scheme" . }}://{{ include "artifactory-ha.fullname" . }}:{{ .Values.artifactory.externalPort }}/; + {{- if .Values.nginx.service.ssloffload}} + {{- if .Values.nginx.service.ssloffloadForceHttps}} + proxy_set_header X-JFrog-Override-Base-Url https://$host; + proxy_set_header X-Forwarded-Proto https; + {{- else }} + proxy_set_header X-JFrog-Override-Base-Url $http_x_forwarded_proto://$host; + proxy_set_header X-Forwarded-Proto $http_x_forwarded_proto; + {{- end }} + {{- else if or (eq (int .Values.nginx.https.internalPort) 80) (eq (int .Values.nginx.https.externalPort) 443)}} + proxy_set_header X-JFrog-Override-Base-Url $http_x_forwarded_proto://$host; + proxy_set_header X-Forwarded-Port $server_port; + proxy_set_header X-Forwarded-Proto $http_x_forwarded_proto; + {{- else }} + proxy_set_header X-JFrog-Override-Base-Url $http_x_forwarded_proto://$host:$host_port; + proxy_set_header X-Forwarded-Port $server_port; + proxy_set_header X-Forwarded-Proto $http_x_forwarded_proto; + {{- end }} + proxy_set_header Host $http_host; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + {{- if .Values.nginx.disableProxyBuffering}} + proxy_http_version 1.1; + proxy_request_buffering off; + proxy_buffering off; + {{- end }} + add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always; + location /artifactory/ { + {{- if .Values.rtfs.enabled }} + if ($request_uri ~ ^/artifactory/service/rtfs/(.*)$ ) { + proxy_pass http://{{ include "artifactory-ha.fullname" . }}:{{ .Values.artifactory.externalPort }}/artifactory/service/rtfs/$1; + break; + } + {{- end }} + if ( $request_uri ~ ^/artifactory/(.*)$ ) { + proxy_pass http://{{ include "artifactory-ha.fullname" . }}:{{ .Values.artifactory.externalArtifactoryPort }}/artifactory/$1; + } + proxy_pass http://{{ include "artifactory-ha.fullname" . }}:{{ .Values.artifactory.externalArtifactoryPort }}/artifactory/; + } + location /pipelines/ { + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "upgrade"; + proxy_set_header Host $http_host; + {{- if .Values.router.tlsEnabled }} + proxy_pass https://{{ include "artifactory-ha.fullname" . }}:{{ .Values.router.internalPort }}; + {{- else }} + proxy_pass http://{{ include "artifactory-ha.fullname" . }}:{{ .Values.router.internalPort }}; + {{- end }} + } +} +} \ No newline at end of file diff --git a/charts/jfrog/artifactory-ha/107.104.10/files/nginx-main-conf.yaml b/charts/jfrog/artifactory-ha/107.104.10/files/nginx-main-conf.yaml new file mode 100644 index 0000000000..78cecea6a1 --- /dev/null +++ b/charts/jfrog/artifactory-ha/107.104.10/files/nginx-main-conf.yaml @@ -0,0 +1,83 @@ +# Main Nginx configuration file +worker_processes 4; + +{{- if .Values.nginx.logs.stderr }} +error_log stderr {{ .Values.nginx.logs.level }}; +{{- else -}} +error_log {{ .Values.nginx.persistence.mountPath }}/logs/error.log {{ .Values.nginx.logs.level }}; +{{- end }} +pid /var/run/nginx.pid; + +{{- if .Values.artifactory.ssh.enabled }} +## SSH Server Configuration +stream { + server { + {{- if .Values.nginx.singleStackIPv6Cluster }} + listen [::]:{{ .Values.nginx.ssh.internalPort }}; + {{- else -}} + listen {{ .Values.nginx.ssh.internalPort }}; + {{- end }} + proxy_pass {{ include "artifactory-ha.fullname" . }}:{{ .Values.artifactory.ssh.externalPort }}; + } +} +{{- end }} + +events { + worker_connections 1024; +} + +http { + include /etc/nginx/mime.types; + default_type application/octet-stream; + + variables_hash_max_size 1024; + variables_hash_bucket_size 64; + server_names_hash_max_size 4096; + server_names_hash_bucket_size 128; + types_hash_max_size 2048; + types_hash_bucket_size 64; + proxy_read_timeout 2400s; + client_header_timeout 2400s; + client_body_timeout 2400s; + proxy_connect_timeout 75s; + proxy_send_timeout 2400s; + proxy_buffer_size 128k; + proxy_buffers 40 128k; + proxy_busy_buffers_size 128k; + proxy_temp_file_write_size 250m; + proxy_http_version 1.1; + client_body_buffer_size 128k; + + log_format main '$remote_addr - $remote_user [$time_local] "$request" ' + '$status $body_bytes_sent "$http_referer" ' + '"$http_user_agent" "$http_x_forwarded_for"'; + + log_format timing 'ip = $remote_addr ' + 'user = \"$remote_user\" ' + 'local_time = \"$time_local\" ' + 'host = $host ' + 'request = \"$request\" ' + 'status = $status ' + 'bytes = $body_bytes_sent ' + 'upstream = \"$upstream_addr\" ' + 'upstream_time = $upstream_response_time ' + 'request_time = $request_time ' + 'referer = \"$http_referer\" ' + 'UA = \"$http_user_agent\"'; + + {{- if .Values.nginx.logs.stdout }} + access_log /dev/stdout timing; + {{- else -}} + access_log {{ .Values.nginx.persistence.mountPath }}/logs/access.log timing; + {{- end }} + + sendfile on; + #tcp_nopush on; + + keepalive_timeout 65; + + #gzip on; + + include /etc/nginx/conf.d/*.conf; + +} diff --git a/charts/jfrog/artifactory-ha/107.104.10/files/system.yaml b/charts/jfrog/artifactory-ha/107.104.10/files/system.yaml new file mode 100644 index 0000000000..152ac5b9c1 --- /dev/null +++ b/charts/jfrog/artifactory-ha/107.104.10/files/system.yaml @@ -0,0 +1,192 @@ +router: + serviceRegistry: + insecure: {{ .Values.router.serviceRegistry.insecure }} +shared: +{{- if .Values.artifactory.coldStorage.enabled }} + jfrogColdStorage: + coldInstanceEnabled: true +{{- end }} +{{- if .Values.artifactory.worker.enabled }} + featureToggler: + worker: true +{{- end }} +{{- with include "artifactory.metrics" . }} + {{- . | nindent 2 }} +{{- end }} + logging: + consoleLog: + enabled: {{ .Values.artifactory.consoleLog }} + extraJavaOpts: > + -Dartifactory.graceful.shutdown.max.request.duration.millis={{ mul .Values.artifactory.terminationGracePeriodSeconds 1000 }} + -Dartifactory.access.client.max.connections={{ .Values.access.tomcat.connector.maxThreads }} +{{- if .Values.artifactory.worker.enabled }} + -Dartifactory.workers.addon.support=true +{{- end }} + {{- with .Values.artifactory.primary.javaOpts }} + {{- if .corePoolSize }} + -Dartifactory.async.corePoolSize={{ .corePoolSize }} + {{- end }} + {{- if .xms }} + -Xms{{ .xms }} + {{- end }} + {{- if .xmx }} + -Xmx{{ .xmx }} + {{- end }} + {{- if .jmx.enabled }} + -Dcom.sun.management.jmxremote + -Dcom.sun.management.jmxremote.port={{ .jmx.port }} + -Dcom.sun.management.jmxremote.rmi.port={{ .jmx.port }} + -Dcom.sun.management.jmxremote.ssl={{ .jmx.ssl }} + {{- if .jmx.host }} + -Djava.rmi.server.hostname={{ tpl .jmx.host $ }} + {{- else }} + -Djava.rmi.server.hostname={{ template "artifactory-ha.fullname" $ }} + {{- end }} + {{- if .jmx.authenticate }} + -Dcom.sun.management.jmxremote.authenticate=true + -Dcom.sun.management.jmxremote.access.file={{ .jmx.accessFile }} + -Dcom.sun.management.jmxremote.password.file={{ .jmx.passwordFile }} + {{- else }} + -Dcom.sun.management.jmxremote.authenticate=false + {{- end }} + {{- end }} + {{- if .other }} + {{ .other }} + {{- end }} + {{- end }} + database: + allowNonPostgresql: {{ .Values.database.allowNonPostgresql }} + {{- if .Values.postgresql.enabled }} + type: postgresql + url: "jdbc:postgresql://{{ .Release.Name }}-postgresql:{{ .Values.postgresql.service.port }}/{{ .Values.postgresql.postgresqlDatabase }}" + host: "" + driver: org.postgresql.Driver + username: "{{ .Values.postgresql.postgresqlUsername }}" + {{ else }} + type: "{{ .Values.database.type }}" + driver: "{{ .Values.database.driver }}" + {{- end }} +artifactory: +{{- if or .Values.artifactory.haDataDir.enabled .Values.artifactory.haBackupDir.enabled }} + node: + {{- if .Values.artifactory.haDataDir.path }} + haDataDir: {{ .Values.artifactory.haDataDir.path }} + {{- end }} + {{- if .Values.artifactory.haBackupDir.path }} + haBackupDir: {{ .Values.artifactory.haBackupDir.path }} + {{- end }} +{{- end }} + database: + maxOpenConnections: {{ .Values.artifactory.database.maxOpenConnections }} + tomcat: + maintenanceConnector: + port: {{ .Values.artifactory.tomcat.maintenanceConnector.port }} + connector: + maxThreads: {{ .Values.artifactory.tomcat.connector.maxThreads }} + sendReasonPhrase: {{ .Values.artifactory.tomcat.connector.sendReasonPhrase }} + extraConfig: {{ .Values.artifactory.tomcat.connector.extraConfig }} +frontend: + session: + timeMinutes: {{ .Values.frontend.session.timeoutMinutes | quote }} +access: + runOnArtifactoryTomcat: {{ .Values.access.runOnArtifactoryTomcat | default false }} + database: + maxOpenConnections: {{ .Values.access.database.maxOpenConnections }} + {{- if not (.Values.access.runOnArtifactoryTomcat | default false) }} + extraJavaOpts: > + {{- if .Values.splitServicesToContainers }} + -XX:InitialRAMPercentage=20 + -XX:MaxRAMPercentage=70 + {{- end }} + {{- with .Values.access.javaOpts }} + {{- if .other }} + {{ .other }} + {{- end }} + {{- end }} + {{- end }} + tomcat: + connector: + maxThreads: {{ .Values.access.tomcat.connector.maxThreads }} + sendReasonPhrase: {{ .Values.access.tomcat.connector.sendReasonPhrase }} + extraConfig: {{ .Values.access.tomcat.connector.extraConfig }} + {{- if .Values.access.database.enabled }} + type: "{{ .Values.access.database.type }}" + url: "{{ .Values.access.database.url }}" + driver: "{{ .Values.access.database.driver }}" + username: "{{ .Values.access.database.user }}" + password: "{{ .Values.access.database.password }}" + {{- end }} +{{- if .Values.artifactory.worker.enabled }} + worker: + enabled: true +{{- end }} +{{- if .Values.mc.enabled }} +mc: + enabled: true + database: + maxOpenConnections: {{ .Values.mc.database.maxOpenConnections }} + idgenerator: + maxOpenConnections: {{ .Values.mc.idgenerator.maxOpenConnections }} + tomcat: + connector: + maxThreads: {{ .Values.mc.tomcat.connector.maxThreads }} + sendReasonPhrase: {{ .Values.mc.tomcat.connector.sendReasonPhrase }} + extraConfig: {{ .Values.mc.tomcat.connector.extraConfig }} +{{- end }} +metadata: + database: + maxOpenConnections: {{ .Values.metadata.database.maxOpenConnections }} +{{- if and .Values.jfconnect.enabled (not (regexMatch "^.*(oss|cpp-ce|jcr).*$" .Values.artifactory.image.repository)) }} +jfconnect: + enabled: true +{{- else }} +jfconnect: + enabled: false +jfconnect_service: + enabled: false +{{- end }} + +onemodel: + enabled: {{ .Values.onemodel.enabled }} + {{- if .Values.onemodel.apolloYaml }} + apolloYaml: +{{ toYaml .Values.onemodel.apolloYaml | nindent 6 }} + {{- end}} +{{- if and .Values.rtfs.enabled (not (regexMatch "^.*(oss|cpp-ce|jcr).*$" .Values.artifactory.image.repository)) }} +rtfs: + enabled: true +{{- else }} +rtfs: + enabled: false +{{- end }} +{{- if .Values.topology.enabled }} +topology: + enabled: {{ .Values.topology.enabled }} + database: + maxOpenConnections: {{ .Values.topology.database.maxOpenConnections }} + port: {{ .Values.topology.internalPort }} + extraJavaOpts: > + {{- if .Values.splitServicesToContainers }} + -XX:InitialRAMPercentage=20 + -XX:MaxRAMPercentage=70 + {{- end }} + {{- with .Values.topology.javaOpts }} + {{- if .other }} + {{ .other }} + {{- end }} + {{- end }} +{{- else }} +topology: + enabled: {{ .Values.topology.enabled }} +{{- end }} +{{- if .Values.event.webhooks }} +event: + webhooks: {{ toYaml .Values.event.webhooks | nindent 6 }} +{{- end }} +{{- if .Values.evidence.enabled }} +evidence: + enabled: true +{{- else }} +evidence: + enabled: false +{{- end }} \ No newline at end of file diff --git a/charts/jfrog/artifactory-ha/107.104.10/logo/artifactory-logo.png b/charts/jfrog/artifactory-ha/107.104.10/logo/artifactory-logo.png new file mode 100644 index 0000000000000000000000000000000000000000..fe6c23c5a7f87edaf49f883ffa99d875073e2285 GIT binary patch literal 82419 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 z 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 GZv9`RA2Y%L literal 0 HcmV?d00001 diff --git a/charts/jfrog/artifactory-ha/107.104.10/questions.yml b/charts/jfrog/artifactory-ha/107.104.10/questions.yml new file mode 100644 index 0000000000..14e9024e6d --- /dev/null +++ b/charts/jfrog/artifactory-ha/107.104.10/questions.yml @@ -0,0 +1,424 @@ +questions: +# Advance Settings +- variable: artifactory.masterKey + default: "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + description: "Artifactory master key. For security reasons, we strongly recommend you generate your own master key using this command: 'openssl rand -hex 32'" + type: string + label: Artifactory master key + group: "Security Settings" + +# Container Images +- variable: defaultImage + default: true + description: "Use default Docker image" + label: Use Default Image + type: boolean + show_subquestion_if: false + group: "Container Images" + subquestions: + - variable: initContainerImage + default: "docker.bintray.io/alpine:3.12" + description: "Init image name" + type: string + label: Init image name + - variable: artifactory.image.repository + default: "docker.bintray.io/jfrog/artifactory-pro" + description: "Artifactory image name" + type: string + label: Artifactory Image Name + - variable: artifactory.image.version + default: "7.6.3" + description: "Artifactory image tag" + type: string + label: Artifactory Image Tag + - variable: nginx.image.repository + default: "docker.bintray.io/jfrog/nginx-artifactory-pro" + description: "Nginx image name" + type: string + label: Nginx Image Name + - variable: nginx.image.version + default: "7.6.3" + description: "Nginx image tag" + type: string + label: Nginx Image Tag + - variable: imagePullSecrets + description: "Image Pull Secret" + type: string + label: Image Pull Secret + +# Services and LoadBalancing Settings +- variable: artifactory.node.replicaCount + default: "2" + description: "Number of Secondary Nodes" + type: string + label: Number of Secondary Nodes + show_subquestion_if: true + group: "Services and Load Balancing" +- variable: ingress.enabled + default: false + description: "Expose app using Layer 7 Load Balancer - ingress" + type: boolean + label: Expose app using Layer 7 Load Balancer + show_subquestion_if: true + group: "Services and Load Balancing" + required: true + subquestions: + - variable: ingress.hosts[0] + default: "xip.io" + description: "Hostname to your artifactory installation" + type: hostname + required: true + label: Hostname + +# Nginx Settings +- variable: nginx.enabled + default: true + description: "Enable nginx server" + type: boolean + label: Enable Nginx Server + group: "Services and Load Balancing" + required: true + show_if: "ingress.enabled=false" +- variable: nginx.service.type + default: "LoadBalancer" + description: "Nginx service type" + type: enum + required: true + label: Nginx Service Type + show_if: "nginx.enabled=true&&ingress.enabled=false" + group: "Services and Load Balancing" + options: + - "ClusterIP" + - "NodePort" + - "LoadBalancer" +- variable: nginx.service.loadBalancerIP + default: "" + description: "Provide Static IP to configure with Nginx" + type: string + label: Config Nginx LoadBalancer IP + show_if: "nginx.enabled=true&&nginx.service.type=LoadBalancer&&ingress.enabled=false" + group: "Services and Load Balancing" +- variable: nginx.tlsSecretName + default: "" + description: "Provide SSL Secret name to configure with Nginx" + type: string + label: Config Nginx SSL Secret + show_if: "nginx.enabled=true&&ingress.enabled=false" + group: "Services and Load Balancing" +- variable: nginx.customArtifactoryConfigMap + default: "" + description: "Provide configMap name to configure Nginx with custom `artifactory.conf`" + type: string + label: ConfigMap for Nginx Artifactory Config + show_if: "nginx.enabled=true&&ingress.enabled=false" + group: "Services and Load Balancing" + +# Artifactory Storage Settings +- variable: artifactory.persistence.size + default: "50Gi" + description: "Artifactory persistent volume size" + type: string + label: Artifactory Persistent Volume Size + required: true + group: "Artifactory Storage" +- variable: artifactory.persistence.type + default: "file-system" + description: "Artifactory persistent volume size" + type: enum + label: Artifactory Persistent Storage Type + required: true + options: + - "file-system" + - "nfs" + - "google-storage" + - "aws-s3" + group: "Artifactory Storage" + +#Storage Type Settings +- variable: artifactory.persistence.nfs.ip + default: "" + type: string + group: "Artifactory Storage" + label: NFS Server IP + description: "NFS server IP" + show_if: "artifactory.persistence.type=nfs" +- variable: artifactory.persistence.nfs.haDataMount + default: "/data" + type: string + label: NFS Data Directory + description: "NFS data directory" + group: "Artifactory Storage" + show_if: "artifactory.persistence.type=nfs" +- variable: artifactory.persistence.nfs.haBackupMount + default: "/backup" + type: string + label: NFS Backup Directory + description: "NFS backup directory" + group: "Artifactory Storage" + show_if: "artifactory.persistence.type=nfs" +- variable: artifactory.persistence.nfs.dataDir + default: "/var/opt/jfrog/artifactory-ha" + type: string + label: HA Data Directory + description: "HA data directory" + group: "Artifactory Storage" + show_if: "artifactory.persistence.type=nfs" +- variable: artifactory.persistence.nfs.backupDir + default: "/var/opt/jfrog/artifactory-backup" + type: string + label: HA Backup Directory + description: "HA backup directory " + group: "Artifactory Storage" + show_if: "artifactory.persistence.type=nfs" +- variable: artifactory.persistence.nfs.capacity + default: "200Gi" + type: string + label: NFS PVC Size + description: "NFS PVC size " + group: "Artifactory Storage" + show_if: "artifactory.persistence.type=nfs" + +#Google storage settings +- variable: artifactory.persistence.googleStorage.bucketName + default: "artifactory-ha-gcp" + type: string + label: Google Storage Bucket Name + description: "Google storage bucket name" + group: "Artifactory Storage" + show_if: "artifactory.persistence.type=google-storage" +- variable: artifactory.persistence.googleStorage.identity + default: "" + type: string + label: Google Storage Service Account ID + description: "Google Storage service account id" + group: "Artifactory Storage" + show_if: "artifactory.persistence.type=google-storage" +- variable: artifactory.persistence.googleStorage.credential + default: "" + type: string + label: Google Storage Service Account Key + description: "Google Storage service account key" + group: "Artifactory Storage" + show_if: "artifactory.persistence.type=google-storage" +- variable: artifactory.persistence.googleStorage.path + default: "artifactory-ha/filestore" + type: string + label: Google Storage Path In Bucket + description: "Google Storage path in bucket" + group: "Artifactory Storage" + show_if: "artifactory.persistence.type=google-storage" +# awsS3 storage settings +- variable: artifactory.persistence.awsS3.bucketName + default: "artifactory-ha-aws" + type: string + label: AWS S3 Bucket Name + description: "AWS S3 bucket name" + group: "Artifactory Storage" + show_if: "artifactory.persistence.type=aws-s3" +- variable: artifactory.persistence.awsS3.region + default: "" + type: string + label: AWS S3 Bucket Region + description: "AWS S3 bucket region" + group: "Artifactory Storage" + show_if: "artifactory.persistence.type=aws-s3" +- variable: artifactory.persistence.awsS3.identity + default: "" + type: string + label: AWS S3 AWS_ACCESS_KEY_ID + description: "AWS S3 AWS_ACCESS_KEY_ID" + group: "Artifactory Storage" + show_if: "artifactory.persistence.type=aws-s3" +- variable: artifactory.persistence.awsS3.credential + default: "" + type: string + label: AWS S3 AWS_SECRET_ACCESS_KEY + description: "AWS S3 AWS_SECRET_ACCESS_KEY" + group: "Artifactory Storage" + show_if: "artifactory.persistence.type=aws-s3" +- variable: artifactory.persistence.awsS3.path + default: "artifactory-ha/filestore" + type: string + label: AWS S3 Path In Bucket + description: "AWS S3 path in bucket" + group: "Artifactory Storage" + show_if: "artifactory.persistence.type=aws-s3" + +# Database Settings +- variable: postgresql.enabled + default: true + description: "Enable PostgreSQL" + type: boolean + required: true + label: Enable PostgreSQL + group: "Database Settings" + show_subquestion_if: true + subquestions: + - variable: postgresql.postgresqlPassword + default: "" + description: "PostgreSQL password" + type: password + required: true + label: PostgreSQL Password + group: "Database Settings" + show_if: "postgresql.enabled=true" + - variable: postgresql.persistence.size + default: 20Gi + description: "PostgreSQL persistent volume size" + type: string + label: PostgreSQL Persistent Volume Size + show_if: "postgresql.enabled=true" + - variable: postgresql.persistence.storageClass + default: "" + description: "If undefined or null, uses the default StorageClass. Default to null" + type: storageclass + label: Default StorageClass for PostgreSQL + show_if: "postgresql.enabled=true" + - variable: postgresql.resources.requests.cpu + default: "200m" + description: "PostgreSQL initial cpu request" + type: string + label: PostgreSQL Initial CPU Request + show_if: "postgresql.enabled=true" + - variable: postgresql.resources.requests.memory + default: "500Mi" + description: "PostgreSQL initial memory request" + type: string + label: PostgreSQL Initial Memory Request + show_if: "postgresql.enabled=true" + - variable: postgresql.resources.limits.cpu + default: "1" + description: "PostgreSQL cpu limit" + type: string + label: PostgreSQL CPU Limit + show_if: "postgresql.enabled=true" + - variable: postgresql.resources.limits.memory + default: "1Gi" + description: "PostgreSQL memory limit" + type: string + label: PostgreSQL Memory Limit + show_if: "postgresql.enabled=true" +- variable: database.type + default: "postgresql" + description: "xternal database type (postgresql, mysql, oracle or mssql)" + type: enum + required: true + label: External Database Type + group: "Database Settings" + show_if: "postgresql.enabled=false" + options: + - "postgresql" + - "mysql" + - "oracle" + - "mssql" +- variable: database.url + default: "" + description: "External database URL. If you set the url, leave host and port empty" + type: string + label: External Database URL + group: "Database Settings" + show_if: "postgresql.enabled=false" +- variable: database.host + default: "" + description: "External database hostname" + type: string + label: External Database Hostname + group: "Database Settings" + show_if: "postgresql.enabled=false" +- variable: database.port + default: "" + description: "External database port" + type: string + label: External Database Port + group: "Database Settings" + show_if: "postgresql.enabled=false" +- variable: database.user + default: "" + description: "External database username" + type: string + label: External Database Username + group: "Database Settings" + show_if: "postgresql.enabled=false" +- variable: database.password + default: "" + description: "External database password" + type: password + label: External Database Password + group: "Database Settings" + show_if: "postgresql.enabled=false" + +# Advance Settings +- variable: advancedOptions + default: false + description: "Show advanced configurations" + label: Show Advanced Configurations + type: boolean + show_subquestion_if: true + group: "Advanced Options" + subquestions: + - variable: artifactory.primary.resources.requests.cpu + default: "500m" + description: "Artifactory primary node initial cpu request" + type: string + label: Artifactory Primary Node Initial CPU Request + - variable: artifactory.primary.resources.requests.memory + default: "1Gi" + description: "Artifactory primary node initial memory request" + type: string + label: Artifactory Primary Node Initial Memory Request + - variable: artifactory.primary.javaOpts.xms + default: "1g" + description: "Artifactory primary node java Xms size" + type: string + label: Artifactory Primary Node Java Xms Size + - variable: artifactory.primary.resources.limits.cpu + default: "2" + description: "Artifactory primary node cpu limit" + type: string + label: Artifactory Primary Node CPU Limit + - variable: artifactory.primary.resources.limits.memory + default: "4Gi" + description: "Artifactory primary node memory limit" + type: string + label: Artifactory Primary Node Memory Limit + - variable: artifactory.primary.javaOpts.xmx + default: "4g" + description: "Artifactory primary node java Xmx size" + type: string + label: Artifactory Primary Node Java Xmx Size + - variable: artifactory.node.resources.requests.cpu + default: "500m" + description: "Artifactory member node initial cpu request" + type: string + label: Artifactory Member Node Initial CPU Request + - variable: artifactory.node.resources.requests.memory + default: "2Gi" + description: "Artifactory member node initial memory request" + type: string + label: Artifactory Member Node Initial Memory Request + - variable: artifactory.node.javaOpts.xms + default: "1g" + description: "Artifactory member node java Xms size" + type: string + label: Artifactory Member Node Java Xms Size + - variable: artifactory.node.resources.limits.cpu + default: "2" + description: "Artifactory member node cpu limit" + type: string + label: Artifactory Member Node CPU Limit + - variable: artifactory.node.resources.limits.memory + default: "4Gi" + description: "Artifactory member node memory limit" + type: string + label: Artifactory Member Node Memory Limit + - variable: artifactory.node.javaOpts.xmx + default: "4g" + description: "Artifactory member node java Xmx size" + type: string + label: Artifactory Member Node Java Xmx Size + +# Internal Settings +- variable: installerInfo + default: '\{\"productId\": \"RancherHelm_artifactory-ha/7.17.5\", \"features\": \[\{\"featureId\": \"Partner/ACC-007246\"\}\]\}' + type: string + group: "Internal Settings (Do not modify)" diff --git a/charts/jfrog/artifactory-ha/107.104.10/sizing/artifactory-2xlarge.yaml b/charts/jfrog/artifactory-ha/107.104.10/sizing/artifactory-2xlarge.yaml new file mode 100644 index 0000000000..92953faec4 --- /dev/null +++ b/charts/jfrog/artifactory-ha/107.104.10/sizing/artifactory-2xlarge.yaml @@ -0,0 +1,185 @@ +############################################################################################## +# The 2xlarge sizing +# This size is intended for very large organizations. It can be increased with adding replicas +# [WARNING] Some of the configuration mentioned in this file are taken inside system.yaml +# hence this configuration will be overridden when enabling systemYamlOverride +############################################################################################## +splitServicesToContainers: true +artifactory: + primary: + # Enterprise and above licenses are required for setting replicaCount greater than 1. + # Count should be equal or above the total number of licenses available for artifactory. + replicaCount: 6 + + # Require multiple Artifactory pods to run on separate nodes + podAntiAffinity: + type: "hard" + + resources: + requests: + cpu: "4" + memory: 20Gi + limits: + # cpu: "20" + memory: 24Gi + + javaOpts: + other: > + -XX:InitialRAMPercentage=40 + -XX:MaxRAMPercentage=70 + -Dartifactory.async.corePoolSize=200 + -Dartifactory.async.poolMaxQueueSize=100000 + -Dartifactory.http.client.max.total.connections=150 + -Dartifactory.http.client.max.connections.per.route=150 + -Dartifactory.access.client.max.connections=200 + -Dartifactory.metadata.event.operator.threads=5 + -XX:MaxMetaspaceSize=512m + -Djdk.nio.maxCachedBufferSize=1048576 + -XX:MaxDirectMemorySize=1024m + + tomcat: + connector: + maxThreads: 800 + extraConfig: 'acceptCount="1200" acceptorThreadCount="2" compression="off" connectionLinger="-1" connectionTimeout="120000" enableLookups="false"' + + database: + maxOpenConnections: 200 + + extraEnvironmentVariables: + - name: MALLOC_ARENA_MAX + value: "16" + - name: SKIP_WAIT_FOR_EXTERNAL_DB + value: "true" + +router: + resources: + requests: + cpu: "2" + memory: 2Gi + limits: + # cpu: "12" + memory: 4Gi + +frontend: + resources: + requests: + cpu: "1" + memory: 500Mi + limits: + # cpu: "5" + memory: 1Gi + +metadata: + database: + maxOpenConnections: 200 + resources: + requests: + cpu: "1" + memory: 500Mi + limits: + # cpu: "5" + memory: 2Gi + +onemodel: + resources: + requests: + cpu: 225m + memory: 180Mi + limits: + # cpu: "1" + memory: 250Mi + +event: + resources: + requests: + cpu: 200m + memory: 100Mi + limits: + # cpu: "1" + memory: 500Mi + +access: + tomcat: + connector: + maxThreads: 200 + javaOpts: + other: > + -XX:InitialRAMPercentage=20 + -XX:MaxRAMPercentage=60 + database: + maxOpenConnections: 200 + resources: + requests: + cpu: 1 + memory: 2Gi + limits: + # cpu: 2 + memory: 4Gi + extraEnvironmentVariables: + - name: MALLOC_ARENA_MAX + value: "16" + - name: SKIP_WAIT_FOR_EXTERNAL_DB + value: "true" + +observability: + resources: + requests: + cpu: 200m + memory: 100Mi + limits: + # cpu: "1" + memory: 500Mi + +jfconnect: + resources: + requests: + cpu: 100m + memory: 100Mi + limits: + # cpu: "1" + memory: 250Mi + +topology: + resources: + requests: + cpu: 100m + memory: 1000Mi + limits: + memory: 1500Mi + extraEnvironmentVariables: + - name: MALLOC_ARENA_MAX + value: "8" + +nginx: + replicaCount: 3 + disableProxyBuffering: true + resources: + requests: + cpu: "4" + memory: "6Gi" + limits: + # cpu: "14" + memory: "8Gi" + +postgresql: + postgresqlExtendedConf: + maxConnections: "5000" + primary: + affinity: + # Require PostgreSQL pod to run on a different node than Artifactory pods + podAntiAffinity: + requiredDuringSchedulingIgnoredDuringExecution: + - labelSelector: + matchExpressions: + - key: app + operator: In + values: + - artifactory + topologyKey: kubernetes.io/hostname + resources: + requests: + memory: 256Gi + cpu: "64" + limits: + memory: 256Gi + # cpu: "128" diff --git a/charts/jfrog/artifactory-ha/107.104.10/sizing/artifactory-large.yaml b/charts/jfrog/artifactory-ha/107.104.10/sizing/artifactory-large.yaml new file mode 100644 index 0000000000..e614248541 --- /dev/null +++ b/charts/jfrog/artifactory-ha/107.104.10/sizing/artifactory-large.yaml @@ -0,0 +1,185 @@ +############################################################################################## +# The large sizing +# This size is intended for large organizations. It can be increased with adding replicas or moving to the large sizing +# [WARNING] Some of the configuration mentioned in this file are taken inside system.yaml +# hence this configuration will be overridden when enabling systemYamlOverride +############################################################################################## +splitServicesToContainers: true +artifactory: + primary: + # Enterprise and above licenses are required for setting replicaCount greater than 1. + # Count should be equal or above the total number of licenses available for artifactory. + replicaCount: 3 + + # Require multiple Artifactory pods to run on separate nodes + podAntiAffinity: + type: "hard" + + resources: + requests: + cpu: "2" + memory: 10Gi + limits: + # cpu: "14" + memory: 12Gi + + javaOpts: + other: > + -XX:InitialRAMPercentage=40 + -XX:MaxRAMPercentage=65 + -Dartifactory.async.corePoolSize=80 + -Dartifactory.async.poolMaxQueueSize=20000 + -Dartifactory.http.client.max.total.connections=100 + -Dartifactory.http.client.max.connections.per.route=100 + -Dartifactory.access.client.max.connections=125 + -Dartifactory.metadata.event.operator.threads=4 + -XX:MaxMetaspaceSize=512m + -Djdk.nio.maxCachedBufferSize=524288 + -XX:MaxDirectMemorySize=512m + + tomcat: + connector: + maxThreads: 500 + extraConfig: 'acceptCount="800" acceptorThreadCount="2" compression="off" connectionLinger="-1" connectionTimeout="120000" enableLookups="false"' + + database: + maxOpenConnections: 100 + + extraEnvironmentVariables: + - name: MALLOC_ARENA_MAX + value: "8" + - name: SKIP_WAIT_FOR_EXTERNAL_DB + value: "true" + +access: + tomcat: + connector: + maxThreads: 125 + javaOpts: + other: > + -XX:InitialRAMPercentage=20 + -XX:MaxRAMPercentage=60 + database: + maxOpenConnections: 100 + resources: + requests: + cpu: 1 + memory: 2Gi + limits: + # cpu: 2 + memory: 3Gi + extraEnvironmentVariables: + - name: MALLOC_ARENA_MAX + value: "8" + - name: SKIP_WAIT_FOR_EXTERNAL_DB + value: "true" + +router: + resources: + requests: + cpu: 400m + memory: 800Mi + limits: + # cpu: "8" + memory: 2Gi + +frontend: + resources: + requests: + cpu: 200m + memory: 300Mi + limits: + # cpu: "3" + memory: 1Gi + +metadata: + database: + maxOpenConnections: 100 + resources: + requests: + cpu: 200m + memory: 200Mi + limits: + # cpu: "4" + memory: 1Gi + +onemodel: + resources: + requests: + cpu: 200m + memory: 160Mi + limits: + # cpu: "1" + memory: 250Mi + +event: + resources: + requests: + cpu: 100m + memory: 50Mi + limits: + # cpu: 500m + memory: 250Mi + +observability: + resources: + requests: + cpu: 100m + memory: 50Mi + limits: + # cpu: 500m + memory: 250Mi + +jfconnect: + resources: + requests: + cpu: 50m + memory: 100Mi + limits: + # cpu: 500m + memory: 250Mi + +topology: + resources: + requests: + cpu: 100m + memory: 1000Mi + limits: + memory: 1500Mi + extraEnvironmentVariables: + - name: MALLOC_ARENA_MAX + value: "8" + +nginx: + replicaCount: 2 + disableProxyBuffering: true + resources: + requests: + cpu: "1" + memory: "500Mi" + limits: + # cpu: "4" + memory: "1Gi" + +postgresql: + postgresqlExtendedConf: + maxConnections: "600" + primary: + affinity: + # Require PostgreSQL pod to run on a different node than Artifactory pods + podAntiAffinity: + requiredDuringSchedulingIgnoredDuringExecution: + - labelSelector: + matchExpressions: + - key: app + operator: In + values: + - artifactory + topologyKey: kubernetes.io/hostname + resources: + requests: + memory: 64Gi + cpu: "16" + limits: + memory: 64Gi + # cpu: "32" diff --git a/charts/jfrog/artifactory-ha/107.104.10/sizing/artifactory-medium.yaml b/charts/jfrog/artifactory-ha/107.104.10/sizing/artifactory-medium.yaml new file mode 100644 index 0000000000..2ace23f00d --- /dev/null +++ b/charts/jfrog/artifactory-ha/107.104.10/sizing/artifactory-medium.yaml @@ -0,0 +1,185 @@ +#################################################################################### +# The medium sizing +# This size is just 2 replicas of the small size. Vertical sizing of all services is not changed +# [WARNING] Some of the configuration mentioned in this file are taken inside system.yaml +# hence this configuration will be overridden when enabling systemYamlOverride +#################################################################################### +splitServicesToContainers: true +artifactory: + primary: + # Enterprise and above licenses are required for setting replicaCount greater than 1. + # Count should be equal or above the total number of licenses available for artifactory. + replicaCount: 2 + + # Require multiple Artifactory pods to run on separate nodes + podAntiAffinity: + type: "hard" + + resources: + requests: + cpu: "1" + memory: 4Gi + limits: + # cpu: "10" + memory: 5Gi + + javaOpts: + other: > + -XX:InitialRAMPercentage=40 + -XX:MaxRAMPercentage=70 + -Dartifactory.async.corePoolSize=40 + -Dartifactory.async.poolMaxQueueSize=10000 + -Dartifactory.http.client.max.total.connections=50 + -Dartifactory.http.client.max.connections.per.route=50 + -Dartifactory.access.client.max.connections=75 + -Dartifactory.metadata.event.operator.threads=3 + -XX:MaxMetaspaceSize=512m + -Djdk.nio.maxCachedBufferSize=262144 + -XX:MaxDirectMemorySize=256m + + tomcat: + connector: + maxThreads: 300 + extraConfig: 'acceptCount="600" acceptorThreadCount="2" compression="off" connectionLinger="-1" connectionTimeout="120000" enableLookups="false"' + + database: + maxOpenConnections: 50 + + extraEnvironmentVariables: + - name: MALLOC_ARENA_MAX + value: "2" + - name: SKIP_WAIT_FOR_EXTERNAL_DB + value: "true" + +router: + resources: + requests: + cpu: 200m + memory: 500Mi + limits: + # cpu: "2" + memory: 1Gi + +frontend: + resources: + requests: + cpu: 100m + memory: 150Mi + limits: + # cpu: "2" + memory: 250Mi + +metadata: + database: + maxOpenConnections: 50 + resources: + requests: + cpu: 100m + memory: 200Mi + limits: + # cpu: "2" + memory: 1Gi + +onemodel: + resources: + requests: + cpu: 175m + memory: 140Mi + limits: + # cpu: "2" + memory: 1Gi + +event: + resources: + requests: + cpu: 100m + memory: 50Mi + limits: + # cpu: 500m + memory: 250Mi + +access: + tomcat: + connector: + maxThreads: 75 + javaOpts: + other: > + -XX:InitialRAMPercentage=20 + -XX:MaxRAMPercentage=60 + database: + maxOpenConnections: 50 + resources: + requests: + cpu: 1 + memory: 1.5Gi + limits: + # cpu: 1.5 + memory: 2Gi + extraEnvironmentVariables: + - name: MALLOC_ARENA_MAX + value: "2" + - name: SKIP_WAIT_FOR_EXTERNAL_DB + value: "true" + +observability: + resources: + requests: + cpu: 100m + memory: 50Mi + limits: + # cpu: 500m + memory: 250Mi + +jfconnect: + resources: + requests: + cpu: 50m + memory: 50Mi + limits: + # cpu: 500m + memory: 250Mi + +topology: + resources: + requests: + cpu: 100m + memory: 600Mi + limits: + memory: 900Mi + extraEnvironmentVariables: + - name: MALLOC_ARENA_MAX + value: "2" + +nginx: + replicaCount: 2 + disableProxyBuffering: true + resources: + requests: + cpu: "100m" + memory: "100Mi" + limits: + # cpu: "2" + memory: "500Mi" + +postgresql: + postgresqlExtendedConf: + maxConnections: "200" + primary: + affinity: + # Require PostgreSQL pod to run on a different node than Artifactory pods + podAntiAffinity: + requiredDuringSchedulingIgnoredDuringExecution: + - labelSelector: + matchExpressions: + - key: app + operator: In + values: + - artifactory + topologyKey: kubernetes.io/hostname + resources: + requests: + memory: 32Gi + cpu: "8" + limits: + memory: 32Gi + # cpu: "16" \ No newline at end of file diff --git a/charts/jfrog/artifactory-ha/107.104.10/sizing/artifactory-small.yaml b/charts/jfrog/artifactory-ha/107.104.10/sizing/artifactory-small.yaml new file mode 100644 index 0000000000..2900cb0801 --- /dev/null +++ b/charts/jfrog/artifactory-ha/107.104.10/sizing/artifactory-small.yaml @@ -0,0 +1,184 @@ +############################################################################################## +# The small sizing +# This is the size recommended for running Artifactory for small teams +# [WARNING] Some of the configuration mentioned in this file are taken inside system.yaml +# hence this configuration will be overridden when enabling systemYamlOverride +############################################################################################## +splitServicesToContainers: true +artifactory: + primary: + # Enterprise and above licenses are required for setting replicaCount greater than 1. + # Count should be equal or above the total number of licenses available for artifactory. + replicaCount: 1 + + # Require multiple Artifactory pods to run on separate nodes + podAntiAffinity: + type: "hard" + + resources: + requests: + cpu: "1" + memory: 4Gi + limits: + # cpu: "10" + memory: 5Gi + + javaOpts: + other: > + -XX:InitialRAMPercentage=40 + -XX:MaxRAMPercentage=70 + -Dartifactory.async.corePoolSize=40 + -Dartifactory.async.poolMaxQueueSize=10000 + -Dartifactory.http.client.max.total.connections=50 + -Dartifactory.http.client.max.connections.per.route=50 + -Dartifactory.access.client.max.connections=75 + -Dartifactory.metadata.event.operator.threads=3 + -XX:MaxMetaspaceSize=512m + -Djdk.nio.maxCachedBufferSize=262144 + -XX:MaxDirectMemorySize=256m + tomcat: + connector: + maxThreads: 300 + extraConfig: 'acceptCount="600" acceptorThreadCount="2" compression="off" connectionLinger="-1" connectionTimeout="120000" enableLookups="false"' + + database: + maxOpenConnections: 50 + + extraEnvironmentVariables: + - name: MALLOC_ARENA_MAX + value: "2" + - name: SKIP_WAIT_FOR_EXTERNAL_DB + value: "true" + +router: + resources: + requests: + cpu: 200m + memory: 500Mi + limits: + # cpu: "2" + memory: 1Gi + +access: + tomcat: + connector: + maxThreads: 75 + javaOpts: + other: > + -XX:InitialRAMPercentage=20 + -XX:MaxRAMPercentage=60 + database: + maxOpenConnections: 50 + resources: + requests: + cpu: 500m + memory: 1.5Gi + limits: + # cpu: 1 + memory: 2Gi + extraEnvironmentVariables: + - name: MALLOC_ARENA_MAX + value: "2" + - name: SKIP_WAIT_FOR_EXTERNAL_DB + value: "true" + +frontend: + resources: + requests: + cpu: 100m + memory: 150Mi + limits: + # cpu: "2" + memory: 250Mi + +metadata: + database: + maxOpenConnections: 50 + resources: + requests: + cpu: 100m + memory: 200Mi + limits: + # cpu: "2" + memory: 1Gi + +onemodel: + resources: + requests: + cpu: 125m + memory: 120Mi + limits: + # cpu: "2" + memory: 1Gi + +event: + resources: + requests: + cpu: 100m + memory: 50Mi + limits: + # cpu: 500m + memory: 250Mi + +observability: + resources: + requests: + cpu: 100m + memory: 50Mi + limits: + # cpu: 500m + memory: 250Mi + +jfconnect: + resources: + requests: + cpu: 50m + memory: 50Mi + limits: + # cpu: 500m + memory: 250Mi + +topology: + resources: + requests: + cpu: 50m + memory: 600Mi + limits: + memory: 900Mi + extraEnvironmentVariables: + - name: MALLOC_ARENA_MAX + value: "2" + +nginx: + replicaCount: 1 + disableProxyBuffering: true + resources: + requests: + cpu: "100m" + memory: "100Mi" + limits: + # cpu: "2" + memory: "500Mi" + +postgresql: + postgresqlExtendedConf: + maxConnections: "100" + primary: + affinity: + # Require PostgreSQL pod to run on a different node than Artifactory pods + podAntiAffinity: + requiredDuringSchedulingIgnoredDuringExecution: + - labelSelector: + matchExpressions: + - key: app + operator: In + values: + - artifactory + topologyKey: kubernetes.io/hostname + resources: + requests: + memory: 16Gi + cpu: "4" + limits: + memory: 16Gi + # cpu: "10" diff --git a/charts/jfrog/artifactory-ha/107.104.10/sizing/artifactory-xlarge.yaml b/charts/jfrog/artifactory-ha/107.104.10/sizing/artifactory-xlarge.yaml new file mode 100644 index 0000000000..140a3903e7 --- /dev/null +++ b/charts/jfrog/artifactory-ha/107.104.10/sizing/artifactory-xlarge.yaml @@ -0,0 +1,184 @@ +############################################################################################## +# The xlarge sizing +# This size is intended for very large organizations. It can be increased with adding replicas +# [WARNING] Some of the configuration mentioned in this file are taken inside system.yaml +# hence this configuration will be overridden when enabling systemYamlOverride +############################################################################################## +splitServicesToContainers: true +artifactory: + primary: + # Enterprise and above licenses are required for setting replicaCount greater than 1. + # Count should be equal or above the total number of licenses available for artifactory. + replicaCount: 4 + + podAntiAffinity: + type: "hard" + + resources: + requests: + cpu: "2" + memory: 14Gi + limits: + # cpu: "14" + memory: 16Gi + + javaOpts: + other: > + -XX:InitialRAMPercentage=40 + -XX:MaxRAMPercentage=65 + -Dartifactory.async.corePoolSize=160 + -Dartifactory.async.poolMaxQueueSize=50000 + -Dartifactory.http.client.max.total.connections=150 + -Dartifactory.http.client.max.connections.per.route=150 + -Dartifactory.access.client.max.connections=150 + -Dartifactory.metadata.event.operator.threads=5 + -XX:MaxMetaspaceSize=512m + -Djdk.nio.maxCachedBufferSize=1048576 + -XX:MaxDirectMemorySize=1024m + tomcat: + connector: + maxThreads: 600 + extraConfig: 'acceptCount="1200" acceptorThreadCount="2" compression="off" connectionLinger="-1" connectionTimeout="120000" enableLookups="false"' + + database: + maxOpenConnections: 150 + # Require multiple Artifactory pods to run on separate nodes + + extraEnvironmentVariables: + - name: MALLOC_ARENA_MAX + value: "16" + - name: SKIP_WAIT_FOR_EXTERNAL_DB + value: "true" + +access: + tomcat: + connector: + maxThreads: 150 + javaOpts: + other: > + -XX:InitialRAMPercentage=20 + -XX:MaxRAMPercentage=60 + database: + maxOpenConnections: 150 + resources: + requests: + cpu: 1 + memory: 2Gi + limits: + # cpu: 2 + memory: 4Gi + extraEnvironmentVariables: + - name: MALLOC_ARENA_MAX + value: "16" + - name: SKIP_WAIT_FOR_EXTERNAL_DB + value: "true" + +router: + resources: + requests: + cpu: 400m + memory: 1Gi + limits: + # cpu: "8" + memory: 2Gi + +frontend: + resources: + requests: + cpu: 200m + memory: 300Mi + limits: + # cpu: "3" + memory: 1Gi + +metadata: + database: + maxOpenConnections: 150 + resources: + requests: + cpu: 200m + memory: 200Mi + limits: + # cpu: "4" + memory: 1Gi + +onemodel: + resources: + requests: + cpu: 250m + memory: 200Mi + limits: + # cpu: "4" + memory: 1Gi + +event: + resources: + requests: + cpu: 100m + memory: 50Mi + limits: + # cpu: 500m + memory: 250Mi + +observability: + resources: + requests: + cpu: 100m + memory: 50Mi + limits: + # cpu: 500m + memory: 250Mi + +jfconnect: + resources: + requests: + cpu: 50m + memory: 100Mi + limits: + # cpu: 500m + memory: 250Mi + +topology: + resources: + requests: + cpu: 100m + memory: 1000Mi + limits: + memory: 1500Mi + extraEnvironmentVariables: + - name: MALLOC_ARENA_MAX + value: "8" + +nginx: + replicaCount: 2 + disableProxyBuffering: true + resources: + requests: + cpu: "4" + memory: "4Gi" + limits: + # cpu: "12" + memory: "8Gi" + +postgresql: + postgresqlExtendedConf: + maxConnections: "2000" + primary: + affinity: + # Require PostgreSQL pod to run on a different node than Artifactory pods + podAntiAffinity: + requiredDuringSchedulingIgnoredDuringExecution: + - labelSelector: + matchExpressions: + - key: app + operator: In + values: + - artifactory + topologyKey: kubernetes.io/hostname + resources: + requests: + memory: 128Gi + cpu: "32" + limits: + memory: 128Gi + # cpu: "64" diff --git a/charts/jfrog/artifactory-ha/107.104.10/sizing/artifactory-xsmall.yaml b/charts/jfrog/artifactory-ha/107.104.10/sizing/artifactory-xsmall.yaml new file mode 100644 index 0000000000..f10e78b5df --- /dev/null +++ b/charts/jfrog/artifactory-ha/107.104.10/sizing/artifactory-xsmall.yaml @@ -0,0 +1,186 @@ +############################################################################################## + +# The xsmall sizing +# This is the minimum size recommended for running Artifactory +# [WARNING] Some of the configuration mentioned in this file are taken inside system.yaml +# hence this configuration will be overridden when enabling systemYamlOverride +############################################################################################## +splitServicesToContainers: true +artifactory: + primary: + # Enterprise and above licenses are required for setting replicaCount greater than 1. + # Count should be equal or above the total number of licenses available for artifactory. + replicaCount: 1 + + # Require multiple Artifactory pods to run on separate nodes + podAntiAffinity: + type: "hard" + + resources: + requests: + cpu: "1" + memory: 3Gi + limits: + # cpu: "10" + memory: 4Gi + + javaOpts: + other: > + -XX:InitialRAMPercentage=40 + -XX:MaxRAMPercentage=70 + -Dartifactory.async.corePoolSize=10 + -Dartifactory.async.poolMaxQueueSize=2000 + -Dartifactory.http.client.max.total.connections=20 + -Dartifactory.http.client.max.connections.per.route=20 + -Dartifactory.access.client.max.connections=15 + -Dartifactory.metadata.event.operator.threads=2 + -XX:MaxMetaspaceSize=400m + -XX:CompressedClassSpaceSize=96m + -Djdk.nio.maxCachedBufferSize=131072 + -XX:MaxDirectMemorySize=128m + tomcat: + connector: + maxThreads: 50 + extraConfig: 'acceptCount="200" acceptorThreadCount="2" compression="off" connectionLinger="-1" connectionTimeout="120000" enableLookups="false"' + + database: + maxOpenConnections: 15 + + extraEnvironmentVariables: + - name: MALLOC_ARENA_MAX + value: "2" + - name: SKIP_WAIT_FOR_EXTERNAL_DB + value: "true" + +access: + tomcat: + connector: + maxThreads: 15 + javaOpts: + other: > + -XX:InitialRAMPercentage=20 + -XX:MaxRAMPercentage=60 + database: + maxOpenConnections: 15 + resources: + requests: + cpu: 500m + memory: 1.5Gi + limits: + # cpu: 1 + memory: 2Gi + extraEnvironmentVariables: + - name: MALLOC_ARENA_MAX + value: "2" + - name: SKIP_WAIT_FOR_EXTERNAL_DB + value: "true" + +router: + resources: + requests: + cpu: 100m + memory: 200Mi + limits: + # cpu: "2" + memory: 1Gi + +frontend: + resources: + requests: + cpu: 50m + memory: 150Mi + limits: + # cpu: "2" + memory: 250Mi + +metadata: + database: + maxOpenConnections: 15 + resources: + requests: + cpu: 50m + memory: 100Mi + limits: + # cpu: "2" + memory: 1Gi + +onemodel: + resources: + requests: + cpu: 125m + memory: 100Mi + limits: + # cpu: "2" + memory: 500Mi + +event: + resources: + requests: + cpu: 50m + memory: 50Mi + limits: + # cpu: 500m + memory: 250Mi + +observability: + resources: + requests: + cpu: 50m + memory: 50Mi + limits: + # cpu: 500m + memory: 250Mi + +jfconnect: + resources: + requests: + cpu: 50m + memory: 50Mi + limits: + # cpu: 500m + memory: 250Mi + +topology: + resources: + requests: + cpu: 50m + memory: 500Mi + limits: + memory: 800Mi + extraEnvironmentVariables: + - name: MALLOC_ARENA_MAX + value: "2" + +nginx: + replicaCount: 1 + disableProxyBuffering: true + resources: + requests: + cpu: "50m" + memory: "50Mi" + limits: + # cpu: "1" + memory: "250Mi" + +postgresql: + postgresqlExtendedConf: + maxConnections: "50" + primary: + affinity: + # Require PostgreSQL pod to run on a different node than Artifactory pods + podAntiAffinity: + requiredDuringSchedulingIgnoredDuringExecution: + - labelSelector: + matchExpressions: + - key: app + operator: In + values: + - artifactory + topologyKey: kubernetes.io/hostname + resources: + requests: + memory: 8Gi + cpu: "2" + limits: + memory: 8Gi + # cpu: "8" \ No newline at end of file diff --git a/charts/jfrog/artifactory-ha/107.104.10/templates/NOTES.txt b/charts/jfrog/artifactory-ha/107.104.10/templates/NOTES.txt new file mode 100644 index 0000000000..30dfab8b8a --- /dev/null +++ b/charts/jfrog/artifactory-ha/107.104.10/templates/NOTES.txt @@ -0,0 +1,149 @@ +Congratulations. You have just deployed JFrog Artifactory HA! + +{{- if .Values.artifactory.masterKey }} +{{- if and (not .Values.artifactory.masterKeySecretName) (eq .Values.artifactory.masterKey "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF") }} + + +***************************************** WARNING ****************************************** +* Your Artifactory master key is still set to the provided example: * +* artifactory.masterKey=FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF * +* * +* You should change this to your own generated key: * +* $ export MASTER_KEY=$(openssl rand -hex 32) * +* $ echo ${MASTER_KEY} * +* * +* Pass the created master key to helm with '--set artifactory.masterKey=${MASTER_KEY}' * +* * +* Alternatively, you can use a pre-existing secret with a key called master-key with * +* '--set artifactory.masterKeySecretName=${SECRET_NAME}' * +******************************************************************************************** +{{- end }} +{{- end }} + +{{- if .Values.artifactory.joinKey }} +{{- if eq .Values.artifactory.joinKey "EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE" }} + + +***************************************** WARNING ****************************************** +* Your Artifactory join key is still set to the provided example: * +* artifactory.joinKey=EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE * +* * +* You should change this to your own generated key: * +* $ export JOIN_KEY=$(openssl rand -hex 32) * +* $ echo ${JOIN_KEY} * +* * +* Pass the created master key to helm with '--set artifactory.joinKey=${JOIN_KEY}' * +* * +******************************************************************************************** +{{- end }} +{{- end }} + + +{{- if .Values.artifactory.setSecurityContext }} +****************************************** WARNING ********************************************** +* From chart version 107.84.x, `setSecurityContext` has been renamed to `podSecurityContext`, * + please change your values.yaml before upgrade , For more Info , refer to 107.84.x changelog * +************************************************************************************************* +{{- end }} + +{{- if and (or (or (or (or (or ( or ( or ( or (or (or ( or (or .Values.artifactory.masterKeySecretName .Values.global.masterKeySecretName) .Values.systemYamlOverride.existingSecret) (or .Values.artifactory.customCertificates.enabled .Values.global.customCertificates.enabled)) .Values.aws.licenseConfigSecretName) .Values.artifactory.persistence.customBinarystoreXmlSecret) .Values.access.customCertificatesSecretName) .Values.systemYamlOverride.existingSecret) .Values.artifactory.license.secret) .Values.artifactory.userPluginSecrets) (and .Values.artifactory.admin.secret .Values.artifactory.admin.dataKey)) (and .Values.artifactory.persistence.googleStorage.gcpServiceAccount.enabled .Values.artifactory.persistence.googleStorage.gcpServiceAccount.customSecretName)) (or .Values.artifactory.joinKeySecretName .Values.global.joinKeySecretName)) .Values.artifactory.unifiedSecretInstallation }} +****************************************** WARNING ************************************************************************************************** +* The unifiedSecretInstallation flag is currently enabled, which creates the unified secret. The existing secrets will continue as separate secrets.* +* Update the values.yaml with the existing secrets to add them to the unified secret. * +***************************************************************************************************************************************************** +{{- end }} + +{{- if .Values.postgresql.enabled }} + +DATABASE: +To extract the database password, run the following +export DB_PASSWORD=$(kubectl get --namespace {{ .Release.Namespace }} $(kubectl get secret --namespace {{ .Release.Namespace }} -o name | grep postgresql) -o jsonpath="{.data.postgresql-password}" | base64 --decode) +echo ${DB_PASSWORD} +{{- end }} + +SETUP: +1. Get the Artifactory IP and URL + + {{- if contains "NodePort" .Values.nginx.service.type }} + export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ template "artifactory-ha.nginx.fullname" . }}) + export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}") + echo http://$NODE_IP:$NODE_PORT/ + + {{- else if contains "LoadBalancer" .Values.nginx.service.type }} + NOTE: It may take a few minutes for the LoadBalancer public IP to be available! + + You can watch the status of the service by running 'kubectl get svc -w {{ template "artifactory-ha.nginx.fullname" . }}' + export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ template "artifactory-ha.nginx.fullname" . }} -o jsonpath='{.status.loadBalancer.ingress[0].ip}') + echo http://$SERVICE_IP/ + + {{- else if contains "ClusterIP" .Values.nginx.service.type }} + export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "component={{ .Values.nginx.name }}" -o jsonpath="{.items[0].metadata.name}") + kubectl port-forward --namespace {{ .Release.Namespace }} $POD_NAME 8080:80 + echo http://127.0.0.1:8080 + + {{- end }} + +2. Open Artifactory in your browser + Default credential for Artifactory: + user: admin + password: password + + {{- if .Values.artifactory.license.secret }} + +3. Artifactory license(s) is deployed as a Kubernetes secret. This method is relevant for initial deployment only! + Updating the license should be done via Artifactory UI or REST API. If you want to keep managing the artifactory license using the same method, you can use artifactory.copyOnEveryStartup in values.yaml. + + {{- else }} + +3. Add HA licenses to activate Artifactory HA through the Artifactory UI + NOTE: Each Artifactory node requires a valid license. See https://www.jfrog.com/confluence/display/RTF/HA+Installation+and+Setup for more details. + + {{- end }} + +{{ if or .Values.artifactory.primary.javaOpts.jmx.enabled .Values.artifactory.node.javaOpts.jmx.enabled }} +JMX configuration: +{{- if not (contains "LoadBalancer" .Values.artifactory.service.type) }} +If you want to access JMX from you computer with jconsole, you should set ".Values.artifactory.service.type=LoadBalancer" !!! +{{ end }} + +1. Get the Artifactory service IP: +{{- if .Values.artifactory.primary.javaOpts.jmx.enabled }} +export PRIMARY_SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ template "artifactory-ha.primary.name" . }} -o jsonpath='{.status.loadBalancer.ingress[0].ip}') +{{- end }} +{{- if .Values.artifactory.node.javaOpts.jmx.enabled }} +export MEMBER_SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ template "artifactory-ha.fullname" . }} -o jsonpath='{.status.loadBalancer.ingress[0].ip}') +{{- end }} + +2. Map the service name to the service IP in /etc/hosts: +{{- if .Values.artifactory.primary.javaOpts.jmx.enabled }} +sudo sh -c "echo \"${PRIMARY_SERVICE_IP} {{ template "artifactory-ha.primary.name" . }}\" >> /etc/hosts" +{{- end }} +{{- if .Values.artifactory.node.javaOpts.jmx.enabled }} +sudo sh -c "echo \"${MEMBER_SERVICE_IP} {{ template "artifactory-ha.fullname" . }}\" >> /etc/hosts" +{{- end }} + +3. Launch jconsole: +{{- if .Values.artifactory.primary.javaOpts.jmx.enabled }} +jconsole {{ template "artifactory-ha.primary.name" . }}:{{ .Values.artifactory.primary.javaOpts.jmx.port }} +{{- end }} +{{- if .Values.artifactory.node.javaOpts.jmx.enabled }} +jconsole {{ template "artifactory-ha.fullname" . }}:{{ .Values.artifactory.node.javaOpts.jmx.port }} +{{- end }} +{{- end }} + + +{{- if ge (.Values.artifactory.node.replicaCount | int) 1 }} +***************************************** WARNING ***************************************************************************** +* Currently member node(s) are enabled, will be deprecated in upcoming releases * +* It is recommended to upgrade from primary-members to primary-only. * +* It can be done by deploying the chart ( >=107.59.x) with the new values. Also, please refer to changelog of 107.59.x chart * +* More Info: https://jfrog.com/help/r/jfrog-installation-setup-documentation/cloud-native-high-availability * +******************************************************************************************************************************* +{{- end }} + +{{- if and .Values.nginx.enabled .Values.ingress.hosts }} +***************************************** WARNING ***************************************************************************** +* when nginx is enabled , .Values.ingress.hosts will be deprecated in upcoming releases * +* It is recommended to use nginx.hosts instead ingress.hosts +******************************************************************************************************************************* +{{- end }} \ No newline at end of file diff --git a/charts/jfrog/artifactory-ha/107.104.10/templates/_helpers.tpl b/charts/jfrog/artifactory-ha/107.104.10/templates/_helpers.tpl new file mode 100644 index 0000000000..1e31d20a6d --- /dev/null +++ b/charts/jfrog/artifactory-ha/107.104.10/templates/_helpers.tpl @@ -0,0 +1,682 @@ +{{/* vim: set filetype=mustache: */}} +{{/* +Expand the name of the chart. +*/}} +{{- define "artifactory-ha.name" -}} +{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}} +{{- end -}} + +{{/* +The primary node name +*/}} +{{- define "artifactory-ha.primary.name" -}} +{{- if .Values.nameOverride -}} +{{- printf "%s-primary" .Values.nameOverride | trunc 63 | trimSuffix "-" -}} +{{- else -}} +{{- $name := .Release.Name | trunc 29 -}} +{{- printf "%s-%s-primary" $name .Chart.Name | trunc 63 | trimSuffix "-" -}} +{{- end -}} +{{- end -}} + +{{/* +The member node name +*/}} +{{- define "artifactory-ha.node.name" -}} +{{- if .Values.nameOverride -}} +{{- printf "%s-member" .Values.nameOverride | trunc 63 | trimSuffix "-" -}} +{{- else -}} +{{- $name := .Release.Name | trunc 29 -}} +{{- printf "%s-%s-member" $name .Chart.Name | trunc 63 | trimSuffix "-" -}} +{{- end -}} +{{- end -}} + +{{/* +Expand the name nginx service. +*/}} +{{- define "artifactory-ha.nginx.name" -}} +{{- default .Values.nginx.name .Values.nameOverride | trunc 63 | trimSuffix "-" -}} +{{- end -}} + +{{/* +Create a default fully qualified app name. +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +If release name contains chart name it will be used as a full name. +*/}} +{{- define "artifactory-ha.fullname" -}} +{{- if .Values.fullnameOverride -}} +{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" -}} +{{- else -}} +{{- $name := default .Chart.Name .Values.nameOverride -}} +{{- if contains $name .Release.Name -}} +{{- .Release.Name | trunc 63 | trimSuffix "-" -}} +{{- else -}} +{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}} +{{- end -}} +{{- end -}} +{{- end -}} + +{{/* +Create a default fully qualified app name. +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +If release name contains chart name it will be used as a full name. +*/}} +{{- define "artifactory-ha.nginx.fullname" -}} +{{- if .Values.nginx.fullnameOverride -}} +{{- .Values.nginx.fullnameOverride | trunc 63 | trimSuffix "-" -}} +{{- else -}} +{{- $name := default .Chart.Name .Values.nginx.name -}} +{{- if contains $name .Release.Name -}} +{{- .Release.Name | trunc 63 | trimSuffix "-" -}} +{{- else -}} +{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}} +{{- end -}} +{{- end -}} +{{- end -}} + +{{/* +Create the name of the service account to use +*/}} +{{- define "artifactory-ha.serviceAccountName" -}} +{{- if .Values.serviceAccount.create -}} +{{ default (include "artifactory-ha.fullname" .) .Values.serviceAccount.name }} +{{- else -}} +{{ default "default" .Values.serviceAccount.name }} +{{- end -}} +{{- end -}} + +{{/* +Create chart name and version as used by the chart label. +*/}} +{{- define "artifactory-ha.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}} +{{- end -}} + +{{/* +Generate SSL certificates +*/}} +{{- define "artifactory-ha.gen-certs" -}} +{{- $altNames := list ( printf "%s.%s" (include "artifactory-ha.fullname" .) .Release.Namespace ) ( printf "%s.%s.svc" (include "artifactory-ha.fullname" .) .Release.Namespace ) -}} +{{- $ca := genCA "artifactory-ca" 365 -}} +{{- $cert := genSignedCert ( include "artifactory-ha.fullname" . ) nil $altNames 365 $ca -}} +tls.crt: {{ $cert.Cert | b64enc }} +tls.key: {{ $cert.Key | b64enc }} +{{- end -}} + +{{/* +Scheme (http/https) based on Access or Router TLS enabled/disabled +*/}} +{{- define "artifactory-ha.scheme" -}} +{{- if or .Values.access.accessConfig.security.tls .Values.router.tlsEnabled -}} +{{- printf "%s" "https" -}} +{{- else -}} +{{- printf "%s" "http" -}} +{{- end -}} +{{- end -}} + +{{/* +Resolve joinKey value +*/}} +{{- define "artifactory-ha.joinKey" -}} +{{- if .Values.global.joinKey -}} +{{- .Values.global.joinKey -}} +{{- else if .Values.artifactory.joinKey -}} +{{- .Values.artifactory.joinKey -}} +{{- end -}} +{{- end -}} + +{{/* +Resolve jfConnectToken value +*/}} +{{- define "artifactory-ha.jfConnectToken" -}} +{{- .Values.artifactory.jfConnectToken -}} +{{- end -}} + +{{/* +Resolve masterKey value +*/}} +{{- define "artifactory-ha.masterKey" -}} +{{- if .Values.global.masterKey -}} +{{- .Values.global.masterKey -}} +{{- else if .Values.artifactory.masterKey -}} +{{- .Values.artifactory.masterKey -}} +{{- end -}} +{{- end -}} + +{{/* +Resolve joinKeySecretName value +*/}} +{{- define "artifactory-ha.joinKeySecretName" -}} +{{- if .Values.global.joinKeySecretName -}} +{{- .Values.global.joinKeySecretName -}} +{{- else if .Values.artifactory.joinKeySecretName -}} +{{- .Values.artifactory.joinKeySecretName -}} +{{- else -}} +{{ include "artifactory-ha.fullname" . }} +{{- end -}} +{{- end -}} + +{{/* +Resolve jfConnectTokenSecretName value +*/}} +{{- define "artifactory-ha.jfConnectTokenSecretName" -}} +{{- if .Values.artifactory.jfConnectTokenSecretName -}} +{{- .Values.artifactory.jfConnectTokenSecretName -}} +{{- else -}} +{{ include "artifactory-ha.fullname" . }} +{{- end -}} +{{- end -}} + +{{/* +Resolve masterKeySecretName value +*/}} +{{- define "artifactory-ha.masterKeySecretName" -}} +{{- if .Values.global.masterKeySecretName -}} +{{- .Values.global.masterKeySecretName -}} +{{- else if .Values.artifactory.masterKeySecretName -}} +{{- .Values.artifactory.masterKeySecretName -}} +{{- else -}} +{{ include "artifactory-ha.fullname" . }} +{{- end -}} +{{- end -}} + +{{/* +Resolve imagePullSecrets value +*/}} +{{- define "artifactory-ha.imagePullSecrets" -}} +{{- if .Values.global.imagePullSecrets }} +imagePullSecrets: +{{- range .Values.global.imagePullSecrets }} + - name: {{ . }} +{{- end }} +{{- else if .Values.imagePullSecrets }} +imagePullSecrets: +{{- range .Values.imagePullSecrets }} + - name: {{ . }} +{{- end }} +{{- end -}} +{{- end -}} + +{{/* +Resolve customInitContainersBegin value +*/}} +{{- define "artifactory-ha.customInitContainersBegin" -}} +{{- if .Values.global.customInitContainersBegin -}} +{{- .Values.global.customInitContainersBegin -}} +{{- end -}} +{{- if .Values.artifactory.customInitContainersBegin -}} +{{- .Values.artifactory.customInitContainersBegin -}} +{{- end -}} +{{- end -}} + +{{/* +Resolve customInitContainers value +*/}} +{{- define "artifactory-ha.customInitContainers" -}} +{{- if .Values.global.customInitContainers -}} +{{- .Values.global.customInitContainers -}} +{{- end -}} +{{- if .Values.artifactory.customInitContainers -}} +{{- .Values.artifactory.customInitContainers -}} +{{- end -}} +{{- end -}} + +{{/* +Resolve customVolumes value +*/}} +{{- define "artifactory-ha.customVolumes" -}} +{{- if .Values.global.customVolumes -}} +{{- .Values.global.customVolumes -}} +{{- end -}} +{{- if .Values.artifactory.customVolumes -}} +{{- .Values.artifactory.customVolumes -}} +{{- end -}} +{{- end -}} + +{{/* +Resolve unifiedCustomSecretVolumeName value +*/}} +{{- define "artifactory-ha.unifiedCustomSecretVolumeName" -}} +{{- printf "%s-%s" (include "artifactory-ha.name" .) ("unified-secret-volume") | trunc 63 -}} +{{- end -}} + +{{/* +Check the Duplication of volume names for secrets. If unifiedSecretInstallation is enabled then the method is checking for volume names, +if the volume exists in customVolume then an extra volume with the same name will not be getting added in unifiedSecretInstallation case.*/}} +{{- define "artifactory-ha.checkDuplicateUnifiedCustomVolume" -}} +{{- if or .Values.global.customVolumes .Values.artifactory.customVolumes -}} +{{- $val := (tpl (include "artifactory-ha.customVolumes" .) .) | toJson -}} +{{- contains (include "artifactory-ha.unifiedCustomSecretVolumeName" .) $val | toString -}} +{{- else -}} +{{- printf "%s" "false" -}} +{{- end -}} +{{- end -}} + +{{/* +Resolve customVolumeMounts value +*/}} +{{- define "artifactory-ha.customVolumeMounts" -}} +{{- if .Values.global.customVolumeMounts -}} +{{- .Values.global.customVolumeMounts -}} +{{- end -}} +{{- if .Values.artifactory.customVolumeMounts -}} +{{- .Values.artifactory.customVolumeMounts -}} +{{- end -}} +{{- end -}} + +{{/* +Resolve customSidecarContainers value +*/}} +{{- define "artifactory-ha.customSidecarContainers" -}} +{{- if .Values.global.customSidecarContainers -}} +{{- .Values.global.customSidecarContainers -}} +{{- end -}} +{{- if .Values.artifactory.customSidecarContainers -}} +{{- .Values.artifactory.customSidecarContainers -}} +{{- end -}} +{{- end -}} + +{{/* +Return the proper artifactory chart image names +*/}} +{{- define "artifactory-ha.getImageInfoByValue" -}} +{{- $dot := index . 0 }} +{{- $indexReference := index . 1 }} +{{- $registryName := index $dot.Values $indexReference "image" "registry" -}} +{{- $repositoryName := index $dot.Values $indexReference "image" "repository" -}} +{{- $tag := "" -}} +{{- if and (eq $indexReference "artifactory") (hasKey $dot.Values "artifactoryService") }} + {{- if default false $dot.Values.artifactoryService.enabled }} + {{- $indexReference = "artifactoryService" -}} + {{- $tag = default $dot.Chart.Annotations.artifactoryServiceVersion (index $dot.Values $indexReference "image" "tag") | toString -}} + {{- $repositoryName = index $dot.Values $indexReference "image" "repository" -}} + {{- else -}} + {{- $tag = default $dot.Chart.AppVersion (index $dot.Values $indexReference "image" "tag") | toString -}} + {{- end -}} +{{- else -}} + {{- $tag = default $dot.Chart.AppVersion (index $dot.Values $indexReference "image" "tag") | toString -}} +{{- end -}} +{{- if and (eq $indexReference "metadata") (hasKey $dot.Values.metadata "standaloneImageEnabled") }} + {{- if default false $dot.Values.metadata.standaloneImageEnabled }} + {{- $tag = default $dot.Chart.Annotations.metadataVersion (index $dot.Values $indexReference "image" "tag") | toString -}} + {{- end -}} +{{- end -}} +{{- if and (eq $indexReference "observability") (hasKey $dot.Values.observability "standaloneImageEnabled") }} + {{- if default false $dot.Values.observability.standaloneImageEnabled }} + {{- $tag = default $dot.Chart.Annotations.observabilityVersion (index $dot.Values $indexReference "image" "tag") | toString -}} + {{- end -}} +{{- end -}} +{{- if $dot.Values.global }} + {{- if and $dot.Values.splitServicesToContainers $dot.Values.global.versions.router (eq $indexReference "router") }} + {{- $tag = $dot.Values.global.versions.router | toString -}} + {{- end -}} + {{- if and $dot.Values.global.versions.initContainers (eq $indexReference "initContainers") }} + {{- $tag = $dot.Values.global.versions.initContainers | toString -}} + {{- end -}} + {{- if and $dot.Values.global.versions.rtfs (eq $indexReference "rtfs") }} + {{- $tag = $dot.Values.global.versions.rtfs | toString -}} + {{- end -}} + {{- if $dot.Values.global.versions.artifactory }} + {{- if or (eq $indexReference "artifactory") (eq $indexReference "metadata") (eq $indexReference "nginx") (eq $indexReference "observability") }} + {{- $tag = $dot.Values.global.versions.artifactory | toString -}} + {{- end -}} + {{- end -}} + {{- if $dot.Values.global.imageRegistry }} + {{- printf "%s/%s:%s" $dot.Values.global.imageRegistry $repositoryName $tag -}} + {{- else -}} + {{- printf "%s/%s:%s" $registryName $repositoryName $tag -}} + {{- end -}} +{{- else -}} + {{- printf "%s/%s:%s" $registryName $repositoryName $tag -}} +{{- end -}} +{{- end -}} + +{{/* +Return the proper artifactory app version +*/}} +{{- define "artifactory-ha.app.version" -}} +{{- $tag := (splitList ":" ((include "artifactory-ha.getImageInfoByValue" (list . "artifactory" )))) | last | toString -}} +{{- printf "%s" $tag -}} +{{- end -}} + +{{/* +Custom certificate copy command +*/}} +{{- define "artifactory-ha.copyCustomCerts" -}} +echo "Copy custom certificates to {{ .Values.artifactory.persistence.mountPath }}/etc/security/keys/trusted"; +mkdir -p {{ .Values.artifactory.persistence.mountPath }}/etc/security/keys/trusted; +for file in $(ls -1 /tmp/certs/* | grep -v .key | grep -v ":" | grep -v grep); do if [ -f "${file}" ]; then cp -v ${file} {{ .Values.artifactory.persistence.mountPath }}/etc/security/keys/trusted; fi done; +if [ -f {{ .Values.artifactory.persistence.mountPath }}/etc/security/keys/trusted/tls.crt ]; then mv -v {{ .Values.artifactory.persistence.mountPath }}/etc/security/keys/trusted/tls.crt {{ .Values.artifactory.persistence.mountPath }}/etc/security/keys/trusted/ca.crt; fi; +{{- end -}} + +{{/* +Circle of trust certificates copy command +*/}} +{{- define "artifactory.copyCircleOfTrustCertsCerts" -}} +echo "Copy circle of trust certificates to {{ .Values.artifactory.persistence.mountPath }}/etc/access/keys/trusted"; +mkdir -p {{ .Values.artifactory.persistence.mountPath }}/etc/access/keys/trusted; +for file in $(ls -1 /tmp/circleoftrustcerts/* | grep -v .key | grep -v ":" | grep -v grep); do if [ -f "${file}" ]; then cp -v ${file} {{ .Values.artifactory.persistence.mountPath }}/etc/access/keys/trusted; fi done; +{{- end -}} + +{{/* +Resolve requiredServiceTypes value +*/}} +{{- define "artifactory-ha.router.requiredServiceTypes" -}} +{{- $requiredTypes := "jfrt,jfac" -}} +{{- if not .Values.access.enabled -}} + {{- $requiredTypes = "jfrt" -}} +{{- end -}} +{{- if .Values.observability.enabled -}} + {{- $requiredTypes = printf "%s,%s" $requiredTypes "jfob" -}} +{{- end -}} +{{- if .Values.metadata.enabled -}} + {{- $requiredTypes = printf "%s,%s" $requiredTypes "jfmd" -}} +{{- end -}} +{{- if .Values.event.enabled -}} + {{- $requiredTypes = printf "%s,%s" $requiredTypes "jfevt" -}} +{{- end -}} +{{- if .Values.frontend.enabled -}} + {{- $requiredTypes = printf "%s,%s" $requiredTypes "jffe" -}} +{{- end -}} +{{- if .Values.jfconnect.enabled -}} + {{- $requiredTypes = printf "%s,%s" $requiredTypes "jfcon" -}} +{{- end -}} +{{- if .Values.evidence.enabled -}} + {{- $requiredTypes = printf "%s,%s" $requiredTypes "jfevd" -}} +{{- end -}} +{{- if .Values.topology.enabled -}} + {{- $requiredTypes = printf "%s,%s" $requiredTypes "jftpl" -}} +{{- end -}} +{{- if .Values.mc.enabled -}} + {{- $requiredTypes = printf "%s,%s" $requiredTypes "jfmc" -}} +{{- end -}} +{{- if .Values.onemodel.enabled -}} + {{- $requiredTypes = printf "%s,%s" $requiredTypes "jfomr" -}} +{{- end -}} +{{- $requiredTypes -}} +{{- end -}} + +{{/* +nginx scheme (http/https) +*/}} +{{- define "nginx.scheme" -}} +{{- if .Values.nginx.http.enabled -}} +{{- printf "%s" "http" -}} +{{- else -}} +{{- printf "%s" "https" -}} +{{- end -}} +{{- end -}} + + +{{/* +nginx command +*/}} +{{- define "nginx.command" -}} +{{- if .Values.nginx.customCommand }} +{{ toYaml .Values.nginx.customCommand }} +{{- end }} +{{- end -}} + +{{/* +nginx port (8080/8443) based on http/https enabled +*/}} +{{- define "nginx.port" -}} +{{- if .Values.nginx.http.enabled -}} +{{- .Values.nginx.http.internalPort -}} +{{- else -}} +{{- .Values.nginx.https.internalPort -}} +{{- end -}} +{{- end -}} + +{{/* +Resolve customInitContainers value +*/}} +{{- define "artifactory.nginx.customInitContainers" -}} +{{- if .Values.nginx.customInitContainers -}} +{{- .Values.nginx.customInitContainers -}} +{{- end -}} +{{- end -}} + +{{/* +Resolve customVolumes value +*/}} +{{- define "artifactory.nginx.customVolumes" -}} +{{- if .Values.nginx.customVolumes -}} +{{- .Values.nginx.customVolumes -}} +{{- end -}} +{{- end -}} + +{{/* +Resolve customVolumeMounts nginx value +*/}} +{{- define "artifactory.nginx.customVolumeMounts" -}} +{{- if .Values.nginx.customVolumeMounts -}} +{{- .Values.nginx.customVolumeMounts -}} +{{- end -}} +{{- end -}} + +{{/* +Resolve customSidecarContainers value +*/}} +{{- define "artifactory.nginx.customSidecarContainers" -}} +{{- if .Values.nginx.customSidecarContainers -}} +{{- .Values.nginx.customSidecarContainers -}} +{{- end -}} +{{- end -}} + +{{/* +Resolve Artifactory pod primary node selector value +*/}} +{{- define "artifactory.nodeSelector" -}} +nodeSelector: +{{- if .Values.global.nodeSelector }} +{{ toYaml .Values.global.nodeSelector | indent 2 }} +{{- else if .Values.artifactory.primary.nodeSelector }} +{{ toYaml .Values.artifactory.primary.nodeSelector | indent 2 }} +{{- end -}} +{{- end -}} + +{{/* +Resolve Artifactory pod node nodeselector value +*/}} +{{- define "artifactory.node.nodeSelector" -}} +nodeSelector: +{{- if .Values.global.nodeSelector }} +{{ toYaml .Values.global.nodeSelector | indent 2 }} +{{- else if .Values.artifactory.node.nodeSelector }} +{{ toYaml .Values.artifactory.node.nodeSelector | indent 2 }} +{{- end -}} +{{- end -}} + +{{/* +Resolve Nginx pods node selector value +*/}} +{{- define "nginx.nodeSelector" -}} +nodeSelector: +{{- if .Values.global.nodeSelector }} +{{ toYaml .Values.global.nodeSelector | indent 2 }} +{{- else if .Values.nginx.nodeSelector }} +{{ toYaml .Values.nginx.nodeSelector | indent 2 }} +{{- end -}} +{{- end -}} + +{{/* +Calculate the systemYaml from structured and unstructured text input +*/}} +{{- define "artifactory.finalSystemYaml" -}} +{{ tpl (mergeOverwrite (include "artifactory.systemYaml" . | fromYaml) .Values.artifactory.extraSystemYaml | toYaml) . }} +{{- end -}} + +{{/* +Calculate the systemYaml from the unstructured text input +*/}} +{{- define "artifactory.systemYaml" -}} +{{ include (print $.Template.BasePath "/_system-yaml-render.tpl") . }} +{{- end -}} + +{{/* +Metrics enabled +*/}} +{{- define "metrics.enabled" -}} + metrics: + enabled: true +{{- end }} + +{{/* +Resolve artifactory metrics +*/}} +{{- define "artifactory.metrics" -}} +{{- if .Values.artifactory.openMetrics -}} +{{- if .Values.artifactory.openMetrics.enabled -}} +{{ include "metrics.enabled" . }} +{{- if .Values.artifactory.openMetrics.filebeat }} +{{- if .Values.artifactory.openMetrics.filebeat.enabled }} +{{ include "metrics.enabled" . }} + filebeat: +{{ tpl (.Values.artifactory.openMetrics.filebeat | toYaml) . | indent 6 }} +{{- end -}} +{{- end -}} +{{- end -}} +{{- else if .Values.artifactory.metrics -}} +{{- if .Values.artifactory.metrics.enabled -}} +{{ include "metrics.enabled" . }} +{{- if .Values.artifactory.metrics.filebeat }} +{{- if .Values.artifactory.metrics.filebeat.enabled }} +{{ include "metrics.enabled" . }} + filebeat: +{{ tpl (.Values.artifactory.metrics.filebeat | toYaml) . | indent 6 }} +{{- end -}} +{{- end -}} +{{- end -}} +{{- end -}} +{{- end -}} + +{{/* +Resolve unified secret prepend release name +*/}} +{{- define "artifactory.unifiedSecretPrependReleaseName" -}} +{{- if .Values.artifactory.unifiedSecretPrependReleaseName }} +{{- printf "%s" (include "artifactory-ha.fullname" .) -}} +{{- else }} +{{- printf "%s" (include "artifactory-ha.name" .) -}} +{{- end }} +{{- end }} + +{{/* +Resolve nginx hosts value +*/}} +{{- define "artifactory.nginx.hosts" -}} +{{- if .Values.ingress.hosts }} +{{- range .Values.ingress.hosts -}} + {{- if contains "." . -}} + {{ "" | indent 0 }} ~(?.+)\.{{ . }} + {{- end -}} +{{- end -}} +{{- else if .Values.nginx.hosts }} +{{- range .Values.nginx.hosts -}} + {{- if contains "." . -}} + {{ "" | indent 0 }} ~(?.+)\.{{ . }} + {{- end -}} +{{- end -}} +{{- end -}} +{{- end -}} + +{{/* +Create a default fully qualified grpc ingress name. +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +*/}} +{{- define "artifactory.ingressGrpc.fullname" -}} +{{- printf "%s-%s" (include "artifactory-ha.fullname" .) .Values.ingressGrpc.name | trunc 63 | trimSuffix "-" -}} +{{- end -}} + +{{/* +Create a default fully qualified grpc service name. +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +*/}} +{{- define "artifactory.serviceGrpc.fullname" -}} +{{- printf "%s-%s" (include "artifactory-ha.fullname" .) .Values.artifactory.serviceGrpc.name | trunc 63 | trimSuffix "-" -}} +{{- end -}} + +{{/* +Create a default fully qualified app name. +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +*/}} +{{- define "rtfs.fullname" -}} +{{- if .Values.fullnameOverride -}} +{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" -}} +{{- else -}} +{{- $name := default .Chart.Name .Values.nameOverride -}} +{{- if contains $name .Release.Name -}} +{{- printf "%s-%s" (.Release.Name | trunc 63 | trimSuffix "-") .Values.rtfs.name -}} +{{- else -}} +{{- printf "%s-%s-%s" (.Release.Name | trunc 63 | trimSuffix "-") $name .Values.rtfs.name -}} +{{- end -}} +{{- end -}} +{{- end -}} + + +{{/* + Resolve jfrogUrl value +*/}} +{{- define "rtfs.jfrogUrl" -}} +{{- if .Values.global.jfrogUrl -}} +{{- .Values.global.jfrogUrl -}} +{{- else if .Values.rtfs.jfrogUrl -}} +{{- .Values.rtfs.jfrogUrl -}} +{{- else -}} +{{- printf "http://%s:8082" (include "artifactory-ha.fullname" .) -}} +{{- end -}} +{{- end -}} + +{{/* +Resolve RTFS customSidecarContainers value +*/}} +{{- define "artifactory.rtfs.customSidecarContainers" -}} +{{- if .Values.rtfs.customSidecarContainers -}} +{{- .Values.rtfs.customSidecarContainers -}} +{{- end -}} +{{- end -}} + +{{/* +Resolve RTFS customInitContainers value +*/}} +{{- define "artifactory.rtfs.customInitContainers" -}} +{{- if .Values.rtfs.customInitContainers -}} +{{- .Values.rtfs.customInitContainers -}} +{{- end -}} +{{- end -}} + +{{/* +Resolve customVolumes value +*/}} +{{- define "artifactory.rtfs.customVolumes" -}} +{{- if .Values.rtfs.customVolumes -}} +{{- .Values.rtfs.customVolumes -}} +{{- end -}} +{{- end -}} + +{{/* +Rtfs command +*/}} +{{- define "rtfs.command" -}} +{{- if .Values.rtfs.customCommand }} +{{ toYaml .Values.rtfs.customCommand }} +{{- end }} +{{- end -}} + +{{/* +Resolve customVolumeMounts rtfs value +*/}} +{{- define "artifactory.rtfs.customVolumeMounts" -}} +{{- if .Values.rtfs.customVolumeMounts -}} +{{- .Values.rtfs.customVolumeMounts -}} +{{- end -}} +{{- end -}} + +{{/* +Resolve RTFS autoscalling metrics +*/}} +{{- define "rtfs.metrics" -}} +{{- if .Values.rtfs.autoscaling.metrics -}} +{{- .Values.rtfs.autoscaling.metrics -}} +{{- end -}} +{{- end -}} \ No newline at end of file diff --git a/charts/jfrog/artifactory-ha/107.104.10/templates/_system-yaml-render.tpl b/charts/jfrog/artifactory-ha/107.104.10/templates/_system-yaml-render.tpl new file mode 100644 index 0000000000..deaa773ea9 --- /dev/null +++ b/charts/jfrog/artifactory-ha/107.104.10/templates/_system-yaml-render.tpl @@ -0,0 +1,5 @@ +{{- if .Values.artifactory.systemYaml -}} +{{- tpl .Values.artifactory.systemYaml . -}} +{{- else -}} +{{ (tpl ( $.Files.Get "files/system.yaml" ) .) }} +{{- end -}} \ No newline at end of file diff --git a/charts/jfrog/artifactory-ha/107.104.10/templates/additional-resources.yaml b/charts/jfrog/artifactory-ha/107.104.10/templates/additional-resources.yaml new file mode 100644 index 0000000000..c4d06f08ad --- /dev/null +++ b/charts/jfrog/artifactory-ha/107.104.10/templates/additional-resources.yaml @@ -0,0 +1,3 @@ +{{ if .Values.additionalResources }} +{{ tpl .Values.additionalResources . }} +{{- end -}} diff --git a/charts/jfrog/artifactory-ha/107.104.10/templates/admin-bootstrap-creds.yaml b/charts/jfrog/artifactory-ha/107.104.10/templates/admin-bootstrap-creds.yaml new file mode 100644 index 0000000000..40d91f75e9 --- /dev/null +++ b/charts/jfrog/artifactory-ha/107.104.10/templates/admin-bootstrap-creds.yaml @@ -0,0 +1,15 @@ +{{- if not (and .Values.artifactory.admin.secret .Values.artifactory.admin.dataKey) }} +{{- if and .Values.artifactory.admin.password (not .Values.artifactory.unifiedSecretInstallation) }} +kind: Secret +apiVersion: v1 +metadata: + name: {{ template "artifactory-ha.fullname" . }}-bootstrap-creds + labels: + app: {{ template "artifactory-ha.name" . }} + chart: {{ template "artifactory-ha.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} +data: + bootstrap.creds: {{ (printf "%s@%s=%s" .Values.artifactory.admin.username .Values.artifactory.admin.ip .Values.artifactory.admin.password) | b64enc }} +{{- end }} +{{- end }} diff --git a/charts/jfrog/artifactory-ha/107.104.10/templates/artifactory-access-config.yaml b/charts/jfrog/artifactory-ha/107.104.10/templates/artifactory-access-config.yaml new file mode 100644 index 0000000000..0b96a337d4 --- /dev/null +++ b/charts/jfrog/artifactory-ha/107.104.10/templates/artifactory-access-config.yaml @@ -0,0 +1,15 @@ +{{- if and .Values.access.accessConfig (not .Values.artifactory.unifiedSecretInstallation) }} +apiVersion: v1 +kind: Secret +metadata: + name: {{ template "artifactory-ha.fullname" . }}-access-config + labels: + app: {{ template "artifactory-ha.name" . }} + chart: {{ template "artifactory-ha.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} +type: Opaque +stringData: + access.config.patch.yml: | +{{ tpl (toYaml .Values.access.accessConfig) . | indent 4 }} +{{- end }} diff --git a/charts/jfrog/artifactory-ha/107.104.10/templates/artifactory-binarystore-secret.yaml b/charts/jfrog/artifactory-ha/107.104.10/templates/artifactory-binarystore-secret.yaml new file mode 100644 index 0000000000..6824fe90f8 --- /dev/null +++ b/charts/jfrog/artifactory-ha/107.104.10/templates/artifactory-binarystore-secret.yaml @@ -0,0 +1,18 @@ +{{- if and (not .Values.artifactory.persistence.customBinarystoreXmlSecret) (not .Values.artifactory.unifiedSecretInstallation) }} +kind: Secret +apiVersion: v1 +metadata: + name: {{ template "artifactory-ha.fullname" . }}-binarystore + labels: + app: {{ template "artifactory-ha.name" . }} + chart: {{ template "artifactory-ha.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} +stringData: + binarystore.xml: |- +{{- if .Values.artifactory.persistence.binarystoreXml }} +{{ tpl .Values.artifactory.persistence.binarystoreXml . | indent 4 }} +{{- else }} +{{ tpl ( .Files.Get "files/binarystore.xml" ) . | indent 4 }} +{{- end }} +{{- end }} diff --git a/charts/jfrog/artifactory-ha/107.104.10/templates/artifactory-configmaps.yaml b/charts/jfrog/artifactory-ha/107.104.10/templates/artifactory-configmaps.yaml new file mode 100644 index 0000000000..1385bc5782 --- /dev/null +++ b/charts/jfrog/artifactory-ha/107.104.10/templates/artifactory-configmaps.yaml @@ -0,0 +1,13 @@ +{{ if .Values.artifactory.configMaps }} +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ template "artifactory-ha.fullname" . }}-configmaps + labels: + app: {{ template "artifactory-ha.fullname" . }} + chart: {{ .Chart.Name }}-{{ .Chart.Version }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} +data: +{{ tpl .Values.artifactory.configMaps . | indent 2 }} +{{ end -}} \ No newline at end of file diff --git a/charts/jfrog/artifactory-ha/107.104.10/templates/artifactory-custom-secrets.yaml b/charts/jfrog/artifactory-ha/107.104.10/templates/artifactory-custom-secrets.yaml new file mode 100644 index 0000000000..8065fe6865 --- /dev/null +++ b/charts/jfrog/artifactory-ha/107.104.10/templates/artifactory-custom-secrets.yaml @@ -0,0 +1,19 @@ +{{- if and .Values.artifactory.customSecrets (not .Values.artifactory.unifiedSecretInstallation) }} +{{- range .Values.artifactory.customSecrets }} +--- +apiVersion: v1 +kind: Secret +metadata: + name: {{ template "artifactory-ha.fullname" $ }}-{{ .name }} + labels: + app: "{{ template "artifactory-ha.name" $ }}" + chart: "{{ template "artifactory-ha.chart" $ }}" + component: "{{ $.Values.artifactory.name }}" + heritage: {{ $.Release.Service | quote }} + release: {{ $.Release.Name | quote }} +type: Opaque +stringData: + {{ .key }}: | +{{ .data | indent 4 -}} +{{- end -}} +{{- end -}} diff --git a/charts/jfrog/artifactory-ha/107.104.10/templates/artifactory-database-secrets.yaml b/charts/jfrog/artifactory-ha/107.104.10/templates/artifactory-database-secrets.yaml new file mode 100644 index 0000000000..6daf5db7bc --- /dev/null +++ b/charts/jfrog/artifactory-ha/107.104.10/templates/artifactory-database-secrets.yaml @@ -0,0 +1,24 @@ +{{- if and (not .Values.database.secrets) (not .Values.postgresql.enabled) (not .Values.artifactory.unifiedSecretInstallation) }} +{{- if or .Values.database.url .Values.database.user .Values.database.password }} +apiVersion: v1 +kind: Secret +metadata: + name: {{ template "artifactory-ha.fullname" . }}-database-creds + labels: + app: {{ template "artifactory-ha.name" . }} + chart: {{ template "artifactory-ha.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} +type: Opaque +data: + {{- with .Values.database.url }} + db-url: {{ tpl . $ | b64enc | quote }} + {{- end }} + {{- with .Values.database.user }} + db-user: {{ tpl . $ | b64enc | quote }} + {{- end }} + {{- with .Values.database.password }} + db-password: {{ tpl . $ | b64enc | quote }} + {{- end }} +{{- end }} +{{- end }} diff --git a/charts/jfrog/artifactory-ha/107.104.10/templates/artifactory-gcp-credentials-secret.yaml b/charts/jfrog/artifactory-ha/107.104.10/templates/artifactory-gcp-credentials-secret.yaml new file mode 100644 index 0000000000..d90769595b --- /dev/null +++ b/charts/jfrog/artifactory-ha/107.104.10/templates/artifactory-gcp-credentials-secret.yaml @@ -0,0 +1,16 @@ +{{- if not .Values.artifactory.persistence.googleStorage.gcpServiceAccount.customSecretName }} +{{- if and (.Values.artifactory.persistence.googleStorage.gcpServiceAccount.enabled) (not .Values.artifactory.unifiedSecretInstallation) }} +kind: Secret +apiVersion: v1 +metadata: + name: {{ template "artifactory-ha.fullname" . }}-gcpcreds + labels: + app: {{ template "artifactory-ha.name" . }} + chart: {{ template "artifactory-ha.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} +stringData: + gcp.credentials.json: |- +{{ tpl .Values.artifactory.persistence.googleStorage.gcpServiceAccount.config . | indent 4 }} +{{- end }} +{{- end }} \ No newline at end of file diff --git a/charts/jfrog/artifactory-ha/107.104.10/templates/artifactory-installer-info.yaml b/charts/jfrog/artifactory-ha/107.104.10/templates/artifactory-installer-info.yaml new file mode 100644 index 0000000000..0dff9dc86f --- /dev/null +++ b/charts/jfrog/artifactory-ha/107.104.10/templates/artifactory-installer-info.yaml @@ -0,0 +1,16 @@ +kind: ConfigMap +apiVersion: v1 +metadata: + name: {{ template "artifactory-ha.fullname" . }}-installer-info + labels: + app: {{ template "artifactory-ha.name" . }} + chart: {{ template "artifactory-ha.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} +data: + installer-info.json: | +{{- if .Values.installerInfo -}} +{{- tpl .Values.installerInfo . | nindent 4 -}} +{{- else -}} +{{ (tpl ( .Files.Get "files/installer-info.json" | nindent 4 ) .) }} +{{- end -}} diff --git a/charts/jfrog/artifactory-ha/107.104.10/templates/artifactory-license-secret.yaml b/charts/jfrog/artifactory-ha/107.104.10/templates/artifactory-license-secret.yaml new file mode 100644 index 0000000000..0018fa044b --- /dev/null +++ b/charts/jfrog/artifactory-ha/107.104.10/templates/artifactory-license-secret.yaml @@ -0,0 +1,16 @@ +{{ if and (not .Values.artifactory.unifiedSecretInstallation) (not .Values.artifactory.license.secret) }} +{{- with .Values.artifactory.license.licenseKey }} +apiVersion: v1 +kind: Secret +metadata: + name: {{ template "artifactory-ha.fullname" $ }}-license + labels: + app: {{ template "artifactory-ha.name" $ }} + chart: {{ template "artifactory-ha.chart" $ }} + heritage: {{ $.Release.Service }} + release: {{ $.Release.Name }} +type: Opaque +data: + artifactory.lic: {{ . | b64enc | quote }} +{{- end }} +{{- end }} diff --git a/charts/jfrog/artifactory-ha/107.104.10/templates/artifactory-migration-scripts.yaml b/charts/jfrog/artifactory-ha/107.104.10/templates/artifactory-migration-scripts.yaml new file mode 100644 index 0000000000..fe40f980fc --- /dev/null +++ b/charts/jfrog/artifactory-ha/107.104.10/templates/artifactory-migration-scripts.yaml @@ -0,0 +1,18 @@ +{{- if .Values.artifactory.migration.enabled }} +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ template "artifactory-ha.fullname" . }}-migration-scripts + labels: + app: {{ template "artifactory-ha.name" . }} + chart: {{ template "artifactory-ha.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} +data: + migrate.sh: | +{{ .Files.Get "files/migrate.sh" | indent 4 }} + migrationHelmInfo.yaml: | +{{ .Files.Get "files/migrationHelmInfo.yaml" | indent 4 }} + migrationStatus.sh: | +{{ .Files.Get "files/migrationStatus.sh" | indent 4 }} +{{- end }} diff --git a/charts/jfrog/artifactory-ha/107.104.10/templates/artifactory-networkpolicy.yaml b/charts/jfrog/artifactory-ha/107.104.10/templates/artifactory-networkpolicy.yaml new file mode 100644 index 0000000000..9924448f04 --- /dev/null +++ b/charts/jfrog/artifactory-ha/107.104.10/templates/artifactory-networkpolicy.yaml @@ -0,0 +1,34 @@ +{{- range .Values.networkpolicy }} +apiVersion: networking.k8s.io/v1 +kind: NetworkPolicy +metadata: + name: {{ template "artifactory-ha.fullname" $ }}-{{ .name }}-networkpolicy + labels: + app: {{ template "artifactory-ha.name" $ }} + chart: {{ template "artifactory-ha.chart" $ }} + release: {{ $.Release.Name }} + heritage: {{ $.Release.Service }} +spec: +{{- if .podSelector }} + podSelector: +{{ .podSelector | toYaml | trimSuffix "\n" | indent 4 -}} +{{ else }} + podSelector: {} +{{- end }} + policyTypes: + {{- if .ingress }} + - Ingress + {{- end }} + {{- if .egress }} + - Egress + {{- end }} +{{- if .ingress }} + ingress: +{{ .ingress | toYaml | trimSuffix "\n" | indent 2 -}} +{{- end }} +{{- if .egress }} + egress: +{{ .egress | toYaml | trimSuffix "\n" | indent 2 -}} +{{- end }} +--- +{{- end -}} \ No newline at end of file diff --git a/charts/jfrog/artifactory-ha/107.104.10/templates/artifactory-nfs-pvc.yaml b/charts/jfrog/artifactory-ha/107.104.10/templates/artifactory-nfs-pvc.yaml new file mode 100644 index 0000000000..6ed7d82f6b --- /dev/null +++ b/charts/jfrog/artifactory-ha/107.104.10/templates/artifactory-nfs-pvc.yaml @@ -0,0 +1,101 @@ +{{- if eq .Values.artifactory.persistence.type "nfs" }} +### Artifactory HA data +apiVersion: v1 +kind: PersistentVolume +metadata: + name: {{ template "artifactory-ha.fullname" . }}-data-pv + labels: + app: {{ template "artifactory-ha.name" . }} + chart: {{ template "artifactory-ha.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + id: {{ template "artifactory-ha.name" . }}-data-pv + type: nfs-volume +spec: + {{- if .Values.artifactory.persistence.nfs.mountOptions }} + mountOptions: +{{ toYaml .Values.artifactory.persistence.nfs.mountOptions | indent 4 }} + {{- end }} + capacity: + storage: {{ .Values.artifactory.persistence.nfs.capacity }} + accessModes: + - ReadWriteOnce + persistentVolumeReclaimPolicy: Retain + nfs: + server: {{ .Values.artifactory.persistence.nfs.ip }} + path: "{{ .Values.artifactory.persistence.nfs.haDataMount }}" + readOnly: false +--- +kind: PersistentVolumeClaim +apiVersion: v1 +metadata: + name: {{ template "artifactory-ha.fullname" . }}-data-pvc + labels: + app: {{ template "artifactory-ha.name" . }} + chart: {{ template "artifactory-ha.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + type: nfs-volume +spec: + accessModes: + - ReadWriteOnce + storageClassName: "" + resources: + requests: + storage: {{ .Values.artifactory.persistence.nfs.capacity }} + selector: + matchLabels: + id: {{ template "artifactory-ha.name" . }}-data-pv + app: {{ template "artifactory-ha.name" . }} + release: {{ .Release.Name }} +--- +### Artifactory HA backup +apiVersion: v1 +kind: PersistentVolume +metadata: + name: {{ template "artifactory-ha.fullname" . }}-backup-pv + labels: + app: {{ template "artifactory-ha.name" . }} + chart: {{ template "artifactory-ha.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + id: {{ template "artifactory-ha.name" . }}-backup-pv + type: nfs-volume +spec: + {{- if .Values.artifactory.persistence.nfs.mountOptions }} + mountOptions: +{{ toYaml .Values.artifactory.persistence.nfs.mountOptions | indent 4 }} + {{- end }} + capacity: + storage: {{ .Values.artifactory.persistence.nfs.capacity }} + accessModes: + - ReadWriteOnce + persistentVolumeReclaimPolicy: Retain + nfs: + server: {{ .Values.artifactory.persistence.nfs.ip }} + path: "{{ .Values.artifactory.persistence.nfs.haBackupMount }}" + readOnly: false +--- +kind: PersistentVolumeClaim +apiVersion: v1 +metadata: + name: {{ template "artifactory-ha.fullname" . }}-backup-pvc + labels: + app: {{ template "artifactory-ha.name" . }} + chart: {{ template "artifactory-ha.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + type: nfs-volume +spec: + accessModes: + - ReadWriteOnce + storageClassName: "" + resources: + requests: + storage: {{ .Values.artifactory.persistence.nfs.capacity }} + selector: + matchLabels: + id: {{ template "artifactory-ha.name" . }}-backup-pv + app: {{ template "artifactory-ha.name" . }} + release: {{ .Release.Name }} +{{- end }} diff --git a/charts/jfrog/artifactory-ha/107.104.10/templates/artifactory-node-pdb.yaml b/charts/jfrog/artifactory-ha/107.104.10/templates/artifactory-node-pdb.yaml new file mode 100644 index 0000000000..46c6dac21d --- /dev/null +++ b/charts/jfrog/artifactory-ha/107.104.10/templates/artifactory-node-pdb.yaml @@ -0,0 +1,26 @@ +{{- if gt (.Values.artifactory.node.replicaCount | int) 0 -}} +{{- if .Values.artifactory.node.minAvailable -}} +{{- if semverCompare " + mkdir -p {{ tpl .Values.artifactory.persistence.fileSystem.existingSharedClaim.dataDir . }}; + resources: +{{ toYaml .Values.initContainers.resources | indent 10 }} + volumeMounts: + - mountPath: {{ .Values.artifactory.persistence.mountPath | quote }} + name: volume + {{- end }} + {{- end }} + {{- if .Values.artifactory.deleteDBPropertiesOnStartup }} + - name: "delete-db-properties" + image: {{ include "artifactory-ha.getImageInfoByValue" (list . "initContainers") }} + imagePullPolicy: {{ .Values.initContainers.image.pullPolicy }} + command: + - 'bash' + - '-c' + - 'rm -fv {{ .Values.artifactory.persistence.mountPath }}/etc/db.properties' + resources: +{{ toYaml .Values.initContainers.resources | indent 10 }} + volumeMounts: + - mountPath: {{ .Values.artifactory.persistence.mountPath | quote }} + name: volume + {{- end }} + {{- end }} + {{- if and .Values.artifactory.node.waitForPrimaryStartup.enabled }} + - name: "wait-for-primary" + image: {{ include "artifactory-ha.getImageInfoByValue" (list . "initContainers") }} + imagePullPolicy: {{ .Values.initContainers.image.pullPolicy }} + {{- if .Values.containerSecurityContext.enabled }} + securityContext: {{- omit .Values.containerSecurityContext "enabled" | toYaml | nindent 10 }} + {{- end }} + command: + - 'bash' + - '-c' + - > + echo "Waiting for primary node to be ready..."; + {{- if and .Values.artifactory.node.waitForPrimaryStartup.enabled .Values.artifactory.node.waitForPrimaryStartup.time }} + echo "Sleeping to allow time for primary node to come up"; + sleep {{ .Values.artifactory.node.waitForPrimaryStartup.time }}; + {{- else }} + ready=false; + while ! $ready; do echo Primary not ready. Waiting...; + timeout 2s bash -c " + if [[ -e "{{ .Values.artifactory.persistence.mountPath }}/etc/filebeat.yaml" ]]; then chmod 644 {{ .Values.artifactory.persistence.mountPath }}/etc/filebeat.yaml; fi; + echo "Copy system.yaml to {{ .Values.artifactory.persistence.mountPath }}/etc"; + mkdir -p {{ .Values.artifactory.persistence.mountPath }}/etc; + mkdir -p {{ .Values.artifactory.persistence.mountPath }}/etc/access/keys/trusted; + {{- if .Values.systemYamlOverride.existingSecret }} + cp -fv /tmp/etc/{{ .Values.systemYamlOverride.dataKey }} {{ .Values.artifactory.persistence.mountPath }}/etc/system.yaml; + {{- else }} + cp -fv /tmp/etc/system.yaml {{ .Values.artifactory.persistence.mountPath }}/etc/system.yaml; + {{- end }} + echo "Copy binarystore.xml file"; + mkdir -p {{ .Values.artifactory.persistence.mountPath }}/etc/artifactory; + cp -fv /tmp/etc/artifactory/binarystore.xml {{ .Values.artifactory.persistence.mountPath }}/etc/artifactory/binarystore.xml; + echo "Removing join.key file"; + rm -fv {{ .Values.artifactory.persistence.mountPath }}/etc/security/join.key; + {{- if .Values.access.resetAccessCAKeys }} + echo "Resetting Access CA Keys - load from database"; + {{- end }} + {{- if .Values.access.customCertificatesSecretName }} + echo "Load custom certificates from database"; + {{- end }} + {{- if .Values.jfconnect.customCertificatesSecretName }} + echo "Load custom certificates from database"; + {{- end }} + {{- if or .Values.artifactory.masterKey .Values.global.masterKey .Values.artifactory.masterKeySecretName .Values.global.masterKeySecretName }} + echo "Copy masterKey to {{ .Values.artifactory.persistence.mountPath }}/etc/security"; + mkdir -p {{ .Values.artifactory.persistence.mountPath }}/etc/security; + echo -n ${ARTIFACTORY_MASTER_KEY} > {{ .Values.artifactory.persistence.mountPath }}/etc/security/master.key; + env: + - name: ARTIFACTORY_MASTER_KEY + valueFrom: + secretKeyRef: + {{- if or (not .Values.artifactory.unifiedSecretInstallation) (or .Values.artifactory.masterKeySecretName .Values.global.masterKeySecretName) }} + name: {{ include "artifactory-ha.masterKeySecretName" . }} + {{- else }} + name: "{{ template "artifactory.unifiedSecretPrependReleaseName" . }}-unified-secret" + {{- end }} + key: master-key + {{- end }} + resources: +{{ toYaml .Values.initContainers.resources | indent 10 }} + volumeMounts: + - name: volume + mountPath: {{ .Values.artifactory.persistence.mountPath | quote }} + + ######################## SystemYaml ######################### + {{- if or (not .Values.artifactory.unifiedSecretInstallation) .Values.systemYamlOverride.existingSecret }} + - name: systemyaml + {{- else }} + - name: {{ include "artifactory-ha.unifiedCustomSecretVolumeName" . }} + {{- end }} + {{- if .Values.systemYamlOverride.existingSecret }} + mountPath: "/tmp/etc/{{.Values.systemYamlOverride.dataKey}}" + subPath: {{ .Values.systemYamlOverride.dataKey }} + {{- else }} + mountPath: "/tmp/etc/system.yaml" + subPath: system.yaml + {{- end }} + + ######################## Binarystore ########################## + {{- if or (not .Values.artifactory.unifiedSecretInstallation) .Values.artifactory.persistence.customBinarystoreXmlSecret }} + - name: binarystore-xml + {{- else }} + - name: {{ include "artifactory-ha.unifiedCustomSecretVolumeName" . }} + {{- end }} + mountPath: "/tmp/etc/artifactory/binarystore.xml" + subPath: binarystore.xml + + ######################## CustomCertificates ########################## + {{- if or .Values.artifactory.customCertificates.enabled .Values.global.customCertificates.enabled }} + - name: copy-custom-certificates + image: {{ include "artifactory-ha.getImageInfoByValue" (list . "initContainers") }} + imagePullPolicy: {{ .Values.initContainers.image.pullPolicy }} + {{- if .Values.containerSecurityContext.enabled }} + securityContext: {{- omit .Values.containerSecurityContext "enabled" | toYaml | nindent 10 }} + {{- end }} + + resources: +{{ toYaml .Values.initContainers.resources | indent 10 }} + command: + - 'bash' + - '-c' + - > +{{ include "artifactory-ha.copyCustomCerts" . | indent 10 }} + volumeMounts: + - name: volume + mountPath: {{ .Values.artifactory.persistence.mountPath }} + - name: ca-certs + mountPath: "/tmp/certs" + {{- end }} + + {{- if .Values.artifactory.circleOfTrustCertificatesSecret }} + - name: copy-circle-of-trust-certificates + image: {{ include "artifactory-ha.getImageInfoByValue" (list . "initContainers") }} + imagePullPolicy: {{ .Values.initContainers.image.pullPolicy }} + {{- if .Values.containerSecurityContext.enabled }} + securityContext: {{- omit .Values.containerSecurityContext "enabled" | toYaml | nindent 10 }} + {{- end }} + resources: +{{ toYaml .Values.initContainers.resources | indent 10 }} + command: + - 'bash' + - '-c' + - > +{{ include "artifactory.copyCircleOfTrustCertsCerts" . | indent 10 }} + volumeMounts: + - name: volume + mountPath: {{ .Values.artifactory.persistence.mountPath }} + - name: circle-of-trust-certs + mountPath: "/tmp/circleoftrustcerts" + {{- end }} + + {{- if .Values.waitForDatabase }} + {{- if or .Values.postgresql.enabled }} + - name: "wait-for-db" + image: {{ include "artifactory-ha.getImageInfoByValue" (list . "initContainers") }} + imagePullPolicy: {{ .Values.initContainers.image.pullPolicy }} + command: + - /bin/bash + - -c + - | + echo "Waiting for postgresql to come up" + ready=false; + while ! $ready; do echo waiting; + timeout 2s bash -c " + {{- if .Values.artifactory.migration.preStartCommand }} + echo "Running custom preStartCommand command"; + {{ tpl .Values.artifactory.migration.preStartCommand . }}; + {{- end }} + scriptsPath="/opt/jfrog/artifactory/app/bin"; + mkdir -p $scriptsPath; + echo "Copy migration scripts and Run migration"; + cp -fv /tmp/migrate.sh $scriptsPath/migrate.sh; + cp -fv /tmp/migrationHelmInfo.yaml $scriptsPath/migrationHelmInfo.yaml; + cp -fv /tmp/migrationStatus.sh $scriptsPath/migrationStatus.sh; + mkdir -p {{ .Values.artifactory.persistence.mountPath }}/log; + bash $scriptsPath/migrationStatus.sh {{ include "artifactory-ha.app.version" . }} {{ .Values.artifactory.migration.timeoutSeconds }} > >(tee {{ .Values.artifactory.persistence.mountPath }}/log/helm-migration.log) 2>&1; + resources: +{{ toYaml .Values.artifactory.node.resources | indent 10 }} + env: + {{- if and (not .Values.waitForDatabase) (not .Values.postgresql.enabled) }} + - name: SKIP_WAIT_FOR_EXTERNAL_DB + value: "true" + {{- end }} + {{- if or .Values.database.secrets.user .Values.database.user }} + - name: JF_SHARED_DATABASE_USERNAME + valueFrom: + secretKeyRef: + {{- if .Values.database.secrets.user }} + name: {{ tpl .Values.database.secrets.user.name . }} + key: {{ tpl .Values.database.secrets.user.key . }} + {{- else if .Values.database.user }} + {{- if not .Values.artifactory.unifiedSecretInstallation }} + name: {{ template "artifactory-ha.fullname" . }}-database-creds + {{- else }} + name: "{{ template "artifactory.unifiedSecretPrependReleaseName" . }}-unified-secret" + {{- end }} + key: db-user + {{- end }} + {{- end }} + {{ if or .Values.database.secrets.password .Values.database.password .Values.postgresql.enabled }} + - name: JF_SHARED_DATABASE_PASSWORD + valueFrom: + secretKeyRef: + {{- if .Values.database.secrets.password }} + name: {{ tpl .Values.database.secrets.password.name . }} + key: {{ tpl .Values.database.secrets.password.key . }} + {{- else if .Values.database.password }} + {{- if not .Values.artifactory.unifiedSecretInstallation }} + name: {{ template "artifactory-ha.fullname" . }}-database-creds + {{- else }} + name: "{{ template "artifactory.unifiedSecretPrependReleaseName" . }}-unified-secret" + {{- end }} + key: db-password + {{- else if .Values.postgresql.enabled }} + name: {{ .Release.Name }}-postgresql + key: postgresql-password + {{- end }} + {{- end }} + {{- if or .Values.database.secrets.url .Values.database.url }} + - name: JF_SHARED_DATABASE_URL + valueFrom: + secretKeyRef: + {{- if .Values.database.secrets.url }} + name: {{ tpl .Values.database.secrets.url.name . }} + key: {{ tpl .Values.database.secrets.url.key . }} + {{- else if .Values.database.url }} + {{- if not .Values.artifactory.unifiedSecretInstallation }} + name: {{ template "artifactory-ha.fullname" . }}-database-creds + {{- else }} + name: "{{ template "artifactory.unifiedSecretPrependReleaseName" . }}-unified-secret" + {{- end }} + key: db-url + {{- end }} + {{- end }} + - name: JF_SHARED_NODE_HAENABLED + value: "true" +{{- with .Values.artifactory.extraEnvironmentVariables }} +{{ tpl (toYaml .) $ | indent 8 }} +{{- end }} + volumeMounts: + - name: migration-scripts + mountPath: "/tmp/migrate.sh" + subPath: migrate.sh + - name: migration-scripts + mountPath: "/tmp/migrationHelmInfo.yaml" + subPath: migrationHelmInfo.yaml + - name: migration-scripts + mountPath: "/tmp/migrationStatus.sh" + subPath: migrationStatus.sh + - name: volume + mountPath: "{{ .Values.artifactory.persistence.mountPath }}" + {{- if eq .Values.artifactory.persistence.type "file-system" }} + {{- if .Values.artifactory.persistence.fileSystem.existingSharedClaim.enabled }} + {{- range $sharedClaimNumber, $e := until (.Values.artifactory.persistence.fileSystem.existingSharedClaim.numberOfExistingClaims|int) }} + - name: artifactory-ha-data-{{ $sharedClaimNumber }} + mountPath: "{{ tpl $.Values.artifactory.persistence.fileSystem.existingSharedClaim.dataDir $ }}/filestore{{ $sharedClaimNumber }}" + {{- end }} + - name: artifactory-ha-backup + mountPath: "{{ $.Values.artifactory.persistence.fileSystem.existingSharedClaim.backupDir }}" + {{- end }} + {{- end }} + {{- if or .Values.artifactory.customVolumeMounts .Values.global.customVolumeMounts }} +{{ tpl (include "artifactory-ha.customVolumeMounts" .) . | indent 8 }} + {{- end }} + + ######################## Artifactory persistence nfs ########################## + {{- if eq .Values.artifactory.persistence.type "nfs" }} + - name: artifactory-ha-data + mountPath: "{{ .Values.artifactory.persistence.nfs.dataDir }}" + - name: artifactory-ha-backup + mountPath: "{{ .Values.artifactory.persistence.nfs.backupDir }}" + {{- else }} + + + ######################## Artifactory persistence binarystore Xml ########################## + {{- if or (not .Values.artifactory.unifiedSecretInstallation) .Values.artifactory.persistence.customBinarystoreXmlSecret }} + - name: binarystore-xml + {{- else }} + - name: {{ include "artifactory-ha.unifiedCustomSecretVolumeName" . }} + {{- end }} + mountPath: "/tmp/etc/artifactory/binarystore.xml" + subPath: binarystore.xml + {{- end }} + + ######################## Artifactory persistence google storage ########################## + {{- if .Values.artifactory.persistence.googleStorage.gcpServiceAccount.enabled }} + {{- if or (not .Values.artifactory.unifiedSecretInstallation) .Values.artifactory.persistence.googleStorage.gcpServiceAccount.customSecretName }} + - name: gcpcreds-json + {{- else }} + - name: {{ include "artifactory-ha.unifiedCustomSecretVolumeName" . }} + {{- end }} + mountPath: "/artifactory_bootstrap/gcp.credentials.json" + subPath: gcp.credentials.json + {{- end }} + +{{- end }} + {{- if .Values.hostAliases }} + hostAliases: +{{ toYaml .Values.hostAliases | indent 6 }} + {{- end }} + containers: + {{- if .Values.splitServicesToContainers }} + - name: {{ .Values.router.name }} + image: {{ include "artifactory-ha.getImageInfoByValue" (list . "router") }} + imagePullPolicy: {{ .Values.router.image.pullPolicy }} + {{- if .Values.containerSecurityContext.enabled }} + securityContext: {{- omit .Values.containerSecurityContext "enabled" | toYaml | nindent 10 }} + {{- end }} + command: + - '/bin/bash' + - '-c' + - > + exec /opt/jfrog/router/app/bin/entrypoint-router.sh + {{- with .Values.router.lifecycle }} + lifecycle: +{{ toYaml . | indent 10 }} + {{- end }} + env: + - name: JF_ROUTER_TOPOLOGY_LOCAL_REQUIREDSERVICETYPES + value: {{ include "artifactory-ha.router.requiredServiceTypes" . }} +{{- with .Values.router.extraEnvironmentVariables }} +{{ tpl (toYaml .) $ | indent 8 }} +{{- end }} + volumeMounts: + - name: volume + mountPath: {{ .Values.router.persistence.mountPath | quote }} +{{- with .Values.router.customVolumeMounts }} +{{ tpl . $ | indent 8 }} +{{- end }} + resources: +{{ toYaml .Values.router.resources | indent 10 }} + {{- if .Values.router.startupProbe.enabled }} + startupProbe: +{{ tpl .Values.router.startupProbe.config . | indent 10 }} + {{- end }} +{{- if .Values.router.readinessProbe.enabled }} + readinessProbe: +{{ tpl .Values.router.readinessProbe.config . | indent 10 }} + {{- end }} + {{- if .Values.router.livenessProbe.enabled }} + livenessProbe: +{{ tpl .Values.router.livenessProbe.config . | indent 10 }} + {{- end }} + {{- if .Values.frontend.enabled }} + - name: {{ .Values.frontend.name }} + image: {{ include "artifactory-ha.getImageInfoByValue" (list . "artifactory") }} + imagePullPolicy: {{ .Values.artifactory.image.pullPolicy }} + {{- if .Values.containerSecurityContext.enabled }} + securityContext: {{- omit .Values.containerSecurityContext "enabled" | toYaml | nindent 10 }} + {{- end }} + command: + - '/bin/bash' + - '-c' + - > + exec /opt/jfrog/artifactory/app/third-party/node/bin/node /opt/jfrog/artifactory/app/frontend/bin/server/dist/bundle.js /opt/jfrog/artifactory/app/frontend + {{- with .Values.frontend.lifecycle }} + lifecycle: +{{ toYaml . | indent 10 }} + {{- end }} + env: + - name: JF_SHARED_NODE_ID + valueFrom: + fieldRef: + fieldPath: metadata.name + - name : JF_SHARED_NODE_HAENABLED + value: "true" +{{- with .Values.frontend.extraEnvironmentVariables }} +{{ tpl (toYaml .) $ | indent 8 }} +{{- end }} + volumeMounts: + {{- if or .Values.artifactory.customVolumeMounts .Values.global.customVolumeMounts }} +{{ tpl (include "artifactory-ha.customVolumeMounts" .) . | indent 8 }} + {{- end }} + - name: volume + mountPath: {{ .Values.artifactory.persistence.mountPath | quote }} + resources: +{{ toYaml .Values.frontend.resources | indent 10 }} + {{- if .Values.frontend.startupProbe.enabled }} + startupProbe: +{{ tpl .Values.frontend.startupProbe.config . | indent 10 }} + {{- end }} + {{- if .Values.frontend.livenessProbe.enabled }} + livenessProbe: +{{ tpl .Values.frontend.livenessProbe.config . | indent 10 }} + {{- end }} + {{- end }} + {{- if .Values.evidence.enabled }} + - name: {{ .Values.evidence.name }} + image: {{ include "artifactory-ha.getImageInfoByValue" (list . "artifactory") }} + imagePullPolicy: {{ .Values.artifactory.image.pullPolicy }} + {{- if .Values.containerSecurityContext.enabled }} + securityContext: {{- omit .Values.containerSecurityContext "enabled" | toYaml | nindent 10 }} + {{- end }} + command: + - '/bin/bash' + - '-c' + - > + exec /opt/jfrog/artifactory/app/evidence/bin/jf-evidence start + {{- with .Values.evidence.lifecycle }} + lifecycle: +{{ toYaml . | indent 10 }} + {{- end }} + env: + - name: JF_SHARED_NODE_ID + valueFrom: + fieldRef: + fieldPath: metadata.name + {{- if or .Values.database.secrets.user .Values.database.user }} + - name: JF_SHARED_DATABASE_USERNAME + valueFrom: + secretKeyRef: + {{- if .Values.database.secrets.user }} + name: {{ tpl .Values.database.secrets.user.name . }} + key: {{ tpl .Values.database.secrets.user.key . }} + {{- else if .Values.database.user }} + {{- if not .Values.artifactory.unifiedSecretInstallation }} + name: {{ template "artifactory-ha.fullname" . }}-database-creds + {{- else }} + name: "{{ template "artifactory.unifiedSecretPrependReleaseName" . }}-unified-secret" + {{- end }} + key: db-user + {{- end }} + {{- end }} + {{ if or .Values.database.secrets.password .Values.database.password .Values.postgresql.enabled }} + - name: JF_SHARED_DATABASE_PASSWORD + valueFrom: + secretKeyRef: + {{- if .Values.database.secrets.password }} + name: {{ tpl .Values.database.secrets.password.name . }} + key: {{ tpl .Values.database.secrets.password.key . }} + {{- else if .Values.database.password }} + {{- if not .Values.artifactory.unifiedSecretInstallation }} + name: {{ template "artifactory-ha.fullname" . }}-database-creds + {{- else }} + name: "{{ template "artifactory.unifiedSecretPrependReleaseName" . }}-unified-secret" + {{- end }} + key: db-password + {{- else if .Values.postgresql.enabled }} + name: {{ .Release.Name }}-postgresql + key: postgresql-password + {{- end }} + {{- end }} + {{- if or .Values.database.secrets.url .Values.database.url }} + - name: JF_SHARED_DATABASE_URL + valueFrom: + secretKeyRef: + {{- if .Values.database.secrets.url }} + name: {{ tpl .Values.database.secrets.url.name . }} + key: {{ tpl .Values.database.secrets.url.key . }} + {{- else if .Values.database.url }} + {{- if not .Values.artifactory.unifiedSecretInstallation }} + name: {{ template "artifactory-ha.fullname" . }}-database-creds + {{- else }} + name: "{{ template "artifactory.unifiedSecretPrependReleaseName" . }}-unified-secret" + {{- end }} + key: db-url + {{- end }} + {{- end }} +{{- with .Values.evidence.extraEnvironmentVariables }} +{{ tpl (toYaml .) $ | indent 8 }} +{{- end }} + ports: + - containerPort: {{ .Values.evidence.internalPort }} + name: http-evidence + - containerPort: {{ .Values.evidence.externalPort }} + name: grpc-evidence + volumeMounts: + {{- if or .Values.artifactory.customVolumeMounts .Values.global.customVolumeMounts }} +{{ tpl (include "artifactory-ha.customVolumeMounts" .) . | indent 8 }} + {{- end }} + - name: volume + mountPath: {{ .Values.artifactory.persistence.mountPath | quote }} + resources: +{{ toYaml .Values.evidence.resources | indent 10 }} + {{- if .Values.evidence.startupProbe.enabled }} + startupProbe: +{{ tpl .Values.evidence.startupProbe.config . | indent 10 }} + {{- end }} + {{- if .Values.evidence.livenessProbe.enabled }} + livenessProbe: +{{ tpl .Values.evidence.livenessProbe.config . | indent 10 }} + {{- end }} + {{- end }} + {{- if .Values.metadata.enabled }} + - name: {{ .Values.metadata.name }} + image: {{ include "artifactory-ha.getImageInfoByValue" (list . "metadata") }} + imagePullPolicy: {{ .Values.artifactory.image.pullPolicy }} + {{- if .Values.containerSecurityContext.enabled }} + securityContext: {{- omit .Values.containerSecurityContext "enabled" | toYaml | nindent 10 }} + {{- end }} + command: + - '/bin/bash' + - '-c' + - > + exec /opt/jfrog/artifactory/app/metadata/bin/jf-metadata start + {{- with .Values.metadata.lifecycle }} + lifecycle: +{{ toYaml . | indent 10 }} + {{- end }} + env: + - name: JF_SHARED_NODE_ID + valueFrom: + fieldRef: + fieldPath: metadata.name + {{- if or .Values.database.secrets.user .Values.database.user }} + - name: JF_SHARED_DATABASE_USERNAME + valueFrom: + secretKeyRef: + {{- if .Values.database.secrets.user }} + name: {{ tpl .Values.database.secrets.user.name . }} + key: {{ tpl .Values.database.secrets.user.key . }} + {{- else if .Values.database.user }} + {{- if not .Values.artifactory.unifiedSecretInstallation }} + name: {{ template "artifactory-ha.fullname" . }}-database-creds + {{- else }} + name: "{{ template "artifactory.unifiedSecretPrependReleaseName" . }}-unified-secret" + {{- end }} + key: db-user + {{- end }} + {{- end }} + {{ if or .Values.database.secrets.password .Values.database.password .Values.postgresql.enabled }} + - name: JF_SHARED_DATABASE_PASSWORD + valueFrom: + secretKeyRef: + {{- if .Values.database.secrets.password }} + name: {{ tpl .Values.database.secrets.password.name . }} + key: {{ tpl .Values.database.secrets.password.key . }} + {{- else if .Values.database.password }} + {{- if not .Values.artifactory.unifiedSecretInstallation }} + name: {{ template "artifactory-ha.fullname" . }}-database-creds + {{- else }} + name: "{{ template "artifactory.unifiedSecretPrependReleaseName" . }}-unified-secret" + {{- end }} + key: db-password + {{- else if .Values.postgresql.enabled }} + name: {{ .Release.Name }}-postgresql + key: postgresql-password + {{- end }} + {{- end }} + {{- if or .Values.database.secrets.url .Values.database.url }} + - name: JF_SHARED_DATABASE_URL + valueFrom: + secretKeyRef: + {{- if .Values.database.secrets.url }} + name: {{ tpl .Values.database.secrets.url.name . }} + key: {{ tpl .Values.database.secrets.url.key . }} + {{- else if .Values.database.url }} + {{- if not .Values.artifactory.unifiedSecretInstallation }} + name: {{ template "artifactory-ha.fullname" . }}-database-creds + {{- else }} + name: "{{ template "artifactory.unifiedSecretPrependReleaseName" . }}-unified-secret" + {{- end }} + key: db-url + {{- end }} + {{- end }} +{{- with .Values.metadata.extraEnvironmentVariables }} +{{ tpl (toYaml .) $ | indent 8 }} +{{- end }} + volumeMounts: + {{- if or .Values.artifactory.customVolumeMounts .Values.global.customVolumeMounts }} +{{ tpl (include "artifactory-ha.customVolumeMounts" .) . | indent 8 }} + {{- end }} + - name: volume + mountPath: {{ .Values.artifactory.persistence.mountPath | quote }} + resources: +{{ toYaml .Values.metadata.resources | indent 10 }} + {{- if .Values.metadata.startupProbe.enabled }} + startupProbe: +{{ tpl .Values.metadata.startupProbe.config . | indent 10 }} + {{- end }} + {{- if .Values.metadata.livenessProbe.enabled }} + livenessProbe: +{{ tpl .Values.metadata.livenessProbe.config . | indent 10 }} + {{- end }} + {{- end }} + {{- if .Values.onemodel.enabled }} + - name: {{ .Values.onemodel.name }} + image: {{ include "artifactory-ha.getImageInfoByValue" (list . "artifactory") }} + imagePullPolicy: {{ .Values.artifactory.image.pullPolicy }} + {{- if .Values.containerSecurityContext.enabled }} + securityContext: {{- omit .Values.containerSecurityContext "enabled" | toYaml | nindent 10 }} + {{- end }} + command: + - '/bin/bash' + - '-c' + - > + exec /opt/jfrog/artifactory/app/onemodel/bin/entrypoint-onemodel.sh start + {{- with .Values.onemodel.lifecycle }} + lifecycle: +{{ toYaml . | indent 10 }} + {{- end }} + env: + - name: JF_SHARED_NODE_ID + valueFrom: + fieldRef: + fieldPath: metadata.name + - name: JF_ONEMODEL_LISTENADDR + value: "0.0.0.0" +{{- with .Values.onemodel.extraEnvironmentVariables }} +{{ tpl (toYaml .) $ | indent 8 }} +{{- end }} + volumeMounts: + {{- if or .Values.artifactory.customVolumeMounts .Values.global.customVolumeMounts }} +{{ tpl (include "artifactory-ha.customVolumeMounts" .) . | indent 8 }} + {{- end }} + - name: volume + mountPath: {{ .Values.artifactory.persistence.mountPath | quote }} + resources: +{{ toYaml .Values.onemodel.resources | indent 10 }} + {{- if .Values.onemodel.startupProbe.enabled }} + startupProbe: +{{ tpl .Values.onemodel.startupProbe.config . | indent 10 }} + {{- end }} + {{- if .Values.onemodel.livenessProbe.enabled }} + livenessProbe: +{{ tpl .Values.onemodel.livenessProbe.config . | indent 10 }} + {{- end }} + {{- if .Values.onemodel.readinessProbe.enabled }} + readinessProbe: +{{ tpl .Values.onemodel.readinessProbe.config . | indent 10 }} + {{- end }} + {{- end }} + {{- if .Values.event.enabled }} + - name: {{ .Values.event.name }} + image: {{ include "artifactory-ha.getImageInfoByValue" (list . "artifactory") }} + imagePullPolicy: {{ .Values.artifactory.image.pullPolicy }} + {{- if .Values.containerSecurityContext.enabled }} + securityContext: {{- omit .Values.containerSecurityContext "enabled" | toYaml | nindent 10 }} + {{- end }} + command: + - '/bin/bash' + - '-c' + - > + exec /opt/jfrog/artifactory/app/event/bin/jf-event start + {{- with .Values.event.lifecycle }} + lifecycle: +{{ toYaml . | indent 10 }} + {{- end }} + env: + - name: JF_SHARED_NODE_ID + valueFrom: + fieldRef: + fieldPath: metadata.name +{{- with .Values.event.extraEnvironmentVariables }} +{{ tpl (toYaml .) $ | indent 8 }} +{{- end }} + volumeMounts: + {{- if or .Values.artifactory.customVolumeMounts .Values.global.customVolumeMounts }} +{{ tpl (include "artifactory-ha.customVolumeMounts" .) . | indent 8 }} + {{- end }} + - name: volume + mountPath: {{ .Values.artifactory.persistence.mountPath | quote }} + resources: +{{ toYaml .Values.event.resources | indent 10 }} + {{- if .Values.event.startupProbe.enabled }} + startupProbe: +{{ tpl .Values.event.startupProbe.config . | indent 10 }} + {{- end }} + {{- if .Values.event.livenessProbe.enabled }} + livenessProbe: +{{ tpl .Values.event.livenessProbe.config . | indent 10 }} + {{- end }} + {{- end }} + {{- if .Values.jfconnect.enabled }} + - name: {{ .Values.jfconnect.name }} + image: {{ include "artifactory-ha.getImageInfoByValue" (list . "artifactory") }} + imagePullPolicy: {{ .Values.artifactory.image.pullPolicy }} + {{- if .Values.containerSecurityContext.enabled }} + securityContext: {{- omit .Values.containerSecurityContext "enabled" | toYaml | nindent 10 }} + {{- end }} + command: + - '/bin/bash' + - '-c' + - > + exec /opt/jfrog/artifactory/app/jfconnect/bin/jf-connect start + {{- with .Values.jfconnect.lifecycle }} + lifecycle: +{{ toYaml . | indent 10 }} + {{- end }} + env: + - name: JF_SHARED_NODE_ID + valueFrom: + fieldRef: + fieldPath: metadata.name +{{- with .Values.jfconnect.extraEnvironmentVariables }} +{{ tpl (toYaml .) $ | indent 8 }} +{{- end }} + volumeMounts: + {{- if or .Values.artifactory.customVolumeMounts .Values.global.customVolumeMounts }} +{{ tpl (include "artifactory-ha.customVolumeMounts" .) . | indent 8 }} + {{- end }} + - name: volume + mountPath: {{ .Values.artifactory.persistence.mountPath | quote }} + resources: +{{ toYaml .Values.jfconnect.resources | indent 10 }} + {{- if .Values.jfconnect.startupProbe.enabled }} + startupProbe: +{{ tpl .Values.jfconnect.startupProbe.config . | indent 10 }} + {{- end }} + {{- if .Values.jfconnect.livenessProbe.enabled }} + livenessProbe: +{{ tpl .Values.jfconnect.livenessProbe.config . | indent 10 }} + {{- end }} + {{- end }} + {{- if .Values.topology.enabled }} + - name: {{ .Values.topology.name }} + image: {{ include "artifactory-ha.getImageInfoByValue" (list . "artifactory") }} + imagePullPolicy: {{ .Values.artifactory.image.pullPolicy }} + {{- if .Values.containerSecurityContext.enabled }} + securityContext: {{- omit .Values.containerSecurityContext "enabled" | toYaml | nindent 10 }} + {{- end }} + command: + - '/bin/bash' + - '-c' + - > + exec /opt/jfrog/artifactory/app/topology/bin/entrypoint-topology.sh + {{- with .Values.topology.lifecycle }} + lifecycle: +{{ toYaml . | indent 10 }} + {{- end }} + env: + - name: JF_SHARED_NODE_ID + valueFrom: + fieldRef: + fieldPath: metadata.name + {{- if or .Values.database.secrets.user .Values.database.user }} + - name: JF_SHARED_DATABASE_USERNAME + valueFrom: + secretKeyRef: + {{- if .Values.database.secrets.user }} + name: {{ tpl .Values.database.secrets.user.name . }} + key: {{ tpl .Values.database.secrets.user.key . }} + {{- else if .Values.database.user }} + {{- if not .Values.artifactory.unifiedSecretInstallation }} + name: {{ template "artifactory-ha.fullname" . }}-database-creds + {{- else }} + name: "{{ template "artifactory.unifiedSecretPrependReleaseName" . }}-unified-secret" + {{- end }} + key: db-user + {{- end }} + {{- end }} + {{ if or .Values.database.secrets.password .Values.database.password .Values.postgresql.enabled }} + - name: JF_SHARED_DATABASE_PASSWORD + valueFrom: + secretKeyRef: + {{- if .Values.database.secrets.password }} + name: {{ tpl .Values.database.secrets.password.name . }} + key: {{ tpl .Values.database.secrets.password.key . }} + {{- else if .Values.database.password }} + {{- if not .Values.artifactory.unifiedSecretInstallation }} + name: {{ template "artifactory-ha.fullname" . }}-database-creds + {{- else }} + name: "{{ template "artifactory.unifiedSecretPrependReleaseName" . }}-unified-secret" + {{- end }} + key: db-password + {{- else if .Values.postgresql.enabled }} + name: {{ .Release.Name }}-postgresql + key: postgresql-password + {{- end }} + {{- end }} + {{- if or .Values.database.secrets.url .Values.database.url }} + - name: JF_SHARED_DATABASE_URL + valueFrom: + secretKeyRef: + {{- if .Values.database.secrets.url }} + name: {{ tpl .Values.database.secrets.url.name . }} + key: {{ tpl .Values.database.secrets.url.key . }} + {{- else if .Values.database.url }} + {{- if not .Values.artifactory.unifiedSecretInstallation }} + name: {{ template "artifactory-ha.fullname" . }}-database-creds + {{- else }} + name: "{{ template "artifactory.unifiedSecretPrependReleaseName" . }}-unified-secret" + {{- end }} + key: db-url + {{- end }} + {{- end }} +{{- with .Values.topology.extraEnvironmentVariables }} +{{ tpl (toYaml .) $ | indent 8 }} +{{- end }} + ports: + - containerPort: {{ .Values.topology.internalPort }} + name: http-topology + volumeMounts: + {{- if or .Values.artifactory.customVolumeMounts .Values.global.customVolumeMounts }} +{{ tpl (include "artifactory-ha.customVolumeMounts" .) . | indent 8 }} + {{- end }} + - name: volume + mountPath: {{ .Values.artifactory.persistence.mountPath | quote }} + resources: +{{ toYaml .Values.topology.resources | indent 10 }} + {{- if .Values.topology.startupProbe.enabled }} + startupProbe: +{{ tpl .Values.topology.startupProbe.config . | indent 10 }} + {{- end }} + {{- if .Values.topology.readinessProbe.enabled }} + readinessProbe: +{{ tpl .Values.topology.readinessProbe.config . | indent 10 }} + {{- end }} + {{- if .Values.topology.livenessProbe.enabled }} + livenessProbe: +{{ tpl .Values.topology.livenessProbe.config . | indent 10 }} + {{- end }} + {{- end }} + {{- if .Values.observability.enabled }} + - name: {{ .Values.observability.name }} + image: {{ include "artifactory-ha.getImageInfoByValue" (list . "observability") }} + imagePullPolicy: {{ .Values.artifactory.image.pullPolicy }} + {{- if .Values.containerSecurityContext.enabled }} + securityContext: {{- omit .Values.containerSecurityContext "enabled" | toYaml | nindent 10 }} + {{- end }} + command: + - '/bin/bash' + - '-c' + - > + exec /opt/jfrog/artifactory/app/observability/bin/jf-observability start + {{- with .Values.observability.lifecycle }} + lifecycle: +{{ toYaml . | indent 10 }} + {{- end }} + env: + - name: JF_SHARED_NODE_ID + valueFrom: + fieldRef: + fieldPath: metadata.name +{{- with .Values.observability.extraEnvironmentVariables }} +{{ tpl (toYaml .) $ | indent 8 }} +{{- end }} + volumeMounts: + {{- if or .Values.artifactory.customVolumeMounts .Values.global.customVolumeMounts }} +{{ tpl (include "artifactory-ha.customVolumeMounts" .) . | indent 8 }} + {{- end }} + - name: volume + mountPath: {{ .Values.artifactory.persistence.mountPath | quote }} + resources: +{{ toYaml .Values.observability.resources | indent 10 }} + {{- if .Values.observability.startupProbe.enabled }} + startupProbe: +{{ tpl .Values.observability.startupProbe.config . | indent 10 }} + {{- end }} + {{- if .Values.observability.livenessProbe.enabled }} + livenessProbe: +{{ tpl .Values.observability.livenessProbe.config . | indent 10 }} + {{- end }} + {{- end }} + {{- if and .Values.access.enabled (not (.Values.access.runOnArtifactoryTomcat | default false)) }} + - name: {{ .Values.access.name }} + image: {{ include "artifactory-ha.getImageInfoByValue" (list . "artifactory") }} + imagePullPolicy: {{ .Values.artifactory.image.pullPolicy }} + {{- if .Values.containerSecurityContext.enabled }} + securityContext: {{- omit .Values.containerSecurityContext "enabled" | toYaml | nindent 10 }} + {{- end }} + {{- if .Values.access.resources }} + resources: +{{ toYaml .Values.access.resources | indent 10 }} + {{- end }} + command: + - '/bin/bash' + - '-c' + - > + set -e; + {{- if .Values.access.preStartCommand }} + echo "Running custom preStartCommand command"; + {{ tpl .Values.access.preStartCommand . }}; + {{- end }} + exec /opt/jfrog/artifactory/app/access/bin/entrypoint-access.sh + {{- with .Values.access.lifecycle }} + lifecycle: +{{ toYaml . | indent 10 }} + {{- end }} + env: + {{- if and (not .Values.waitForDatabase) (not .Values.postgresql.enabled) }} + - name: SKIP_WAIT_FOR_EXTERNAL_DB + value: "true" + {{- end }} + {{- if or .Values.database.secrets.user .Values.database.user }} + - name: JF_SHARED_DATABASE_USERNAME + valueFrom: + secretKeyRef: + {{- if .Values.database.secrets.user }} + name: {{ tpl .Values.database.secrets.user.name . }} + key: {{ tpl .Values.database.secrets.user.key . }} + {{- else if .Values.database.user }} + {{- if not .Values.artifactory.unifiedSecretInstallation }} + name: {{ template "artifactory-ha.fullname" . }}-database-creds + {{- else }} + name: "{{ template "artifactory.unifiedSecretPrependReleaseName" . }}-unified-secret" + {{- end }} + key: db-user + {{- end }} + {{- end }} + {{ if or .Values.database.secrets.password .Values.database.password .Values.postgresql.enabled }} + - name: JF_SHARED_DATABASE_PASSWORD + valueFrom: + secretKeyRef: + {{- if .Values.database.secrets.password }} + name: {{ tpl .Values.database.secrets.password.name . }} + key: {{ tpl .Values.database.secrets.password.key . }} + {{- else if .Values.database.password }} + {{- if not .Values.artifactory.unifiedSecretInstallation }} + name: {{ template "artifactory-ha.fullname" . }}-database-creds + {{- else }} + name: "{{ template "artifactory.unifiedSecretPrependReleaseName" . }}-unified-secret" + {{- end }} + key: db-password + {{- else if .Values.postgresql.enabled }} + name: {{ .Release.Name }}-postgresql + key: postgresql-password + {{- end }} + {{- end }} + {{- if or .Values.database.secrets.url .Values.database.url }} + - name: JF_SHARED_DATABASE_URL + valueFrom: + secretKeyRef: + {{- if .Values.database.secrets.url }} + name: {{ tpl .Values.database.secrets.url.name . }} + key: {{ tpl .Values.database.secrets.url.key . }} + {{- else if .Values.database.url }} + {{- if not .Values.artifactory.unifiedSecretInstallation }} + name: {{ template "artifactory-ha.fullname" . }}-database-creds + {{- else }} + name: "{{ template "artifactory.unifiedSecretPrependReleaseName" . }}-unified-secret" + {{- end }} + key: db-url + {{- end }} + {{- end }} +{{- with .Values.access.extraEnvironmentVariables }} +{{ tpl (toYaml .) $ | indent 8 }} +{{- end }} + volumeMounts: + {{- if .Values.artifactory.customPersistentVolumeClaim }} + - name: {{ .Values.artifactory.customPersistentVolumeClaim.name }} + mountPath: {{ .Values.artifactory.customPersistentVolumeClaim.mountPath }} + {{- end }} + {{- if .Values.artifactory.customPersistentPodVolumeClaim }} + - name: {{ .Values.artifactory.customPersistentPodVolumeClaim.name }} + mountPath: {{ .Values.artifactory.customPersistentPodVolumeClaim.mountPath }} + {{- end }} + {{- if .Values.aws.licenseConfigSecretName }} + - name: awsmp-product-license + mountPath: "/var/run/secrets/product-license" + {{- end }} + - name: volume + mountPath: "{{ .Values.artifactory.persistence.mountPath }}" + + ######################## Artifactory persistence fs ########################## + {{- if eq .Values.artifactory.persistence.type "file-system" }} + {{- if .Values.artifactory.persistence.fileSystem.existingSharedClaim.enabled }} + {{- range $sharedClaimNumber, $e := until (.Values.artifactory.persistence.fileSystem.existingSharedClaim.numberOfExistingClaims|int) }} + - name: artifactory-ha-data-{{ $sharedClaimNumber }} + mountPath: "{{ tpl $.Values.artifactory.persistence.fileSystem.existingSharedClaim.dataDir $ }}/filestore{{ $sharedClaimNumber }}" + {{- end }} + - name: artifactory-ha-backup + mountPath: "{{ $.Values.artifactory.persistence.fileSystem.existingSharedClaim.backupDir }}" + {{- end }} + {{- end }} + + ######################## Artifactory persistence nfs ########################## + {{- if eq .Values.artifactory.persistence.type "nfs" }} + - name: artifactory-ha-data + mountPath: "{{ .Values.artifactory.persistence.nfs.dataDir }}" + - name: artifactory-ha-backup + mountPath: "{{ .Values.artifactory.persistence.nfs.backupDir }}" + {{- else }} + + ######################## Artifactory persistence binarystore Xml ########################## + {{- if or (not .Values.artifactory.unifiedSecretInstallation) .Values.artifactory.persistence.customBinarystoreXmlSecret }} + - name: binarystore-xml + {{- else }} + - name: {{ include "artifactory-ha.unifiedCustomSecretVolumeName" . }} + {{- end }} + mountPath: "/tmp/etc/artifactory/binarystore.xml" + subPath: binarystore.xml + + ######################## Artifactory persistence google storage ########################## + {{- if .Values.artifactory.persistence.googleStorage.gcpServiceAccount.enabled }} + {{- if or (not .Values.artifactory.unifiedSecretInstallation) .Values.artifactory.persistence.googleStorage.gcpServiceAccount.customSecretName }} + - name: gcpcreds-json + {{- else }} + - name: {{ include "artifactory-ha.unifiedCustomSecretVolumeName" . }} + {{- end }} + mountPath: "/artifactory_bootstrap/gcp.credentials.json" + subPath: gcp.credentials.json + {{- end }} + + ######################## Artifactory ConfigMap ########################## + {{- if .Values.artifactory.configMapName }} + - name: bootstrap-config + mountPath: "/bootstrap/" + {{- end }} + + ######################## Artifactory license ########################## + {{- if or .Values.artifactory.license.secret .Values.artifactory.license.licenseKey }} + {{- if or (not .Values.artifactory.unifiedSecretInstallation) .Values.artifactory.license.secret }} + - name: artifactory-license + {{- else }} + - name: {{ include "artifactory-ha.unifiedCustomSecretVolumeName" . }} + {{- end }} + mountPath: "/artifactory_bootstrap/artifactory.cluster.license" + {{- if .Values.artifactory.license.secret }} + subPath: {{ .Values.artifactory.license.dataKey }} + {{- else if .Values.artifactory.license.licenseKey }} + subPath: artifactory.lic + {{- end }} + {{- end }} + {{- end }} + {{- if or .Values.artifactory.customVolumeMounts .Values.global.customVolumeMounts }} +{{ tpl (include "artifactory-ha.customVolumeMounts" .) . | indent 8 }} + {{- end }} + {{- if .Values.access.startupProbe.enabled }} + startupProbe: +{{ tpl .Values.access.startupProbe.config . | indent 10 }} + {{- end }} + {{- if semverCompare " + set -e; + {{- range .Values.artifactory.copyOnEveryStartup }} + {{- $targetPath := printf "%s/%s" $.Values.artifactory.persistence.mountPath .target }} + {{- $baseDirectory := regexFind ".*/" $targetPath }} + mkdir -p {{ $baseDirectory }}; + cp -Lrf {{ .source }} {{ $.Values.artifactory.persistence.mountPath }}/{{ .target }}; + {{- end }} + {{- if .Values.artifactory.preStartCommand }} + echo "Running custom preStartCommand command"; + {{ tpl .Values.artifactory.preStartCommand . }}; + {{- end }} + {{- with .Values.artifactory.node.preStartCommand }} + echo "Running member node specific custom preStartCommand command"; + {{ tpl . $ }}; + {{- end }} + exec /entrypoint-artifactory.sh + {{- with .Values.artifactory.lifecycle }} + lifecycle: +{{ toYaml . | indent 10 }} + {{- end }} + env: + {{- if .Values.aws.license.enabled }} + - name: IS_AWS_LICENSE + value: "true" + - name: AWS_REGION + value: {{ .Values.aws.region | quote }} + {{- if .Values.aws.licenseConfigSecretName }} + - name: AWS_WEB_IDENTITY_REFRESH_TOKEN_FILE + value: "/var/run/secrets/product-license/license_token" + - name: AWS_ROLE_ARN + valueFrom: + secretKeyRef: + name: {{ .Values.aws.licenseConfigSecretName }} + key: iam_role + {{- end }} + {{- end }} + {{- if .Values.splitServicesToContainers }} + - name : JF_ROUTER_ENABLED + value: "true" + - name : JF_ROUTER_SERVICE_ENABLED + value: "false" + - name : JF_EVENT_ENABLED + value: "false" + - name : JF_METADATA_ENABLED + value: "false" + - name : JF_FRONTEND_ENABLED + value: "false" + - name: JF_FEDERATION_ENABLED + value: "false" + - name : JF_OBSERVABILITY_ENABLED + value: "false" + - name : JF_JFCONNECT_SERVICE_ENABLED + value: "false" + - name : JF_EVIDENCE_ENABLED + value: "false" + - name : JF_ONEMODEL_ENABLED + value: "false" + {{- if not (.Values.access.runOnArtifactoryTomcat | default false) }} + - name : JF_ACCESS_ENABLED + value: "false" + {{- end}} + - name: JF_TOPOLOGY_SPLIT_CONTAINER_ENABLED + value: "true" + {{- end }} + {{- if and (not .Values.waitForDatabase) (not .Values.postgresql.enabled) }} + - name: SKIP_WAIT_FOR_EXTERNAL_DB + value: "true" + {{- end }} + {{- if or .Values.database.secrets.user .Values.database.user }} + - name: JF_SHARED_DATABASE_USERNAME + valueFrom: + secretKeyRef: + {{- if .Values.database.secrets.user }} + name: {{ tpl .Values.database.secrets.user.name . }} + key: {{ tpl .Values.database.secrets.user.key . }} + {{- else if .Values.database.user }} + {{- if not .Values.artifactory.unifiedSecretInstallation }} + name: {{ template "artifactory-ha.fullname" . }}-database-creds + {{- else }} + name: "{{ template "artifactory.unifiedSecretPrependReleaseName" . }}-unified-secret" + {{- end }} + key: db-user + {{- end }} + {{- end }} + {{ if or .Values.database.secrets.password .Values.database.password .Values.postgresql.enabled }} + - name: JF_SHARED_DATABASE_PASSWORD + valueFrom: + secretKeyRef: + {{- if .Values.database.secrets.password }} + name: {{ tpl .Values.database.secrets.password.name . }} + key: {{ tpl .Values.database.secrets.password.key . }} + {{- else if .Values.database.password }} + {{- if not .Values.artifactory.unifiedSecretInstallation }} + name: {{ template "artifactory-ha.fullname" . }}-database-creds + {{- else }} + name: "{{ template "artifactory.unifiedSecretPrependReleaseName" . }}-unified-secret" + {{- end }} + key: db-password + {{- else if .Values.postgresql.enabled }} + name: {{ .Release.Name }}-postgresql + key: postgresql-password + {{- end }} + {{- end }} + {{- if or .Values.database.secrets.url .Values.database.url }} + - name: JF_SHARED_DATABASE_URL + valueFrom: + secretKeyRef: + {{- if .Values.database.secrets.url }} + name: {{ tpl .Values.database.secrets.url.name . }} + key: {{ tpl .Values.database.secrets.url.key . }} + {{- else if .Values.database.url }} + {{- if not .Values.artifactory.unifiedSecretInstallation }} + name: {{ template "artifactory-ha.fullname" . }}-database-creds + {{- else }} + name: "{{ template "artifactory.unifiedSecretPrependReleaseName" . }}-unified-secret" + {{- end }} + key: db-url + {{- end }} + {{- end }} + - name: JF_SHARED_NODE_HAENABLED + value: "true" +{{- with .Values.artifactory.extraEnvironmentVariables }} +{{ tpl (toYaml .) $ | indent 8 }} +{{- end }} + ports: + - containerPort: {{ .Values.artifactory.internalPort }} + name: http + - containerPort: {{ .Values.artifactory.internalArtifactoryPort }} + name: http-internal + {{- if .Values.artifactory.node.javaOpts.jmx.enabled }} + - containerPort: {{ .Values.artifactory.node.javaOpts.jmx.port }} + name: tcp-jmx + {{- end }} + {{- if .Values.artifactory.ssh.enabled }} + - containerPort: {{ .Values.artifactory.ssh.internalPort }} + name: tcp-ssh + {{- end }} + volumeMounts: + {{- if .Values.artifactory.customPersistentVolumeClaim }} + - name: {{ .Values.artifactory.customPersistentVolumeClaim.name }} + mountPath: {{ .Values.artifactory.customPersistentVolumeClaim.mountPath }} + {{- end }} + {{- if .Values.artifactory.customPersistentPodVolumeClaim }} + - name: {{ .Values.artifactory.customPersistentPodVolumeClaim.name }} + mountPath: {{ .Values.artifactory.customPersistentPodVolumeClaim.mountPath }} + {{- end }} + {{- if .Values.aws.licenseConfigSecretName }} + - name: awsmp-product-license + mountPath: "/var/run/secrets/product-license" + {{- end }} + - name: volume + mountPath: "{{ .Values.artifactory.persistence.mountPath }}" + + ######################## Artifactory persistence fs ########################## + {{- if eq .Values.artifactory.persistence.type "file-system" }} + {{- if .Values.artifactory.persistence.fileSystem.existingSharedClaim.enabled }} + {{- range $sharedClaimNumber, $e := until (.Values.artifactory.persistence.fileSystem.existingSharedClaim.numberOfExistingClaims|int) }} + - name: artifactory-ha-data-{{ $sharedClaimNumber }} + mountPath: "{{ tpl $.Values.artifactory.persistence.fileSystem.existingSharedClaim.dataDir $ }}/filestore{{ $sharedClaimNumber }}" + {{- end }} + - name: artifactory-ha-backup + mountPath: "{{ $.Values.artifactory.persistence.fileSystem.existingSharedClaim.backupDir }}" + {{- end }} + {{- end }} + + ######################## Artifactory persistence nfs ########################## + {{- if eq .Values.artifactory.persistence.type "nfs" }} + - name: artifactory-ha-data + mountPath: "{{ .Values.artifactory.persistence.nfs.dataDir }}" + - name: artifactory-ha-backup + mountPath: "{{ .Values.artifactory.persistence.nfs.backupDir }}" + {{- else }} + + ######################## Artifactory persistence binarystore Xml ########################## + {{- if or (not .Values.artifactory.unifiedSecretInstallation) .Values.artifactory.persistence.customBinarystoreXmlSecret }} + - name: binarystore-xml + {{- else }} + - name: {{ include "artifactory-ha.unifiedCustomSecretVolumeName" . }} + {{- end }} + mountPath: "/tmp/etc/artifactory/binarystore.xml" + subPath: binarystore.xml + + ######################## Artifactory persistence google storage ########################## + {{- if .Values.artifactory.persistence.googleStorage.gcpServiceAccount.enabled }} + {{- if or (not .Values.artifactory.unifiedSecretInstallation) .Values.artifactory.persistence.googleStorage.gcpServiceAccount.customSecretName }} + - name: gcpcreds-json + {{- else }} + - name: {{ include "artifactory-ha.unifiedCustomSecretVolumeName" . }} + {{- end }} + mountPath: "/artifactory_bootstrap/gcp.credentials.json" + subPath: gcp.credentials.json + {{- end }} + + ######################## Artifactory ConfigMap ########################## + {{- if .Values.artifactory.configMapName }} + - name: bootstrap-config + mountPath: "/bootstrap/" + {{- end }} + + ######################## Artifactory license ########################## + {{- if or .Values.artifactory.license.secret .Values.artifactory.license.licenseKey }} + {{- if or (not .Values.artifactory.unifiedSecretInstallation) .Values.artifactory.license.secret }} + - name: artifactory-license + {{- else }} + - name: {{ include "artifactory-ha.unifiedCustomSecretVolumeName" . }} + {{- end }} + mountPath: "/artifactory_bootstrap/artifactory.cluster.license" + {{- if .Values.artifactory.license.secret }} + subPath: {{ .Values.artifactory.license.dataKey }} + {{- else if .Values.artifactory.license.licenseKey }} + subPath: artifactory.lic + {{- end }} + {{- end }} + {{- end }} + - name: installer-info + mountPath: "/artifactory_bootstrap/info/installer-info.json" + subPath: installer-info.json + {{- if or .Values.artifactory.customVolumeMounts .Values.global.customVolumeMounts }} +{{ tpl (include "artifactory-ha.customVolumeMounts" .) . | indent 8 }} + {{- end }} + resources: +{{ toYaml .Values.artifactory.node.resources | indent 10 }} + {{- if .Values.artifactory.startupProbe.enabled }} + startupProbe: +{{ tpl .Values.artifactory.startupProbe.config . | indent 10 }} + {{- end }} + {{- if and (not .Values.splitServicesToContainers) (semverCompare "= 107.79.x), just set databaseUpgradeReady=true \n" .Values.databaseUpgradeReady | quote }} +{{- end }} +{{- if .Values.artifactory.postStartCommand }} + {{- fail ".Values.artifactory.postStartCommand is not supported and should be replaced with .Values.artifactory.lifecycle.postStart.exec.command" }} +{{- end }} +{{- if eq .Values.artifactory.persistence.type "aws-s3" }} + {{- fail "\nPersistence storage type 'aws-s3' is deprecated and is not supported and should be replaced with 'aws-s3-v3'" }} +{{- end }} +{{- if or .Values.artifactory.persistence.googleStorage.identity .Values.artifactory.persistence.googleStorage.credential }} + {{- fail "\nGCP Bucket Authentication with Identity and Credential is deprecated" }} +{{- end }} +{{- if (eq (.Values.artifactory.setSecurityContext | toString) "false" ) }} + {{- fail "\n You need to set security context at the pod level. .Values.artifactory.setSecurityContext is no longer supported. Replace it with .Values.artifactory.podSecurityContext" }} +{{- end }} +{{- if or .Values.artifactory.uid .Values.artifactory.gid }} +{{- if or (not (eq (.Values.artifactory.uid | toString) "1030" )) (not (eq (.Values.artifactory.gid | toString) "1030" )) }} + {{- fail "\n .Values.artifactory.uid and .Values.artifactory.gid are no longer supported. You need to set these values at the pod security context level. Replace them with .Values.artifactory.podSecurityContext.runAsUser, .Values.artifactory.podSecurityContext.runAsGroup and .Values.artifactory.podSecurityContext.fsGroup" }} +{{- end }} +{{- end }} +{{- if or .Values.artifactory.fsGroupChangePolicy .Values.artifactory.seLinuxOptions }} + {{- fail "\n .Values.artifactory.fsGroupChangePolicy and .Values.artifactory.seLinuxOptions are no longer supported. You need to set these values at the pod security context level. Replace them with .Values.artifactory.podSecurityContext.fsGroupChangePolicy and .Values.artifactory.podSecurityContext.seLinuxOptions" }} +{{- end }} +{{- if .Values.initContainerImage }} + {{- fail "\n .Values.initContainerImage is no longer supported. Replace it with .Values.initContainers.image.registry .Values.initContainers.image.repository and .Values.initContainers.image.tag" }} +{{- end }} +{{- if and (eq .Values.rtfs.enabled true) (ne .Values.database.type "postgresql") (not (empty .Values.database.type))}} + {{- fail "\n RTFS supports only PostgreSQL databases and is not compatible with other databases. If you are currently using a different database, disable RTFS." }} +{{- end }} +{{- with .Values.artifactory.primary.labels }} +{{ toYaml . | indent 4 }} +{{- end }} +{{- with .Values.artifactory.statefulset.annotations }} + annotations: +{{ toYaml . | indent 4 }} +{{- end }} +spec: + serviceName: {{ template "artifactory-ha.primary.name" . }} + replicas: {{ .Values.artifactory.primary.replicaCount }} + updateStrategy: {{- toYaml .Values.artifactory.primary.updateStrategy | nindent 4}} + selector: + matchLabels: + app: {{ template "artifactory-ha.name" . }} + role: {{ template "artifactory-ha.primary.name" . }} + release: {{ .Release.Name }} + template: + metadata: + labels: + app: {{ template "artifactory-ha.name" . }} + chart: {{ template "artifactory-ha.chart" . }} + role: {{ template "artifactory-ha.primary.name" . }} + component: {{ .Values.artifactory.name }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + {{- with .Values.artifactory.primary.labels }} +{{ toYaml . | indent 8 }} + {{- end }} + annotations: + {{- if not .Values.artifactory.unifiedSecretInstallation }} + checksum/database-secrets: {{ include (print $.Template.BasePath "/artifactory-database-secrets.yaml") . | sha256sum }} + checksum/binarystore: {{ include (print $.Template.BasePath "/artifactory-binarystore-secret.yaml") . | sha256sum }} + checksum/systemyaml: {{ include (print $.Template.BasePath "/artifactory-system-yaml.yaml") . | sha256sum }} + {{- if .Values.access.accessConfig }} + checksum/access-config: {{ include (print $.Template.BasePath "/artifactory-access-config.yaml") . | sha256sum }} + {{- end }} + {{- if .Values.artifactory.persistence.googleStorage.gcpServiceAccount.enabled }} + checksum/gcpcredentials: {{ include (print $.Template.BasePath "/artifactory-gcp-credentials-secret.yaml") . | sha256sum }} + {{- end }} + {{- if not (and .Values.artifactory.admin.secret .Values.artifactory.admin.dataKey) }} + checksum/admin-creds: {{ include (print $.Template.BasePath "/admin-bootstrap-creds.yaml") . | sha256sum }} + {{- end }} + {{- else }} + checksum/artifactory-unified-secret: {{ include (print $.Template.BasePath "/artifactory-unified-secret.yaml") . | sha256sum }} + {{- end }} + {{- with .Values.artifactory.annotations }} +{{ toYaml . | indent 8 }} + {{- end }} + spec: + {{- if .Values.artifactory.schedulerName }} + schedulerName: {{ .Values.artifactory.schedulerName | quote }} + {{- end }} + {{- if .Values.artifactory.priorityClass.existingPriorityClass }} + priorityClassName: {{ .Values.artifactory.priorityClass.existingPriorityClass }} + {{- else -}} + {{- if .Values.artifactory.priorityClass.create }} + priorityClassName: {{ default (include "artifactory-ha.fullname" .) .Values.artifactory.priorityClass.name }} + {{- end }} + {{- end }} + serviceAccountName: {{ template "artifactory-ha.serviceAccountName" . }} + terminationGracePeriodSeconds: {{ add .Values.artifactory.terminationGracePeriodSeconds 10 }} + {{- if or .Values.imagePullSecrets .Values.global.imagePullSecrets }} +{{- include "artifactory-ha.imagePullSecrets" . | indent 6 }} + {{- end }} + {{- if .Values.artifactory.podSecurityContext.enabled }} + securityContext: {{- omit .Values.artifactory.podSecurityContext "enabled" | toYaml | nindent 8 }} + {{- end }} + {{- if .Values.artifactory.topologySpreadConstraints }} + topologySpreadConstraints: +{{ tpl (toYaml .Values.artifactory.topologySpreadConstraints) . | indent 8 }} + {{- end }} + initContainers: + {{- if or .Values.artifactory.customInitContainersBegin .Values.global.customInitContainersBegin }} +{{ tpl (include "artifactory-ha.customInitContainersBegin" .) . | indent 6 }} + {{- end }} + {{- if .Values.artifactory.persistence.enabled }} + {{- if eq .Values.artifactory.persistence.type "file-system" }} + {{- if .Values.artifactory.persistence.fileSystem.existingSharedClaim.enabled }} + - name: "create-artifactory-data-dir" + image: {{ include "artifactory-ha.getImageInfoByValue" (list . "initContainers") }} + imagePullPolicy: {{ .Values.initContainers.image.pullPolicy }} + {{- if .Values.containerSecurityContext.enabled }} + securityContext: {{- omit .Values.containerSecurityContext "enabled" | toYaml | nindent 10 }} + {{- end }} + resources: +{{ toYaml .Values.initContainers.resources | indent 10 }} + command: + - 'bash' + - '-c' + - > + mkdir -p {{ tpl .Values.artifactory.persistence.fileSystem.existingSharedClaim.dataDir . }}; + volumeMounts: + - mountPath: {{ .Values.artifactory.persistence.mountPath | quote }} + name: volume + {{- end }} + {{- end }} + {{- if .Values.artifactory.deleteDBPropertiesOnStartup }} + - name: "delete-db-properties" + image: {{ include "artifactory-ha.getImageInfoByValue" (list . "initContainers") }} + imagePullPolicy: {{ .Values.initContainers.image.pullPolicy }} + {{- if .Values.containerSecurityContext.enabled }} + securityContext: {{- omit .Values.containerSecurityContext "enabled" | toYaml | nindent 10 }} + {{- end }} + resources: +{{ toYaml .Values.initContainers.resources | indent 10 }} + command: + - 'bash' + - '-c' + - 'rm -fv {{ .Values.artifactory.persistence.mountPath }}/etc/db.properties' + volumeMounts: + - mountPath: {{ .Values.artifactory.persistence.mountPath | quote }} + name: volume + {{- end }} + {{- if or (and .Values.artifactory.admin.secret .Values.artifactory.admin.dataKey) .Values.artifactory.admin.password }} + - name: "access-bootstrap-creds" + image: {{ include "artifactory-ha.getImageInfoByValue" (list . "initContainers") }} + imagePullPolicy: {{ .Values.initContainers.image.pullPolicy }} + {{- if .Values.containerSecurityContext.enabled }} + securityContext: {{- omit .Values.containerSecurityContext "enabled" | toYaml | nindent 10 }} + {{- end }} + resources: +{{ toYaml .Values.initContainers.resources | indent 10 }} + command: + - 'bash' + - '-c' + - > + echo "Preparing {{ .Values.artifactory.persistence.mountPath }}/etc/access/bootstrap.creds"; + mkdir -p {{ .Values.artifactory.persistence.mountPath }}/etc/access; + cp -Lrf /tmp/access/bootstrap.creds {{ .Values.artifactory.persistence.mountPath }}/etc/access/bootstrap.creds; + chmod 600 {{ .Values.artifactory.persistence.mountPath }}/etc/access/bootstrap.creds; + volumeMounts: + - name: volume + mountPath: "{{ .Values.artifactory.persistence.mountPath }}" + {{- if or (not .Values.artifactory.unifiedSecretInstallation) (and .Values.artifactory.admin.secret .Values.artifactory.admin.dataKey) }} + - name: access-bootstrap-creds + {{- else }} + - name: {{ include "artifactory-ha.unifiedCustomSecretVolumeName" . }} + {{- end }} + mountPath: "/tmp/access/bootstrap.creds" + {{- if and .Values.artifactory.admin.secret .Values.artifactory.admin.dataKey }} + subPath: {{ .Values.artifactory.admin.dataKey }} + {{- else }} + subPath: bootstrap.creds + {{- end }} + {{- end }} + {{- end }} + - name: 'copy-system-configurations' + image: {{ include "artifactory-ha.getImageInfoByValue" (list . "initContainers") }} + imagePullPolicy: {{ .Values.initContainers.image.pullPolicy }} + {{- if .Values.containerSecurityContext.enabled }} + securityContext: {{- omit .Values.containerSecurityContext "enabled" | toYaml | nindent 10 }} + {{- end }} + resources: +{{ toYaml .Values.initContainers.resources | indent 10 }} + command: + - '/bin/bash' + - '-c' + - > + if [[ -e "{{ .Values.artifactory.persistence.mountPath }}/etc/filebeat.yaml" ]]; then chmod 644 {{ .Values.artifactory.persistence.mountPath }}/etc/filebeat.yaml; fi; + echo "Copy system.yaml to {{ .Values.artifactory.persistence.mountPath }}/etc"; + mkdir -p {{ .Values.artifactory.persistence.mountPath }}/etc; + mkdir -p {{ .Values.artifactory.persistence.mountPath }}/etc/access/keys/trusted; + {{- if .Values.systemYamlOverride.existingSecret }} + cp -fv /tmp/etc/{{ .Values.systemYamlOverride.dataKey }} {{ .Values.artifactory.persistence.mountPath }}/etc/system.yaml; + {{- else }} + cp -fv /tmp/etc/system.yaml {{ .Values.artifactory.persistence.mountPath }}/etc/system.yaml; + {{- end }} + echo "Copy binarystore.xml file"; + mkdir -p {{ .Values.artifactory.persistence.mountPath }}/etc/artifactory; + cp -fv /tmp/etc/artifactory/binarystore.xml {{ .Values.artifactory.persistence.mountPath }}/etc/artifactory/binarystore.xml; + {{- if .Values.access.accessConfig }} + echo "Copy access.config.patch.yml to {{ .Values.artifactory.persistence.mountPath }}/etc/access"; + mkdir -p {{ .Values.artifactory.persistence.mountPath }}/etc/access; + cp -fv /tmp/etc/access.config.patch.yml {{ .Values.artifactory.persistence.mountPath }}/etc/access/access.config.patch.yml; + {{- end }} + {{- if .Values.access.resetAccessCAKeys }} + echo "Resetting Access CA Keys"; + mkdir -p {{ .Values.artifactory.persistence.mountPath }}/bootstrap/etc/access/keys; + touch {{ .Values.artifactory.persistence.mountPath }}/bootstrap/etc/access/keys/reset_ca_keys; + {{- end }} + {{- if .Values.access.customCertificatesSecretName }} + echo "Copying custom certificates to {{ .Values.artifactory.persistence.mountPath }}/bootstrap/etc/access/keys"; + mkdir -p {{ .Values.artifactory.persistence.mountPath }}/bootstrap/etc/access/keys; + cp -fv /tmp/etc/tls.crt {{ .Values.artifactory.persistence.mountPath }}/bootstrap/etc/access/keys/ca.crt; + cp -fv /tmp/etc/tls.key {{ .Values.artifactory.persistence.mountPath }}/bootstrap/etc/access/keys/ca.private.key; + {{- end }} + {{- if .Values.jfconnect.customCertificatesSecretName }} + echo "Copying custom certificates to {{ .Values.artifactory.persistence.mountPath }}/bootstrap/jfconnect/etc/keys"; + mkdir -p {{ .Values.artifactory.persistence.mountPath }}/bootstrap/jfconnect/etc/keys; + cp -fv /tmp/etc/tls.crt {{ .Values.artifactory.persistence.mountPath }}/bootstrap/jfconnect/etc/keys/; + cp -fv /tmp/etc/tls.cer {{ .Values.artifactory.persistence.mountPath }}/bootstrap/jfconnect/etc/keys/; + cp -fv /tmp/etc/tls.pem {{ .Values.artifactory.persistence.mountPath }}/bootstrap/jfconnect/etc/keys/; + cp -fv /tmp/etc/tls.key {{ .Values.artifactory.persistence.mountPath }}/bootstrap/jfconnect/etc/keys/; + {{- end }} + {{- if or .Values.artifactory.joinKey .Values.global.joinKey .Values.artifactory.joinKeySecretName .Values.global.joinKeySecretName }} + echo "Copy joinKey to {{ .Values.artifactory.persistence.mountPath }}/bootstrap/access/etc/security"; + mkdir -p {{ .Values.artifactory.persistence.mountPath }}/bootstrap/access/etc/security; + echo -n ${ARTIFACTORY_JOIN_KEY} > {{ .Values.artifactory.persistence.mountPath }}/bootstrap/access/etc/security/join.key; + {{- end }} + {{- if or .Values.artifactory.jfConnectToken .Values.artifactory.jfConnectTokenSecretName }} + echo "Copy jfConnectToken to {{ .Values.artifactory.persistence.mountPath }}/bootstrap/jfconnect/registration_token"; + mkdir -p {{ .Values.artifactory.persistence.mountPath }}/bootstrap/jfconnect/; + echo -n ${ARTIFACTORY_JFCONNECT_TOKEN} > {{ .Values.artifactory.persistence.mountPath }}/bootstrap/jfconnect/registration_token; + {{- end }} + {{- if or .Values.artifactory.masterKey .Values.global.masterKey .Values.artifactory.masterKeySecretName .Values.global.masterKeySecretName }} + echo "Copy masterKey to {{ .Values.artifactory.persistence.mountPath }}/etc/security"; + mkdir -p {{ .Values.artifactory.persistence.mountPath }}/etc/security; + echo -n ${ARTIFACTORY_MASTER_KEY} > {{ .Values.artifactory.persistence.mountPath }}/etc/security/master.key; + {{- end }} + env: + {{- if or .Values.artifactory.joinKey .Values.global.joinKey .Values.artifactory.joinKeySecretName .Values.global.joinKeySecretName }} + - name: ARTIFACTORY_JOIN_KEY + valueFrom: + secretKeyRef: + {{- if or (not .Values.artifactory.unifiedSecretInstallation) .Values.artifactory.joinKeySecretName .Values.global.joinKeySecretName }} + name: {{ include "artifactory-ha.joinKeySecretName" . }} + {{- else }} + name: "{{ template "artifactory.unifiedSecretPrependReleaseName" . }}-unified-secret" + {{- end }} + key: join-key + {{- end }} + {{- if or .Values.artifactory.jfConnectToken .Values.artifactory.jfConnectTokenSecretName }} + - name: ARTIFACTORY_JFCONNECT_TOKEN + valueFrom: + secretKeyRef: + {{- if or (not .Values.artifactory.unifiedSecretInstallation) .Values.artifactory.jfConnectTokenSecretName }} + name: {{ include "artifactory-ha.jfConnectTokenSecretName" . }} + {{- else }} + name: "{{ template "artifactory.unifiedSecretPrependReleaseName" . }}-unified-secret" + {{- end }} + key: jfconnect-token + {{- end }} + {{- if or .Values.artifactory.masterKey .Values.global.masterKey .Values.artifactory.masterKeySecretName .Values.global.masterKeySecretName }} + - name: ARTIFACTORY_MASTER_KEY + valueFrom: + secretKeyRef: + {{- if or (not .Values.artifactory.unifiedSecretInstallation) .Values.artifactory.masterKeySecretName .Values.global.masterKeySecretName }} + name: {{ include "artifactory-ha.masterKeySecretName" . }} + {{- else }} + name: "{{ template "artifactory.unifiedSecretPrependReleaseName" . }}-unified-secret" + {{- end }} + key: master-key + {{- end }} + + ######################## Volume Mounts For copy-system-configurations ########################## + volumeMounts: + - name: volume + mountPath: {{ .Values.artifactory.persistence.mountPath | quote }} + + ######################## SystemYaml ########################## + {{- if or (not .Values.artifactory.unifiedSecretInstallation) .Values.systemYamlOverride.existingSecret }} + - name: systemyaml + {{- else }} + - name: {{ include "artifactory-ha.unifiedCustomSecretVolumeName" . }} + {{- end }} + {{- if .Values.systemYamlOverride.existingSecret }} + mountPath: "/tmp/etc/{{.Values.systemYamlOverride.dataKey}}" + subPath: {{ .Values.systemYamlOverride.dataKey }} + {{- else }} + mountPath: "/tmp/etc/system.yaml" + subPath: system.yaml + {{- end }} + + ######################## Binarystore ########################## + {{- if or (not .Values.artifactory.unifiedSecretInstallation) .Values.artifactory.persistence.customBinarystoreXmlSecret }} + - name: binarystore-xml + {{- else }} + - name: {{ include "artifactory-ha.unifiedCustomSecretVolumeName" . }} + {{- end }} + mountPath: "/tmp/etc/artifactory/binarystore.xml" + subPath: binarystore.xml + + ######################## Access config ########################## + {{- if .Values.access.accessConfig }} + {{- if not .Values.artifactory.unifiedSecretInstallation }} + - name: access-config + {{- else }} + - name: {{ include "artifactory-ha.unifiedCustomSecretVolumeName" . }} + {{- end }} + mountPath: "/tmp/etc/access.config.patch.yml" + subPath: access.config.patch.yml + {{- end }} + + ######################## Access certs external secret ########################## + {{- if .Values.access.customCertificatesSecretName }} + - name: access-certs + mountPath: "/tmp/etc/tls.crt" + subPath: tls.crt + - name: access-certs + mountPath: "/tmp/etc/tls.key" + subPath: tls.key + {{- end }} + + {{- if .Values.jfconnect.customCertificatesSecretName }} + - name: jfconnect-certs + mountPath: "/tmp/etc/tls.crt" + subPath: tls.crt + - name: jfconnect-certs + mountPath: "/tmp/etc/tls.pem" + subPath: tls.pem + - name: jfconnect-certs + mountPath: "/tmp/etc/tls.cer" + subPath: tls.cer + - name: jfconnect-certs + mountPath: "/tmp/etc/tls.key" + subPath: tls.key + {{- end }} + + {{- if or .Values.artifactory.customCertificates.enabled .Values.global.customCertificates.enabled }} + - name: copy-custom-certificates + image: {{ include "artifactory-ha.getImageInfoByValue" (list . "initContainers") }} + imagePullPolicy: {{ .Values.initContainers.image.pullPolicy }} + {{- if .Values.containerSecurityContext.enabled }} + securityContext: {{- omit .Values.containerSecurityContext "enabled" | toYaml | nindent 10 }} + {{- end }} + resources: +{{ toYaml .Values.initContainers.resources | indent 10 }} + command: + - 'bash' + - '-c' + - > +{{ include "artifactory-ha.copyCustomCerts" . | indent 10 }} + volumeMounts: + - name: volume + mountPath: {{ .Values.artifactory.persistence.mountPath }} + - name: ca-certs + mountPath: "/tmp/certs" + {{- end }} + + {{- if .Values.artifactory.circleOfTrustCertificatesSecret }} + - name: copy-circle-of-trust-certificates + image: {{ include "artifactory-ha.getImageInfoByValue" (list . "initContainers") }} + imagePullPolicy: {{ .Values.initContainers.image.pullPolicy }} + {{- if .Values.containerSecurityContext.enabled }} + securityContext: {{- omit .Values.containerSecurityContext "enabled" | toYaml | nindent 10 }} + {{- end }} + resources: +{{ toYaml .Values.initContainers.resources | indent 10 }} + command: + - 'bash' + - '-c' + - > +{{ include "artifactory.copyCircleOfTrustCertsCerts" . | indent 10 }} + volumeMounts: + - name: volume + mountPath: {{ .Values.artifactory.persistence.mountPath }} + - name: circle-of-trust-certs + mountPath: "/tmp/circleoftrustcerts" + {{- end }} + + {{- if .Values.waitForDatabase }} + {{- if or .Values.postgresql.enabled }} + - name: "wait-for-db" + image: {{ include "artifactory-ha.getImageInfoByValue" (list . "initContainers") }} + imagePullPolicy: {{ .Values.initContainers.image.pullPolicy }} + {{- if .Values.containerSecurityContext.enabled }} + securityContext: {{- omit .Values.containerSecurityContext "enabled" | toYaml | nindent 10 }} + {{- end }} + command: + - /bin/bash + - -c + - | + echo "Waiting for postgresql to come up" + ready=false; + while ! $ready; do echo waiting; + timeout 2s bash -c " + {{- if .Values.artifactory.migration.preStartCommand }} + echo "Running custom preStartCommand command"; + {{ tpl .Values.artifactory.migration.preStartCommand . }}; + {{- end }} + scriptsPath="/opt/jfrog/artifactory/app/bin"; + mkdir -p $scriptsPath; + echo "Copy migration scripts and Run migration"; + cp -fv /tmp/migrate.sh $scriptsPath/migrate.sh; + cp -fv /tmp/migrationHelmInfo.yaml $scriptsPath/migrationHelmInfo.yaml; + cp -fv /tmp/migrationStatus.sh $scriptsPath/migrationStatus.sh; + mkdir -p {{ .Values.artifactory.persistence.mountPath }}/log; + bash $scriptsPath/migrationStatus.sh {{ include "artifactory-ha.app.version" . }} {{ .Values.artifactory.migration.timeoutSeconds }} > >(tee {{ .Values.artifactory.persistence.mountPath }}/log/helm-migration.log) 2>&1; + env: + {{- if and (not .Values.waitForDatabase) (not .Values.postgresql.enabled) }} + - name: SKIP_WAIT_FOR_EXTERNAL_DB + value: "true" + {{- end }} + {{- if or .Values.database.secrets.user .Values.database.user }} + - name: JF_SHARED_DATABASE_USERNAME + valueFrom: + secretKeyRef: + {{- if .Values.database.secrets.user }} + name: {{ tpl .Values.database.secrets.user.name . }} + key: {{ tpl .Values.database.secrets.user.key . }} + {{- else if .Values.database.user }} + {{- if not .Values.artifactory.unifiedSecretInstallation }} + name: {{ template "artifactory-ha.fullname" . }}-database-creds + {{- else }} + name: "{{ template "artifactory.unifiedSecretPrependReleaseName" . }}-unified-secret" + {{- end }} + key: db-user + {{- end }} + {{- end }} + {{ if or .Values.database.secrets.password .Values.database.password .Values.postgresql.enabled }} + - name: JF_SHARED_DATABASE_PASSWORD + valueFrom: + secretKeyRef: + {{- if .Values.database.secrets.password }} + name: {{ tpl .Values.database.secrets.password.name . }} + key: {{ tpl .Values.database.secrets.password.key . }} + {{- else if .Values.database.password }} + {{- if not .Values.artifactory.unifiedSecretInstallation }} + name: {{ template "artifactory-ha.fullname" . }}-database-creds + {{- else }} + name: "{{ template "artifactory.unifiedSecretPrependReleaseName" . }}-unified-secret" + {{- end }} + key: db-password + {{- else if .Values.postgresql.enabled }} + name: {{ .Release.Name }}-postgresql + key: postgresql-password + {{- end }} + {{- end }} + {{- if or .Values.database.secrets.url .Values.database.url }} + - name: JF_SHARED_DATABASE_URL + valueFrom: + secretKeyRef: + {{- if .Values.database.secrets.url }} + name: {{ tpl .Values.database.secrets.url.name . }} + key: {{ tpl .Values.database.secrets.url.key . }} + {{- else if .Values.database.url }} + {{- if not .Values.artifactory.unifiedSecretInstallation }} + name: {{ template "artifactory-ha.fullname" . }}-database-creds + {{- else }} + name: "{{ template "artifactory.unifiedSecretPrependReleaseName" . }}-unified-secret" + {{- end }} + key: db-url + {{- end }} + {{- end }} + - name: JF_SHARED_NODE_HAENABLED + value: "true" +{{- with .Values.artifactory.extraEnvironmentVariables }} +{{ tpl (toYaml .) $ | indent 8 }} +{{- end }} + volumeMounts: + - name: migration-scripts + mountPath: "/tmp/migrate.sh" + subPath: migrate.sh + - name: migration-scripts + mountPath: "/tmp/migrationHelmInfo.yaml" + subPath: migrationHelmInfo.yaml + - name: migration-scripts + mountPath: "/tmp/migrationStatus.sh" + subPath: migrationStatus.sh + - name: volume + mountPath: "{{ .Values.artifactory.persistence.mountPath }}" + + ######################## Artifactory persistence fs ########################## + {{- if eq .Values.artifactory.persistence.type "file-system" }} + {{- if .Values.artifactory.persistence.fileSystem.existingSharedClaim.enabled }} + {{- range $sharedClaimNumber, $e := until (.Values.artifactory.persistence.fileSystem.existingSharedClaim.numberOfExistingClaims|int) }} + - name: artifactory-ha-data-{{ $sharedClaimNumber }} + mountPath: "{{ tpl $.Values.artifactory.persistence.fileSystem.existingSharedClaim.dataDir $ }}/filestore{{ $sharedClaimNumber }}" + {{- end }} + - name: artifactory-ha-backup + mountPath: "{{ $.Values.artifactory.persistence.fileSystem.existingSharedClaim.backupDir }}" + {{- end }} + {{- end }} + + ######################## CustomVolumeMounts ########################## + {{- if or .Values.artifactory.customVolumeMounts .Values.global.customVolumeMounts }} +{{ tpl (include "artifactory-ha.customVolumeMounts" .) . | indent 8 }} + {{- end }} + + ######################## Artifactory persistence nfs ########################## + {{- if eq .Values.artifactory.persistence.type "nfs" }} + - name: artifactory-ha-data + mountPath: "{{ .Values.artifactory.persistence.nfs.dataDir }}" + - name: artifactory-ha-backup + mountPath: "{{ .Values.artifactory.persistence.nfs.backupDir }}" + {{- else }} + + ######################## Artifactory persistence binarystore Xml ########################## + {{- if or (not .Values.artifactory.unifiedSecretInstallation) .Values.artifactory.persistence.customBinarystoreXmlSecret }} + - name: binarystore-xml + {{- else }} + - name: {{ include "artifactory-ha.unifiedCustomSecretVolumeName" . }} + {{- end }} + mountPath: "/tmp/etc/artifactory/binarystore.xml" + subPath: binarystore.xml + + ######################## Artifactory persistence google storage ########################## + {{- if .Values.artifactory.persistence.googleStorage.gcpServiceAccount.enabled }} + {{- if or (not .Values.artifactory.unifiedSecretInstallation) .Values.artifactory.persistence.googleStorage.gcpServiceAccount.customSecretName }} + - name: gcpcreds-json + {{- else }} + - name: {{ include "artifactory-ha.unifiedCustomSecretVolumeName" . }} + {{- end }} + mountPath: "/artifactory_bootstrap/gcp.credentials.json" + subPath: gcp.credentials.json + {{- end }} + {{- end }} + +{{- end }} + + {{- if .Values.hostAliases }} + hostAliases: +{{ toYaml .Values.hostAliases | indent 6 }} + {{- end }} + containers: + {{- if .Values.splitServicesToContainers }} + - name: {{ .Values.router.name }} + image: {{ include "artifactory-ha.getImageInfoByValue" (list . "router") }} + imagePullPolicy: {{ .Values.router.image.pullPolicy }} + {{- if .Values.containerSecurityContext.enabled }} + securityContext: {{- omit .Values.containerSecurityContext "enabled" | toYaml | nindent 10 }} + {{- end }} + command: + - '/bin/bash' + - '-c' + - > + exec /opt/jfrog/router/app/bin/entrypoint-router.sh; + {{- with .Values.router.lifecycle }} + lifecycle: +{{ toYaml . | indent 10 }} + {{- end }} + env: + - name: JF_ROUTER_TOPOLOGY_LOCAL_REQUIREDSERVICETYPES + value: {{ include "artifactory-ha.router.requiredServiceTypes" . }} +{{- with .Values.router.extraEnvironmentVariables }} +{{ tpl (toYaml .) $ | indent 8 }} +{{- end }} + volumeMounts: + - name: volume + mountPath: {{ .Values.router.persistence.mountPath | quote }} +{{- with .Values.router.customVolumeMounts }} +{{ tpl . $ | indent 8 }} +{{- end }} + resources: +{{ toYaml .Values.router.resources | indent 10 }} + {{- if .Values.router.startupProbe.enabled }} + startupProbe: +{{ tpl .Values.router.startupProbe.config . | indent 10 }} + {{- end }} + {{- if .Values.router.readinessProbe.enabled }} + readinessProbe: +{{ tpl .Values.router.readinessProbe.config . | indent 10 }} + {{- end }} + {{- if .Values.router.livenessProbe.enabled }} + livenessProbe: +{{ tpl .Values.router.livenessProbe.config . | indent 10 }} + {{- end }} + {{- if .Values.frontend.enabled }} + - name: {{ .Values.frontend.name }} + image: {{ include "artifactory-ha.getImageInfoByValue" (list . "artifactory") }} + imagePullPolicy: {{ .Values.artifactory.image.pullPolicy }} + {{- if .Values.containerSecurityContext.enabled }} + securityContext: {{- omit .Values.containerSecurityContext "enabled" | toYaml | nindent 10 }} + {{- end }} + command: + - '/bin/bash' + - '-c' + - > + exec /opt/jfrog/artifactory/app/third-party/node/bin/node /opt/jfrog/artifactory/app/frontend/bin/server/dist/bundle.js /opt/jfrog/artifactory/app/frontend + {{- with .Values.frontend.lifecycle }} + lifecycle: +{{ toYaml . | indent 10 }} + {{- end }} + env: + - name: JF_SHARED_NODE_ID + valueFrom: + fieldRef: + fieldPath: metadata.name + - name : JF_SHARED_NODE_HAENABLED + value: "true" +{{- with .Values.frontend.extraEnvironmentVariables }} +{{ tpl (toYaml .) $ | indent 8 }} +{{- end }} + volumeMounts: + {{- if or .Values.artifactory.customVolumeMounts .Values.global.customVolumeMounts }} +{{ tpl (include "artifactory-ha.customVolumeMounts" .) . | indent 8 }} + {{- end }} + - name: volume + mountPath: {{ .Values.artifactory.persistence.mountPath | quote }} + resources: +{{ toYaml .Values.frontend.resources | indent 10 }} + {{- if .Values.frontend.startupProbe.enabled }} + startupProbe: +{{ tpl .Values.frontend.startupProbe.config . | indent 10 }} + {{- end }} + {{- if .Values.frontend.livenessProbe.enabled }} + livenessProbe: +{{ tpl .Values.frontend.livenessProbe.config . | indent 10 }} + {{- end }} + {{- end }} + {{- if .Values.evidence.enabled }} + - name: {{ .Values.evidence.name }} + image: {{ include "artifactory-ha.getImageInfoByValue" (list . "artifactory") }} + imagePullPolicy: {{ .Values.artifactory.image.pullPolicy }} + {{- if .Values.containerSecurityContext.enabled }} + securityContext: {{- omit .Values.containerSecurityContext "enabled" | toYaml | nindent 10 }} + {{- end }} + command: + - '/bin/bash' + - '-c' + - > + exec /opt/jfrog/artifactory/app/evidence/bin/jf-evidence start + {{- with .Values.evidence.lifecycle }} + lifecycle: +{{ toYaml . | indent 10 }} + {{- end }} + env: + - name: JF_SHARED_NODE_ID + valueFrom: + fieldRef: + fieldPath: metadata.name + {{- if or .Values.database.secrets.user .Values.database.user }} + - name: JF_SHARED_DATABASE_USERNAME + valueFrom: + secretKeyRef: + {{- if .Values.database.secrets.user }} + name: {{ tpl .Values.database.secrets.user.name . }} + key: {{ tpl .Values.database.secrets.user.key . }} + {{- else if .Values.database.user }} + {{- if not .Values.artifactory.unifiedSecretInstallation }} + name: {{ template "artifactory-ha.fullname" . }}-database-creds + {{- else }} + name: "{{ template "artifactory.unifiedSecretPrependReleaseName" . }}-unified-secret" + {{- end }} + key: db-user + {{- end }} + {{- end }} + {{ if or .Values.database.secrets.password .Values.database.password .Values.postgresql.enabled }} + - name: JF_SHARED_DATABASE_PASSWORD + valueFrom: + secretKeyRef: + {{- if .Values.database.secrets.password }} + name: {{ tpl .Values.database.secrets.password.name . }} + key: {{ tpl .Values.database.secrets.password.key . }} + {{- else if .Values.database.password }} + {{- if not .Values.artifactory.unifiedSecretInstallation }} + name: {{ template "artifactory-ha.fullname" . }}-database-creds + {{- else }} + name: "{{ template "artifactory.unifiedSecretPrependReleaseName" . }}-unified-secret" + {{- end }} + key: db-password + {{- else if .Values.postgresql.enabled }} + name: {{ .Release.Name }}-postgresql + key: postgresql-password + {{- end }} + {{- end }} + {{- if or .Values.database.secrets.url .Values.database.url }} + - name: JF_SHARED_DATABASE_URL + valueFrom: + secretKeyRef: + {{- if .Values.database.secrets.url }} + name: {{ tpl .Values.database.secrets.url.name . }} + key: {{ tpl .Values.database.secrets.url.key . }} + {{- else if .Values.database.url }} + {{- if not .Values.artifactory.unifiedSecretInstallation }} + name: {{ template "artifactory-ha.fullname" . }}-database-creds + {{- else }} + name: "{{ template "artifactory.unifiedSecretPrependReleaseName" . }}-unified-secret" + {{- end }} + key: db-url + {{- end }} + {{- end }} +{{- with .Values.evidence.extraEnvironmentVariables }} +{{ tpl (toYaml .) $ | indent 8 }} +{{- end }} + ports: + - containerPort: {{ .Values.evidence.internalPort }} + name: http-evidence + - containerPort: {{ .Values.evidence.externalPort }} + name: grpc-evidence + volumeMounts: + {{- if or .Values.artifactory.customVolumeMounts .Values.global.customVolumeMounts }} +{{ tpl (include "artifactory-ha.customVolumeMounts" .) . | indent 8 }} + {{- end }} + - name: volume + mountPath: {{ .Values.artifactory.persistence.mountPath | quote }} + resources: +{{ toYaml .Values.evidence.resources | indent 10 }} + {{- if .Values.evidence.startupProbe.enabled }} + startupProbe: +{{ tpl .Values.evidence.startupProbe.config . | indent 10 }} + {{- end }} + {{- if .Values.evidence.livenessProbe.enabled }} + livenessProbe: +{{ tpl .Values.evidence.livenessProbe.config . | indent 10 }} + {{- end }} + {{- end }} + {{- if .Values.metadata.enabled }} + - name: {{ .Values.metadata.name }} + image: {{ include "artifactory-ha.getImageInfoByValue" (list . "metadata") }} + imagePullPolicy: {{ .Values.artifactory.image.pullPolicy }} + {{- if .Values.containerSecurityContext.enabled }} + securityContext: {{- omit .Values.containerSecurityContext "enabled" | toYaml | nindent 10 }} + {{- end }} + command: + - '/bin/bash' + - '-c' + {{- if default false .Values.metadata.standaloneImageEnabled }} + - exec /opt/jfrog/metadata/app/metadata/bin/jf-metadata + {{- else }} + - exec /opt/jfrog/artifactory/app/metadata/bin/jf-metadata start + {{- end }} + {{- with .Values.metadata.lifecycle }} + lifecycle: +{{ toYaml . | indent 10 }} + {{- end }} + env: + - name: JF_SHARED_NODE_ID + valueFrom: + fieldRef: + fieldPath: metadata.name + {{- if or .Values.database.secrets.user .Values.database.user }} + - name: JF_SHARED_DATABASE_USERNAME + valueFrom: + secretKeyRef: + {{- if .Values.database.secrets.user }} + name: {{ tpl .Values.database.secrets.user.name . }} + key: {{ tpl .Values.database.secrets.user.key . }} + {{- else if .Values.database.user }} + {{- if not .Values.artifactory.unifiedSecretInstallation }} + name: {{ template "artifactory-ha.fullname" . }}-database-creds + {{- else }} + name: "{{ template "artifactory.unifiedSecretPrependReleaseName" . }}-unified-secret" + {{- end }} + key: db-user + {{- end }} + {{- end }} + {{ if or .Values.database.secrets.password .Values.database.password .Values.postgresql.enabled }} + - name: JF_SHARED_DATABASE_PASSWORD + valueFrom: + secretKeyRef: + {{- if .Values.database.secrets.password }} + name: {{ tpl .Values.database.secrets.password.name . }} + key: {{ tpl .Values.database.secrets.password.key . }} + {{- else if .Values.database.password }} + {{- if not .Values.artifactory.unifiedSecretInstallation }} + name: {{ template "artifactory-ha.fullname" . }}-database-creds + {{- else }} + name: "{{ template "artifactory.unifiedSecretPrependReleaseName" . }}-unified-secret" + {{- end }} + key: db-password + {{- else if .Values.postgresql.enabled }} + name: {{ .Release.Name }}-postgresql + key: postgresql-password + {{- end }} + {{- end }} + {{- if or .Values.database.secrets.url .Values.database.url }} + - name: JF_SHARED_DATABASE_URL + valueFrom: + secretKeyRef: + {{- if .Values.database.secrets.url }} + name: {{ tpl .Values.database.secrets.url.name . }} + key: {{ tpl .Values.database.secrets.url.key . }} + {{- else if .Values.database.url }} + {{- if not .Values.artifactory.unifiedSecretInstallation }} + name: {{ template "artifactory-ha.fullname" . }}-database-creds + {{- else }} + name: "{{ template "artifactory.unifiedSecretPrependReleaseName" . }}-unified-secret" + {{- end }} + key: db-url + {{- end }} + {{- end }} +{{- with .Values.metadata.extraEnvironmentVariables }} +{{ tpl (toYaml .) $ | indent 8 }} +{{- end }} + volumeMounts: + {{- if or .Values.artifactory.customVolumeMounts .Values.global.customVolumeMounts }} +{{ tpl (include "artifactory-ha.customVolumeMounts" .) . | indent 8 }} + {{- end }} + - name: volume + {{- if default false .Values.metadata.standaloneImageEnabled }} + mountPath: {{ .Values.metadata.persistence.mountPath | quote }} + {{- else }} + mountPath: {{ .Values.artifactory.persistence.mountPath | quote }} + {{- end }} + resources: +{{ toYaml .Values.metadata.resources | indent 10 }} + {{- if .Values.metadata.startupProbe.enabled }} + startupProbe: +{{ tpl .Values.metadata.startupProbe.config . | indent 10 }} + {{- end }} + {{- if .Values.metadata.livenessProbe.enabled }} + livenessProbe: +{{ tpl .Values.metadata.livenessProbe.config . | indent 10 }} + {{- end }} + {{- end }} + {{- if .Values.onemodel.enabled }} + - name: {{ .Values.onemodel.name }} + image: {{ include "artifactory-ha.getImageInfoByValue" (list . "artifactory") }} + imagePullPolicy: {{ .Values.artifactory.image.pullPolicy }} + {{- if .Values.containerSecurityContext.enabled }} + securityContext: {{- omit .Values.containerSecurityContext "enabled" | toYaml | nindent 10 }} + {{- end }} + command: + - '/bin/bash' + - '-c' + - > + exec /opt/jfrog/artifactory/app/onemodel/bin/entrypoint-onemodel.sh + {{- with .Values.onemodel.lifecycle }} + lifecycle: +{{ toYaml . | indent 10 }} + {{- end }} + env: + - name: JF_SHARED_NODE_ID + valueFrom: + fieldRef: + fieldPath: metadata.name + - name: JF_ONEMODEL_LISTENADDR + value: "0.0.0.0" +{{- with .Values.onemodel.extraEnvironmentVariables }} +{{ tpl (toYaml .) $ | indent 8 }} +{{- end }} + volumeMounts: + {{- if or .Values.artifactory.customVolumeMounts .Values.global.customVolumeMounts }} +{{ tpl (include "artifactory-ha.customVolumeMounts" .) . | indent 8 }} + {{- end }} + - name: volume + mountPath: {{ .Values.artifactory.persistence.mountPath | quote }} + resources: +{{ toYaml .Values.onemodel.resources | indent 10 }} + {{- if .Values.onemodel.startupProbe.enabled }} + startupProbe: +{{ tpl .Values.onemodel.startupProbe.config . | indent 10 }} + {{- end }} + {{- if .Values.onemodel.livenessProbe.enabled }} + livenessProbe: +{{ tpl .Values.onemodel.livenessProbe.config . | indent 10 }} + {{- end }} + {{- if .Values.onemodel.readinessProbe.enabled }} + readinessProbe: +{{ tpl .Values.onemodel.readinessProbe.config . | indent 10 }} + {{- end }} + {{- end }} + {{- if .Values.event.enabled }} + - name: {{ .Values.event.name }} + image: {{ include "artifactory-ha.getImageInfoByValue" (list . "artifactory") }} + imagePullPolicy: {{ .Values.artifactory.image.pullPolicy }} + {{- if .Values.containerSecurityContext.enabled }} + securityContext: {{- omit .Values.containerSecurityContext "enabled" | toYaml | nindent 10 }} + {{- end }} + command: + - '/bin/bash' + - '-c' + - > + exec /opt/jfrog/artifactory/app/event/bin/jf-event start + {{- with .Values.event.lifecycle }} + lifecycle: +{{ toYaml . | indent 10 }} + {{- end }} + env: + - name: JF_SHARED_NODE_ID + valueFrom: + fieldRef: + fieldPath: metadata.name +{{- with .Values.event.extraEnvironmentVariables }} +{{ tpl (toYaml .) $ | indent 8 }} +{{- end }} + volumeMounts: + {{- if or .Values.artifactory.customVolumeMounts .Values.global.customVolumeMounts }} +{{ tpl (include "artifactory-ha.customVolumeMounts" .) . | indent 8 }} + {{- end }} + - name: volume + mountPath: {{ .Values.artifactory.persistence.mountPath | quote }} + resources: +{{ toYaml .Values.event.resources | indent 10 }} + {{- if .Values.event.startupProbe.enabled }} + startupProbe: +{{ tpl .Values.event.startupProbe.config . | indent 10 }} + {{- end }} + {{- if .Values.event.livenessProbe.enabled }} + livenessProbe: +{{ tpl .Values.event.livenessProbe.config . | indent 10 }} + {{- end }} + {{- end }} + {{- if .Values.jfconnect.enabled }} + - name: {{ .Values.jfconnect.name }} + image: {{ include "artifactory-ha.getImageInfoByValue" (list . "artifactory") }} + imagePullPolicy: {{ .Values.artifactory.image.pullPolicy }} + {{- if .Values.containerSecurityContext.enabled }} + securityContext: {{- omit .Values.containerSecurityContext "enabled" | toYaml | nindent 10 }} + {{- end }} + command: + - '/bin/bash' + - '-c' + - > + exec /opt/jfrog/artifactory/app/jfconnect/bin/jf-connect start + {{- with .Values.jfconnect.lifecycle }} + lifecycle: +{{ toYaml . | indent 10 }} + {{- end }} + env: + - name: JF_SHARED_NODE_ID + valueFrom: + fieldRef: + fieldPath: metadata.name +{{- with .Values.jfconnect.extraEnvironmentVariables }} +{{ tpl (toYaml .) $ | indent 8 }} +{{- end }} + volumeMounts: + {{- if or .Values.artifactory.customVolumeMounts .Values.global.customVolumeMounts }} +{{ tpl (include "artifactory-ha.customVolumeMounts" .) . | indent 8 }} + {{- end }} + - name: volume + mountPath: {{ .Values.artifactory.persistence.mountPath | quote }} + resources: +{{ toYaml .Values.jfconnect.resources | indent 10 }} + {{- if .Values.jfconnect.startupProbe.enabled }} + startupProbe: +{{ tpl .Values.jfconnect.startupProbe.config . | indent 10 }} + {{- end }} + {{- if .Values.jfconnect.livenessProbe.enabled }} + livenessProbe: +{{ tpl .Values.jfconnect.livenessProbe.config . | indent 10 }} + {{- end }} + {{- end }} + {{- if .Values.topology.enabled }} + - name: {{ .Values.topology.name }} + image: {{ include "artifactory-ha.getImageInfoByValue" (list . "artifactory") }} + imagePullPolicy: {{ .Values.artifactory.image.pullPolicy }} + {{- if .Values.containerSecurityContext.enabled }} + securityContext: {{- omit .Values.containerSecurityContext "enabled" | toYaml | nindent 10 }} + {{- end }} + command: + - '/bin/bash' + - '-c' + - > + exec /opt/jfrog/artifactory/app/topology/bin/entrypoint-topology.sh + {{- with .Values.topology.lifecycle }} + lifecycle: +{{ toYaml . | indent 10 }} + {{- end }} + env: + - name: JF_SHARED_NODE_ID + valueFrom: + fieldRef: + fieldPath: metadata.name + {{- if or .Values.database.secrets.user .Values.database.user }} + - name: JF_SHARED_DATABASE_USERNAME + valueFrom: + secretKeyRef: + {{- if .Values.database.secrets.user }} + name: {{ tpl .Values.database.secrets.user.name . }} + key: {{ tpl .Values.database.secrets.user.key . }} + {{- else if .Values.database.user }} + {{- if not .Values.artifactory.unifiedSecretInstallation }} + name: {{ template "artifactory-ha.fullname" . }}-database-creds + {{- else }} + name: "{{ template "artifactory.unifiedSecretPrependReleaseName" . }}-unified-secret" + {{- end }} + key: db-user + {{- end }} + {{- end }} + {{ if or .Values.database.secrets.password .Values.database.password .Values.postgresql.enabled }} + - name: JF_SHARED_DATABASE_PASSWORD + valueFrom: + secretKeyRef: + {{- if .Values.database.secrets.password }} + name: {{ tpl .Values.database.secrets.password.name . }} + key: {{ tpl .Values.database.secrets.password.key . }} + {{- else if .Values.database.password }} + {{- if not .Values.artifactory.unifiedSecretInstallation }} + name: {{ template "artifactory-ha.fullname" . }}-database-creds + {{- else }} + name: "{{ template "artifactory.unifiedSecretPrependReleaseName" . }}-unified-secret" + {{- end }} + key: db-password + {{- else if .Values.postgresql.enabled }} + name: {{ .Release.Name }}-postgresql + key: postgresql-password + {{- end }} + {{- end }} + {{- if or .Values.database.secrets.url .Values.database.url }} + - name: JF_SHARED_DATABASE_URL + valueFrom: + secretKeyRef: + {{- if .Values.database.secrets.url }} + name: {{ tpl .Values.database.secrets.url.name . }} + key: {{ tpl .Values.database.secrets.url.key . }} + {{- else if .Values.database.url }} + {{- if not .Values.artifactory.unifiedSecretInstallation }} + name: {{ template "artifactory-ha.fullname" . }}-database-creds + {{- else }} + name: "{{ template "artifactory.unifiedSecretPrependReleaseName" . }}-unified-secret" + {{- end }} + key: db-url + {{- end }} + {{- end }} +{{- with .Values.topology.extraEnvironmentVariables }} +{{ tpl (toYaml .) $ | indent 8 }} +{{- end }} + ports: + - containerPort: {{ .Values.topology.internalPort }} + name: http-topology + volumeMounts: + {{- if or .Values.artifactory.customVolumeMounts .Values.global.customVolumeMounts }} +{{ tpl (include "artifactory-ha.customVolumeMounts" .) . | indent 8 }} + {{- end }} + - name: volume + mountPath: {{ .Values.artifactory.persistence.mountPath | quote }} + resources: +{{ toYaml .Values.topology.resources | indent 10 }} + {{- if .Values.topology.startupProbe.enabled }} + startupProbe: +{{ tpl .Values.topology.startupProbe.config . | indent 10 }} + {{- end }} + {{- if .Values.topology.readinessProbe.enabled }} + readinessProbe: +{{ tpl .Values.topology.readinessProbe.config . | indent 10 }} + {{- end }} + {{- if .Values.topology.livenessProbe.enabled }} + livenessProbe: +{{ tpl .Values.topology.livenessProbe.config . | indent 10 }} + {{- end }} + {{- end }} + {{- if .Values.observability.enabled }} + - name: {{ .Values.observability.name }} + image: {{ include "artifactory-ha.getImageInfoByValue" (list . "observability") }} + imagePullPolicy: {{ .Values.artifactory.image.pullPolicy }} + {{- if .Values.containerSecurityContext.enabled }} + securityContext: {{- omit .Values.containerSecurityContext "enabled" | toYaml | nindent 10 }} + {{- end }} + command: + - '/bin/bash' + - '-c' + {{- if default false .Values.observability.standaloneImageEnabled }} + - exec /opt/jfrog/observability/app/bin/entrypoint-observability.sh + {{- else }} + - exec /opt/jfrog/artifactory/app/observability/bin/jf-observability start + {{- end }} + {{- with .Values.observability.lifecycle }} + lifecycle: +{{ toYaml . | indent 10 }} + {{- end }} + env: + - name: JF_SHARED_NODE_ID + valueFrom: + fieldRef: + fieldPath: metadata.name +{{- with .Values.observability.extraEnvironmentVariables }} +{{ tpl (toYaml .) $ | indent 8 }} +{{- end }} + volumeMounts: + {{- if or .Values.artifactory.customVolumeMounts .Values.global.customVolumeMounts }} +{{ tpl (include "artifactory-ha.customVolumeMounts" .) . | indent 8 }} + {{- end }} + - name: volume + {{- if default false .Values.observability.standaloneImageEnabled }} + mountPath: {{ .Values.observability.persistence.mountPath | quote }} + {{- else }} + mountPath: {{ .Values.artifactory.persistence.mountPath | quote }} + {{- end }} + resources: +{{ toYaml .Values.observability.resources | indent 10 }} + {{- if .Values.observability.startupProbe.enabled }} + startupProbe: +{{ tpl .Values.observability.startupProbe.config . | indent 10 }} + {{- end }} + {{- if .Values.observability.livenessProbe.enabled }} + livenessProbe: +{{ tpl .Values.observability.livenessProbe.config . | indent 10 }} + {{- end }} + {{- end }} + {{- if and .Values.access.enabled (not (.Values.access.runOnArtifactoryTomcat | default false)) }} + - name: {{ .Values.access.name }} + image: {{ include "artifactory-ha.getImageInfoByValue" (list . "artifactory") }} + imagePullPolicy: {{ .Values.artifactory.image.pullPolicy }} + {{- if .Values.containerSecurityContext.enabled }} + securityContext: {{- omit .Values.containerSecurityContext "enabled" | toYaml | nindent 10 }} + {{- end }} + {{- if .Values.access.resources }} + resources: +{{ toYaml .Values.access.resources | indent 10 }} + {{- end }} + command: + - '/bin/bash' + - '-c' + - > + set -e; + {{- if .Values.access.preStartCommand }} + echo "Running custom preStartCommand command"; + {{ tpl .Values.access.preStartCommand . }}; + {{- end }} + exec /opt/jfrog/artifactory/app/access/bin/entrypoint-access.sh + {{- with .Values.access.lifecycle }} + lifecycle: +{{ toYaml . | indent 10 }} + {{- end }} + env: + {{- if and (not .Values.waitForDatabase) (not .Values.postgresql.enabled) }} + - name: SKIP_WAIT_FOR_EXTERNAL_DB + value: "true" + {{- end }} + {{- if or .Values.database.secrets.user .Values.database.user }} + - name: JF_SHARED_DATABASE_USERNAME + valueFrom: + secretKeyRef: + {{- if .Values.database.secrets.user }} + name: {{ tpl .Values.database.secrets.user.name . }} + key: {{ tpl .Values.database.secrets.user.key . }} + {{- else if .Values.database.user }} + {{- if not .Values.artifactory.unifiedSecretInstallation }} + name: {{ template "artifactory-ha.fullname" . }}-database-creds + {{- else }} + name: "{{ template "artifactory.unifiedSecretPrependReleaseName" . }}-unified-secret" + {{- end }} + key: db-user + {{- end }} + {{- end }} + {{ if or .Values.database.secrets.password .Values.database.password .Values.postgresql.enabled }} + - name: JF_SHARED_DATABASE_PASSWORD + valueFrom: + secretKeyRef: + {{- if .Values.database.secrets.password }} + name: {{ tpl .Values.database.secrets.password.name . }} + key: {{ tpl .Values.database.secrets.password.key . }} + {{- else if .Values.database.password }} + {{- if not .Values.artifactory.unifiedSecretInstallation }} + name: {{ template "artifactory-ha.fullname" . }}-database-creds + {{- else }} + name: "{{ template "artifactory.unifiedSecretPrependReleaseName" . }}-unified-secret" + {{- end }} + key: db-password + {{- else if .Values.postgresql.enabled }} + name: {{ .Release.Name }}-postgresql + key: postgresql-password + {{- end }} + {{- end }} + {{- if or .Values.database.secrets.url .Values.database.url }} + - name: JF_SHARED_DATABASE_URL + valueFrom: + secretKeyRef: + {{- if .Values.database.secrets.url }} + name: {{ tpl .Values.database.secrets.url.name . }} + key: {{ tpl .Values.database.secrets.url.key . }} + {{- else if .Values.database.url }} + {{- if not .Values.artifactory.unifiedSecretInstallation }} + name: {{ template "artifactory-ha.fullname" . }}-database-creds + {{- else }} + name: "{{ template "artifactory.unifiedSecretPrependReleaseName" . }}-unified-secret" + {{- end }} + key: db-url + {{- end }} + {{- end }} +{{- with .Values.access.extraEnvironmentVariables }} +{{ tpl (toYaml .) $ | indent 8 }} +{{- end }} + volumeMounts: + {{- if .Values.artifactory.customPersistentVolumeClaim }} + - name: {{ .Values.artifactory.customPersistentVolumeClaim.name }} + mountPath: {{ .Values.artifactory.customPersistentVolumeClaim.mountPath }} + {{- end }} + {{- if .Values.artifactory.customPersistentPodVolumeClaim }} + - name: {{ .Values.artifactory.customPersistentPodVolumeClaim.name }} + mountPath: {{ .Values.artifactory.customPersistentPodVolumeClaim.mountPath }} + {{- end }} + {{- if .Values.aws.licenseConfigSecretName }} + - name: awsmp-product-license + mountPath: "/var/run/secrets/product-license" + {{- end }} + - name: volume + mountPath: "{{ .Values.artifactory.persistence.mountPath }}" + + ######################## Artifactory persistence fs ########################## + {{- if eq .Values.artifactory.persistence.type "file-system" }} + {{- if .Values.artifactory.persistence.fileSystem.existingSharedClaim.enabled }} + {{- range $sharedClaimNumber, $e := until (.Values.artifactory.persistence.fileSystem.existingSharedClaim.numberOfExistingClaims|int) }} + - name: artifactory-ha-data-{{ $sharedClaimNumber }} + mountPath: "{{ tpl $.Values.artifactory.persistence.fileSystem.existingSharedClaim.dataDir $ }}/filestore{{ $sharedClaimNumber }}" + {{- end }} + - name: artifactory-ha-backup + mountPath: "{{ $.Values.artifactory.persistence.fileSystem.existingSharedClaim.backupDir }}" + {{- end }} + {{- end }} + + ######################## Artifactory persistence nfs ########################## + {{- if eq .Values.artifactory.persistence.type "nfs" }} + - name: artifactory-ha-data + mountPath: "{{ .Values.artifactory.persistence.nfs.dataDir }}" + - name: artifactory-ha-backup + mountPath: "{{ .Values.artifactory.persistence.nfs.backupDir }}" + {{- else }} + + ######################## Artifactory persistence binarystore Xml ########################## + {{- if or (not .Values.artifactory.unifiedSecretInstallation) .Values.artifactory.persistence.customBinarystoreXmlSecret }} + - name: binarystore-xml + {{- else }} + - name: {{ include "artifactory-ha.unifiedCustomSecretVolumeName" . }} + {{- end }} + mountPath: "/tmp/etc/artifactory/binarystore.xml" + subPath: binarystore.xml + + ######################## Artifactory persistence google storage ########################## + {{- if .Values.artifactory.persistence.googleStorage.gcpServiceAccount.enabled }} + {{- if or (not .Values.artifactory.unifiedSecretInstallation) .Values.artifactory.persistence.googleStorage.gcpServiceAccount.customSecretName }} + - name: gcpcreds-json + {{- else }} + - name: {{ include "artifactory-ha.unifiedCustomSecretVolumeName" . }} + {{- end }} + mountPath: "/artifactory_bootstrap/gcp.credentials.json" + subPath: gcp.credentials.json + {{- end }} + + + ######################## Artifactory license ########################## + {{- if or .Values.artifactory.license.secret .Values.artifactory.license.licenseKey }} + {{- if or (not .Values.artifactory.unifiedSecretInstallation) .Values.artifactory.license.secret }} + - name: artifactory-license + {{- else }} + - name: {{ include "artifactory-ha.unifiedCustomSecretVolumeName" . }} + {{- end }} + mountPath: "/artifactory_bootstrap/artifactory.cluster.license" + {{- if .Values.artifactory.license.secret }} + subPath: {{ .Values.artifactory.license.dataKey }} + {{- else if .Values.artifactory.license.licenseKey }} + subPath: artifactory.lic + {{- end }} + {{- end }} + {{- end }} + {{- if or .Values.artifactory.customVolumeMounts .Values.global.customVolumeMounts }} +{{ tpl (include "artifactory-ha.customVolumeMounts" .) . | indent 8 }} + {{- end }} + {{- if .Values.access.startupProbe.enabled }} + startupProbe: +{{ tpl .Values.access.startupProbe.config . | indent 10 }} + {{- end }} + {{- if semverCompare " + set -e; + if [ -d /artifactory_extra_conf ] && [ -d /artifactory_bootstrap ]; then + echo "Copying bootstrap config from /artifactory_extra_conf to /artifactory_bootstrap"; + cp -Lrfv /artifactory_extra_conf/ /artifactory_bootstrap/; + fi; + {{- if .Values.artifactory.configMapName }} + echo "Copying bootstrap configs"; + cp -Lrf /bootstrap/* /artifactory_bootstrap/; + {{- end }} + {{- if .Values.artifactory.userPluginSecrets }} + echo "Copying plugins"; + cp -Lrf /tmp/plugin/*/* /artifactory_bootstrap/plugins; + {{- end }} + {{- range .Values.artifactory.copyOnEveryStartup }} + {{- $targetPath := printf "%s/%s" $.Values.artifactory.persistence.mountPath .target }} + {{- $baseDirectory := regexFind ".*/" $targetPath }} + mkdir -p {{ $baseDirectory }}; + cp -Lrf {{ .source }} {{ $.Values.artifactory.persistence.mountPath }}/{{ .target }}; + {{- end }} + {{- with .Values.artifactory.preStartCommand }} + echo "Running custom preStartCommand command"; + {{ tpl . $ }}; + {{- end }} + {{- with .Values.artifactory.primary.preStartCommand }} + echo "Running primary specific custom preStartCommand command"; + {{ tpl . $ }}; + {{- end }} + exec /entrypoint-artifactory.sh + {{- with .Values.artifactory.lifecycle }} + lifecycle: +{{ toYaml . | indent 10 }} + {{- end }} + env: + {{- if .Values.aws.license.enabled }} + - name: IS_AWS_LICENSE + value: "true" + - name: AWS_REGION + value: {{ .Values.aws.region | quote }} + {{- if .Values.aws.licenseConfigSecretName }} + - name: AWS_WEB_IDENTITY_REFRESH_TOKEN_FILE + value: "/var/run/secrets/product-license/license_token" + - name: AWS_ROLE_ARN + valueFrom: + secretKeyRef: + name: {{ .Values.aws.licenseConfigSecretName }} + key: iam_role + {{- end }} + {{- end }} + {{- if .Values.splitServicesToContainers }} + - name : JF_ROUTER_ENABLED + value: "true" + - name : JF_ROUTER_SERVICE_ENABLED + value: "false" + - name : JF_EVENT_ENABLED + value: "false" + - name : JF_METADATA_ENABLED + value: "false" + - name : JF_FRONTEND_ENABLED + value: "false" + - name: JF_FEDERATION_ENABLED + value: "false" + - name : JF_OBSERVABILITY_ENABLED + value: "false" + - name : JF_JFCONNECT_SERVICE_ENABLED + value: "false" + - name : JF_EVIDENCE_ENABLED + value: "false" + - name : JF_ONEMODEL_ENABLED + value: "false" + {{- if not (.Values.access.runOnArtifactoryTomcat | default false) }} + - name : JF_ACCESS_ENABLED + value: "false" + {{- end}} + - name: JF_TOPOLOGY_SPLIT_CONTAINER_ENABLED + value: "true" + {{- end }} + {{- if and (not .Values.waitForDatabase) (not .Values.postgresql.enabled) }} + - name: SKIP_WAIT_FOR_EXTERNAL_DB + value: "true" + {{- end }} + {{- if or .Values.database.secrets.user .Values.database.user }} + - name: JF_SHARED_DATABASE_USERNAME + valueFrom: + secretKeyRef: + {{- if .Values.database.secrets.user }} + name: {{ tpl .Values.database.secrets.user.name . }} + key: {{ tpl .Values.database.secrets.user.key . }} + {{- else if .Values.database.user }} + {{- if not .Values.artifactory.unifiedSecretInstallation }} + name: {{ template "artifactory-ha.fullname" . }}-database-creds + {{- else }} + name: "{{ template "artifactory.unifiedSecretPrependReleaseName" . }}-unified-secret" + {{- end }} + key: db-user + {{- end }} + {{- end }} + {{ if or .Values.database.secrets.password .Values.database.password .Values.postgresql.enabled }} + - name: JF_SHARED_DATABASE_PASSWORD + valueFrom: + secretKeyRef: + {{- if .Values.database.secrets.password }} + name: {{ tpl .Values.database.secrets.password.name . }} + key: {{ tpl .Values.database.secrets.password.key . }} + {{- else if .Values.database.password }} + {{- if not .Values.artifactory.unifiedSecretInstallation }} + name: {{ template "artifactory-ha.fullname" . }}-database-creds + {{- else }} + name: "{{ template "artifactory.unifiedSecretPrependReleaseName" . }}-unified-secret" + {{- end }} + key: db-password + {{- else if .Values.postgresql.enabled }} + name: {{ .Release.Name }}-postgresql + key: postgresql-password + {{- end }} + {{- end }} + {{- if or .Values.database.secrets.url .Values.database.url }} + - name: JF_SHARED_DATABASE_URL + valueFrom: + secretKeyRef: + {{- if .Values.database.secrets.url }} + name: {{ tpl .Values.database.secrets.url.name . }} + key: {{ tpl .Values.database.secrets.url.key . }} + {{- else if .Values.database.url }} + {{- if not .Values.artifactory.unifiedSecretInstallation }} + name: {{ template "artifactory-ha.fullname" . }}-database-creds + {{- else }} + name: "{{ template "artifactory.unifiedSecretPrependReleaseName" . }}-unified-secret" + {{- end }} + key: db-url + {{- end }} + {{- end }} + - name: JF_SHARED_NODE_HAENABLED + value: "true" +{{- with .Values.artifactory.extraEnvironmentVariables }} +{{ tpl (toYaml .) $ | indent 8 }} +{{- end }} + ports: + - containerPort: {{ .Values.artifactory.internalPort }} + name: http + - containerPort: {{ .Values.artifactory.internalArtifactoryPort }} + name: http-internal + {{- if .Values.artifactory.primary.javaOpts.jmx.enabled }} + - containerPort: {{ .Values.artifactory.primary.javaOpts.jmx.port }} + name: tcp-jmx + {{- end }} + {{- if .Values.artifactory.ssh.enabled }} + - containerPort: {{ .Values.artifactory.ssh.internalPort }} + name: tcp-ssh + {{- end }} + + volumeMounts: + {{- if .Values.artifactory.customPersistentVolumeClaim }} + - name: {{ .Values.artifactory.customPersistentVolumeClaim.name }} + mountPath: {{ .Values.artifactory.customPersistentVolumeClaim.mountPath }} + {{- end }} + {{- if .Values.artifactory.customPersistentPodVolumeClaim }} + - name: {{ .Values.artifactory.customPersistentPodVolumeClaim.name }} + mountPath: {{ .Values.artifactory.customPersistentPodVolumeClaim.mountPath }} + {{- end }} + {{- if .Values.aws.licenseConfigSecretName }} + - name: awsmp-product-license + mountPath: "/var/run/secrets/product-license" + {{- end }} + {{- if .Values.artifactory.userPluginSecrets }} + - name: bootstrap-plugins + mountPath: "/artifactory_bootstrap/plugins/" + {{- range .Values.artifactory.userPluginSecrets }} + - name: {{ tpl . $ }} + mountPath: "/tmp/plugin/{{ tpl . $ }}" + {{- end }} + {{- end }} + - name: volume + mountPath: "{{ .Values.artifactory.persistence.mountPath }}" + + ######################## Artifactory persistence fs ########################## + {{- if eq .Values.artifactory.persistence.type "file-system" }} + {{- if .Values.artifactory.persistence.fileSystem.existingSharedClaim.enabled }} + {{- range $sharedClaimNumber, $e := until (.Values.artifactory.persistence.fileSystem.existingSharedClaim.numberOfExistingClaims|int) }} + - name: artifactory-ha-data-{{ $sharedClaimNumber }} + mountPath: "{{ tpl $.Values.artifactory.persistence.fileSystem.existingSharedClaim.dataDir $ }}/filestore{{ $sharedClaimNumber }}" + {{- end }} + - name: artifactory-ha-backup + mountPath: "{{ $.Values.artifactory.persistence.fileSystem.existingSharedClaim.backupDir }}" + {{- end }} + {{- end }} + + ######################## Artifactory persistence nfs ########################## + {{- if eq .Values.artifactory.persistence.type "nfs" }} + - name: artifactory-ha-data + mountPath: "{{ .Values.artifactory.persistence.nfs.dataDir }}" + - name: artifactory-ha-backup + mountPath: "{{ .Values.artifactory.persistence.nfs.backupDir }}" + {{- else }} + + ######################## Artifactory persistence binarystoreXml ########################## + {{- if or (not .Values.artifactory.unifiedSecretInstallation) .Values.artifactory.persistence.customBinarystoreXmlSecret }} + - name: binarystore-xml + {{- else }} + - name: {{ include "artifactory-ha.unifiedCustomSecretVolumeName" . }} + {{- end }} + mountPath: "/tmp/etc/artifactory/binarystore.xml" + subPath: binarystore.xml + + ######################## Artifactory persistence googleStorage ########################## + {{- if .Values.artifactory.persistence.googleStorage.gcpServiceAccount.enabled }} + {{- if or (not .Values.artifactory.unifiedSecretInstallation) .Values.artifactory.persistence.googleStorage.gcpServiceAccount.customSecretName }} + - name: gcpcreds-json + {{- else }} + - name: {{ include "artifactory-ha.unifiedCustomSecretVolumeName" . }} + {{- end }} + mountPath: "/artifactory_bootstrap/gcp.credentials.json" + subPath: gcp.credentials.json + {{- end }} + {{- end }} + + ######################## Artifactory configMapName ########################## + {{- if .Values.artifactory.configMapName }} + - name: bootstrap-config + mountPath: "/bootstrap/" + {{- end }} + + ######################## Artifactory license ########################## + {{- if or .Values.artifactory.license.secret .Values.artifactory.license.licenseKey }} + {{- if or (not .Values.artifactory.unifiedSecretInstallation) .Values.artifactory.license.secret }} + - name: artifactory-license + {{- else }} + - name: {{ include "artifactory-ha.unifiedCustomSecretVolumeName" . }} + {{- end }} + mountPath: "/artifactory_bootstrap/artifactory.cluster.license" + {{- if .Values.artifactory.license.secret }} + subPath: {{ .Values.artifactory.license.dataKey }} + {{- else if .Values.artifactory.license.licenseKey }} + subPath: artifactory.lic + {{- end }} + {{- end }} + + - name: installer-info + mountPath: "/artifactory_bootstrap/info/installer-info.json" + subPath: installer-info.json + {{- if or .Values.artifactory.customVolumeMounts .Values.global.customVolumeMounts }} +{{ tpl (include "artifactory-ha.customVolumeMounts" .) . | indent 8 }} + {{- end }} + resources: +{{ toYaml .Values.artifactory.primary.resources | indent 10 }} + {{- if .Values.artifactory.startupProbe.enabled }} + startupProbe: +{{ tpl .Values.artifactory.startupProbe.config . | indent 10 }} + {{- end }} + {{- if and (not .Values.splitServicesToContainers) (semverCompare "=1.18.0-0" .Capabilities.KubeVersion.GitVersion) }} + ingressClassName: {{ .Values.ingressGrpc.className }} + {{- end }} + {{- if .Values.ingressGrpc.defaultBackend.enabled }} + {{- if .Capabilities.APIVersions.Has "networking.k8s.io/v1" }} + defaultBackend: + service: + name: {{ $serviceName }} + port: + number: {{ $servicePort }} + {{- else }} + backend: + serviceName: {{ $serviceName }} + servicePort: {{ $servicePort }} + {{- end }} + {{- end }} + rules: +{{- if .Values.ingressGrpc.hosts }} + {{- if .Capabilities.APIVersions.Has "networking.k8s.io/v1" }} + {{- range $host := .Values.ingressGrpc.hosts }} + - host: {{ $host | quote }} + http: + paths: + - path: {{ $.Values.ingressGrpc.grpcPath }} + pathType: {{ $.Values.ingressGrpc.pathType | default "ImplementationSpecific" }} + backend: + service: + name: {{ $serviceName }} + port: + number: {{ $servicePort }} + {{- end }} + {{- else }} + {{- range $host := .Values.ingressGrpc.hosts }} + - host: {{ $host | quote }} + http: + paths: + - path: {{ $.Values.ingressGrpc.grpcPath }} + backend: + serviceName: {{ $serviceName }} + servicePort: {{ $servicePort }} + {{- end }} + {{- end }} +{{- end -}} + {{- with .Values.ingressGrpc.additionalRules }} +{{ tpl . $ | indent 2 }} + {{- end }} + + {{- if .Values.ingressGrpc.tls }} + tls: +{{ toYaml .Values.ingressGrpc.tls | indent 4 }} + {{- end -}} + +{{- end -}} diff --git a/charts/jfrog/artifactory-ha/107.104.10/templates/ingress.yaml b/charts/jfrog/artifactory-ha/107.104.10/templates/ingress.yaml new file mode 100644 index 0000000000..125bbc56b3 --- /dev/null +++ b/charts/jfrog/artifactory-ha/107.104.10/templates/ingress.yaml @@ -0,0 +1,107 @@ +{{- if .Values.ingress.enabled -}} +{{- $serviceName := include "artifactory-ha.fullname" . -}} +{{- $servicePort := .Values.artifactory.externalPort -}} +{{- $serviceRTFSName := include "rtfs.fullname" . -}} +{{- $artifactoryServicePort := .Values.artifactory.externalArtifactoryPort -}} +{{- $ingressName := default ( include "artifactory-ha.fullname" . ) .Values.ingress.name -}} +{{- if .Capabilities.APIVersions.Has "networking.k8s.io/v1" }} +apiVersion: networking.k8s.io/v1 +{{- else if .Capabilities.APIVersions.Has "networking.k8s.io/v1beta1" }} +apiVersion: networking.k8s.io/v1beta1 +{{- else }} +apiVersion: extensions/v1beta1 +{{- end }} +kind: Ingress +metadata: + name: {{ $ingressName }} + labels: + app: {{ template "artifactory-ha.name" . }} + chart: {{ template "artifactory-ha.chart" . }} + release: {{ .Release.Name }} + heritage: {{ .Release.Service }} +{{- if .Values.ingress.labels }} +{{ .Values.ingress.labels | toYaml | trimSuffix "\n"| indent 4 -}} +{{- end}} +{{- if .Values.ingress.annotations }} + annotations: +{{ .Values.ingress.annotations | toYaml | trimSuffix "\n" | indent 4 -}} +{{- end }} +spec: + {{- if and .Values.ingress.className (semverCompare ">=1.18.0-0" .Capabilities.KubeVersion.GitVersion) }} + ingressClassName: {{ .Values.ingress.className }} + {{- end }} + {{- if .Values.ingress.defaultBackend.enabled }} + {{- if .Capabilities.APIVersions.Has "networking.k8s.io/v1" }} + defaultBackend: + service: + name: {{ $serviceName }} + port: + number: {{ $servicePort }} + {{- else }} + backend: + serviceName: {{ $serviceName }} + servicePort: {{ $servicePort }} + {{- end }} + {{- end }} + rules: +{{- if .Values.ingress.hosts }} + {{- if .Capabilities.APIVersions.Has "networking.k8s.io/v1" }} + {{- range $host := .Values.ingress.hosts }} + - host: {{ $host | quote }} + http: + paths: + - path: {{ $.Values.ingress.routerPath }} + pathType: {{ $.Values.ingress.pathType | default "ImplementationSpecific" }} + backend: + service: + name: {{ $serviceName }} + port: + number: {{ $servicePort }} + {{- if not $.Values.ingress.disableRouterBypass }} + - path: {{ $.Values.ingress.artifactoryPath }} + pathType: {{ $.Values.ingress.pathType | default "ImplementationSpecific" }} + backend: + service: + name: {{ $serviceName }} + port: + number: {{ $artifactoryServicePort }} + {{- end }} + {{- if and $.Values.rtfs.enabled $.Values.ingress.enabled (not (regexMatch "^.*(oss|cpp-ce|jcr).*$" $.Values.artifactory.image.repository)) }} + - path: {{ $.Values.ingress.rtfsPath }} + pathType: {{ $.Values.ingress.pathType | default "ImplementationSpecific" }} + backend: + service: + name: {{ $serviceRTFSName }} + port: + name: http-router + {{- end }} + {{- end }} + {{- else }} + {{- range $host := .Values.ingress.hosts }} + - host: {{ $host | quote }} + http: + paths: + - path: {{ $.Values.ingress.routerPath }} + backend: + serviceName: {{ $serviceName }} + servicePort: {{ $servicePort }} + - path: {{ $.Values.ingress.artifactoryPath }} + backend: + serviceName: {{ $serviceName }} + servicePort: {{ $artifactoryServicePort }} + {{- end }} + {{- end }} +{{- end -}} + {{- with .Values.ingress.additionalRules }} +{{ tpl . $ | indent 2 }} + {{- end }} + {{- if .Values.ingress.tls }} + tls: +{{ toYaml .Values.ingress.tls | indent 4 }} + {{- end -}} + +{{- if .Values.customIngress }} +--- +{{ .Values.customIngress | toYaml | trimSuffix "\n" }} +{{- end -}} +{{- end -}} diff --git a/charts/jfrog/artifactory-ha/107.104.10/templates/logger-configmap.yaml b/charts/jfrog/artifactory-ha/107.104.10/templates/logger-configmap.yaml new file mode 100644 index 0000000000..d3597905d9 --- /dev/null +++ b/charts/jfrog/artifactory-ha/107.104.10/templates/logger-configmap.yaml @@ -0,0 +1,63 @@ +{{- if or .Values.artifactory.loggers .Values.artifactory.catalinaLoggers }} +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ template "artifactory-ha.fullname" . }}-logger + labels: + app: {{ template "artifactory-ha.name" . }} + chart: {{ template "artifactory-ha.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} +data: + tail-log.sh: | + #!/bin/sh + + LOG_DIR=$1 + LOG_NAME=$2 + PID= + + # Wait for log dir to appear + while [ ! -d ${LOG_DIR} ]; do + sleep 1 + done + + cd ${LOG_DIR} + + LOG_PREFIX=$(echo ${LOG_NAME} | sed 's/.log$//g') + + # Find the log to tail + LOG_FILE=$(ls -1t ./${LOG_PREFIX}.log 2>/dev/null) + + # Wait for the log file + while [ -z "${LOG_FILE}" ]; do + sleep 1 + LOG_FILE=$(ls -1t ./${LOG_PREFIX}.log 2>/dev/null) + done + + echo "Log file ${LOG_FILE} is ready!" + + # Get inode number + INODE_ID=$(ls -i ${LOG_FILE}) + + # echo "Tailing ${LOG_FILE}" + tail -F ${LOG_FILE} & + PID=$! + + # Loop forever to see if a new log was created + while true; do + # Check inode number + NEW_INODE_ID=$(ls -i ${LOG_FILE}) + + # If inode number changed, this means log was rotated and need to start a new tail + if [ "${INODE_ID}" != "${NEW_INODE_ID}" ]; then + kill -9 ${PID} 2>/dev/null + INODE_ID="${NEW_INODE_ID}" + + # Start a new tail + tail -F ${LOG_FILE} & + PID=$! + fi + sleep 1 + done + +{{- end }} diff --git a/charts/jfrog/artifactory-ha/107.104.10/templates/nginx-artifactory-conf.yaml b/charts/jfrog/artifactory-ha/107.104.10/templates/nginx-artifactory-conf.yaml new file mode 100644 index 0000000000..97ae5f27be --- /dev/null +++ b/charts/jfrog/artifactory-ha/107.104.10/templates/nginx-artifactory-conf.yaml @@ -0,0 +1,18 @@ +{{- if and (not .Values.nginx.customArtifactoryConfigMap) .Values.nginx.enabled }} +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ template "artifactory-ha.fullname" . }}-nginx-artifactory-conf + labels: + app: {{ template "artifactory-ha.name" . }} + chart: {{ template "artifactory-ha.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} +data: + artifactory.conf: | +{{- if .Values.nginx.artifactoryConf }} +{{ tpl .Values.nginx.artifactoryConf . | indent 4 }} +{{- else }} +{{ tpl ( .Files.Get "files/nginx-artifactory-conf.yaml" ) . | indent 4 }} +{{- end }} +{{- end }} \ No newline at end of file diff --git a/charts/jfrog/artifactory-ha/107.104.10/templates/nginx-certificate-secret.yaml b/charts/jfrog/artifactory-ha/107.104.10/templates/nginx-certificate-secret.yaml new file mode 100644 index 0000000000..29c77ad5a4 --- /dev/null +++ b/charts/jfrog/artifactory-ha/107.104.10/templates/nginx-certificate-secret.yaml @@ -0,0 +1,14 @@ +{{- if and (not .Values.nginx.tlsSecretName) .Values.nginx.enabled .Values.nginx.https.enabled }} +apiVersion: v1 +kind: Secret +type: kubernetes.io/tls +metadata: + name: {{ template "artifactory-ha.fullname" . }}-nginx-certificate + labels: + app: {{ template "artifactory-ha.name" . }} + chart: {{ template "artifactory-ha.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} +data: +{{ ( include "artifactory-ha.gen-certs" . ) | indent 2 }} +{{- end }} diff --git a/charts/jfrog/artifactory-ha/107.104.10/templates/nginx-conf.yaml b/charts/jfrog/artifactory-ha/107.104.10/templates/nginx-conf.yaml new file mode 100644 index 0000000000..4f0d65f25c --- /dev/null +++ b/charts/jfrog/artifactory-ha/107.104.10/templates/nginx-conf.yaml @@ -0,0 +1,18 @@ +{{- if and (not .Values.nginx.customConfigMap) .Values.nginx.enabled }} +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ template "artifactory-ha.fullname" . }}-nginx-conf + labels: + app: {{ template "artifactory-ha.name" . }} + chart: {{ template "artifactory-ha.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} +data: + nginx.conf: | +{{- if .Values.nginx.mainConf }} +{{ tpl .Values.nginx.mainConf . | indent 4 }} +{{- else }} +{{ tpl ( .Files.Get "files/nginx-main-conf.yaml" ) . | indent 4 }} +{{- end }} +{{- end }} diff --git a/charts/jfrog/artifactory-ha/107.104.10/templates/nginx-deployment.yaml b/charts/jfrog/artifactory-ha/107.104.10/templates/nginx-deployment.yaml new file mode 100644 index 0000000000..d43689b8cd --- /dev/null +++ b/charts/jfrog/artifactory-ha/107.104.10/templates/nginx-deployment.yaml @@ -0,0 +1,221 @@ +{{- if .Values.nginx.enabled -}} +{{- $serviceName := include "artifactory-ha.fullname" . -}} +{{- $servicePort := .Values.artifactory.externalPort -}} +apiVersion: apps/v1 +kind: {{ .Values.nginx.kind }} +metadata: + name: {{ template "artifactory-ha.nginx.fullname" . }} + labels: + app: {{ template "artifactory-ha.name" . }} + chart: {{ template "artifactory-ha.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: {{ .Values.nginx.name }} +{{- if .Values.nginx.labels }} +{{ toYaml .Values.nginx.labels | indent 4 }} +{{- end }} +{{- with .Values.nginx.deployment.annotations }} + annotations: +{{ toYaml . | indent 4 }} +{{- end }} +spec: +{{- if ne .Values.nginx.kind "DaemonSet" }} + replicas: {{ .Values.nginx.replicaCount }} +{{- end }} + selector: + matchLabels: + app: {{ template "artifactory-ha.name" . }} + release: {{ .Release.Name }} + component: {{ .Values.nginx.name }} + template: + metadata: + annotations: + checksum/nginx-conf: {{ include (print $.Template.BasePath "/nginx-conf.yaml") . | sha256sum }} + checksum/nginx-artifactory-conf: {{ include (print $.Template.BasePath "/nginx-artifactory-conf.yaml") . | sha256sum }} + {{- range $key, $value := .Values.nginx.annotations }} + {{ $key }}: {{ $value | quote }} + {{- end }} + labels: + app: {{ template "artifactory-ha.name" . }} + chart: {{ template "artifactory-ha.chart" . }} + component: {{ .Values.nginx.name }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} +{{- if .Values.nginx.labels }} +{{ toYaml .Values.nginx.labels | indent 8 }} +{{- end }} + spec: + {{- if .Values.nginx.podSecurityContext.enabled }} + securityContext: {{- omit .Values.nginx.podSecurityContext "enabled" | toYaml | nindent 8 }} + {{- end }} + serviceAccountName: {{ template "artifactory-ha.serviceAccountName" . }} + terminationGracePeriodSeconds: {{ .Values.nginx.terminationGracePeriodSeconds }} + {{- if or .Values.imagePullSecrets .Values.global.imagePullSecrets }} +{{- include "artifactory-ha.imagePullSecrets" . | indent 6 }} + {{- end }} + {{- if .Values.nginx.priorityClassName }} + priorityClassName: {{ .Values.nginx.priorityClassName | quote }} + {{- end }} + {{- if .Values.nginx.topologySpreadConstraints }} + topologySpreadConstraints: +{{ tpl (toYaml .Values.nginx.topologySpreadConstraints) . | indent 8 }} + {{- end }} + initContainers: + {{- if .Values.nginx.customInitContainers }} +{{ tpl (include "artifactory.nginx.customInitContainers" .) . | indent 6 }} + {{- end }} + - name: "setup" + image: {{ include "artifactory-ha.getImageInfoByValue" (list . "initContainers") }} + imagePullPolicy: {{ .Values.initContainers.image.imagePullPolicy }} + {{- if .Values.containerSecurityContext.enabled }} + securityContext: {{- omit .Values.containerSecurityContext "enabled" | toYaml | nindent 10 }} + {{- end }} + command: + - '/bin/sh' + - '-c' + - > + rm -rfv {{ .Values.nginx.persistence.mountPath }}/lost+found; + mkdir -p {{ .Values.nginx.persistence.mountPath }}/logs; + resources: + {{- toYaml .Values.initContainers.resources | nindent 10 }} + volumeMounts: + - mountPath: {{ .Values.nginx.persistence.mountPath | quote }} + name: nginx-volume + containers: + - name: {{ .Values.nginx.name }} + image: {{ include "artifactory-ha.getImageInfoByValue" (list . "nginx") }} + imagePullPolicy: {{ .Values.nginx.image.pullPolicy }} + {{- if .Values.nginx.containerSecurityContext.enabled }} + securityContext: {{- omit .Values.nginx.containerSecurityContext "enabled" | toYaml | nindent 10 }} + {{- end }} + {{- if .Values.nginx.customCommand }} + command: +{{- tpl (include "nginx.command" .) . | indent 10 }} + {{- end }} + ports: +{{ if .Values.nginx.customPorts }} +{{ toYaml .Values.nginx.customPorts | indent 8 }} +{{ end }} + # DEPRECATION NOTE: The following is to maintain support for values pre 1.3.1 and + # will be cleaned up in a later version + {{- if .Values.nginx.http }} + {{- if .Values.nginx.http.enabled }} + - containerPort: {{ .Values.nginx.http.internalPort }} + name: http + {{- end }} + {{- else }} # DEPRECATED + - containerPort: {{ .Values.nginx.internalPortHttp }} + name: http-internal + {{- end }} + {{- if .Values.nginx.https }} + {{- if .Values.nginx.https.enabled }} + - containerPort: {{ .Values.nginx.https.internalPort }} + name: https + {{- end }} + {{- else }} # DEPRECATED + - containerPort: {{ .Values.nginx.internalPortHttps }} + name: https-internal + {{- end }} + {{- if .Values.artifactory.ssh.enabled }} + - containerPort: {{ .Values.nginx.ssh.internalPort }} + name: tcp-ssh + {{- end }} + {{- with .Values.nginx.lifecycle }} + lifecycle: +{{ toYaml . | indent 10 }} + {{- end }} + volumeMounts: + - name: nginx-conf + mountPath: /etc/nginx/nginx.conf + subPath: nginx.conf + - name: nginx-artifactory-conf + mountPath: "{{ .Values.nginx.persistence.mountPath }}/conf.d/" + - name: nginx-volume + mountPath: {{ .Values.nginx.persistence.mountPath | quote }} + {{- if .Values.nginx.https.enabled }} + - name: ssl-certificates + mountPath: "{{ .Values.nginx.persistence.mountPath }}/ssl" + {{- end }} + {{- if .Values.nginx.customVolumeMounts }} +{{ tpl (include "artifactory.nginx.customVolumeMounts" .) . | indent 8 }} + {{- end }} + resources: +{{ toYaml .Values.nginx.resources | indent 10 }} + {{- if .Values.nginx.startupProbe.enabled }} + startupProbe: +{{ tpl .Values.nginx.startupProbe.config . | indent 10 }} + {{- end }} + {{- if .Values.nginx.readinessProbe.enabled }} + readinessProbe: +{{ tpl .Values.nginx.readinessProbe.config . | indent 10 }} + {{- end }} + {{- if .Values.nginx.livenessProbe.enabled }} + livenessProbe: +{{ tpl .Values.nginx.livenessProbe.config . | indent 10 }} + {{- end }} + {{- $mountPath := .Values.nginx.persistence.mountPath }} + {{- range .Values.nginx.loggers }} + - name: {{ . | replace "_" "-" | replace "." "-" }} + image: {{ include "artifactory-ha.getImageInfoByValue" (list $ "initContainers") }} + imagePullPolicy: {{ $.Values.initContainers.image.pullPolicy }} + command: + - tail + args: + - '-F' + - '{{ $mountPath }}/logs/{{ . }}' + volumeMounts: + - name: nginx-volume + mountPath: {{ $mountPath }} + resources: +{{ toYaml $.Values.nginx.loggersResources | indent 10 }} + {{- end }} + {{- if .Values.nginx.customSidecarContainers }} +{{ tpl (include "artifactory.nginx.customSidecarContainers" .) . | indent 6 }} + {{- end }} + {{- if or .Values.nginx.nodeSelector .Values.global.nodeSelector }} +{{ tpl (include "nginx.nodeSelector" .) . | indent 6 }} + {{- end }} + {{- with .Values.nginx.affinity }} + affinity: +{{ toYaml . | indent 8 }} + {{- end }} + {{- with .Values.nginx.tolerations }} + tolerations: +{{ toYaml . | indent 8 }} + {{- end }} + volumes: + {{- if .Values.nginx.customVolumes }} +{{ tpl (include "artifactory.nginx.customVolumes" .) . | indent 6 }} + {{- end }} + - name: nginx-conf + configMap: + {{- if .Values.nginx.customConfigMap }} + name: {{ .Values.nginx.customConfigMap }} + {{- else }} + name: {{ template "artifactory-ha.fullname" . }}-nginx-conf + {{- end }} + - name: nginx-artifactory-conf + configMap: + {{- if .Values.nginx.customArtifactoryConfigMap }} + name: {{ .Values.nginx.customArtifactoryConfigMap }} + {{- else }} + name: {{ template "artifactory-ha.fullname" . }}-nginx-artifactory-conf + {{- end }} + + - name: nginx-volume + {{- if .Values.nginx.persistence.enabled }} + persistentVolumeClaim: + claimName: {{ .Values.nginx.persistence.existingClaim | default (include "artifactory-ha.nginx.fullname" .) }} + {{- else }} + emptyDir: {} + {{- end }} + {{- if .Values.nginx.https.enabled }} + - name: ssl-certificates + secret: + {{- if .Values.nginx.tlsSecretName }} + secretName: {{ .Values.nginx.tlsSecretName }} + {{- else }} + secretName: {{ template "artifactory-ha.fullname" . }}-nginx-certificate + {{- end }} + {{- end }} +{{- end }} \ No newline at end of file diff --git a/charts/jfrog/artifactory-ha/107.104.10/templates/nginx-pdb.yaml b/charts/jfrog/artifactory-ha/107.104.10/templates/nginx-pdb.yaml new file mode 100644 index 0000000000..0aed993682 --- /dev/null +++ b/charts/jfrog/artifactory-ha/107.104.10/templates/nginx-pdb.yaml @@ -0,0 +1,23 @@ +{{- if .Values.nginx.enabled -}} +{{- if semverCompare " + echo "Copy system.yaml to {{ .Values.rtfs.persistence.mountPath }}/etc"; + mkdir -p {{ .Values.rtfs.persistence.mountPath }}/etc; + {{- if .Values.systemYamlOverride.existingSecret }} + cp -fv /tmp/etc/{{ .Values.systemYamlOverride.dataKey }} {{ .Values.rtfs.persistence.mountPath }}/etc/system.yaml; + {{- else }} + cp -fv /tmp/etc/system.yaml {{ .Values.rtfs.persistence.mountPath }}/etc/system.yaml; + {{- end }} + echo "Copying artifactory-federation properties to {{ .Values.rtfs.persistence.mountPath }}/etc"; + mkdir -p {{ .Values.rtfs.persistence.mountPath }}/etc; + echo "Starting to copy files to artifactory-federation.properties"; + found=false; + for file in /tmp/etc/properties/*; do + if [ -f "$file" ]; then + found=true; + key=$(basename "$file"); + value=$(cat "$file"); + echo "Processing file: $file"; + echo "$key=$value" >> {{ .Values.rtfs.persistence.mountPath }}/etc/artifactory-federation.properties; + fi; + done; + if [ "$found" = false ]; then + echo "No matching files found, creating an empty artifactory-federation.properties file"; + touch {{ .Values.rtfs.persistence.mountPath }}/etc/artifactory-federation.properties; + fi + volumeMounts: + - name: data + mountPath: {{ .Values.rtfs.persistence.mountPath | quote }} + {{- if or (not .Values.artifactory.unifiedSecretInstallation) .Values.systemYamlOverride.existingSecret }} + - name: systemyaml + {{- else }} + - name: {{ include "artifactory-ha.unifiedCustomSecretVolumeName" . }} + {{- end }} + {{- if .Values.systemYamlOverride.existingSecret }} + mountPath: "/tmp/etc/{{.Values.systemYamlOverride.dataKey }}" + subPath: {{ .Values.systemYamlOverride.dataKey }} + {{- else }} + mountPath: "/tmp/etc/system.yaml" + subPath: "system.yaml" + {{- end }} + {{- if or .Values.rtfs.persistence.federationProperties .Values.rtfs.persistence.customFederationPropertiesConfig }} + - name: federationproperties + mountPath: /tmp/etc/properties + {{- end }} + containers: + - name: rtfs + image: {{ include "artifactory-ha.getImageInfoByValue" (list . "rtfs") }} + imagePullPolicy: {{ .Values.initContainers.image.pullPolicy | quote }} + {{- if .Values.rtfs.containerSecurityContext.enabled }} + securityContext: {{- omit .Values.rtfs.containerSecurityContext "enabled" | toYaml | nindent 12 }} + {{- end }} + volumeMounts: + - name: data + mountPath: {{ .Values.rtfs.persistence.mountPath | quote }} + {{- if .Values.rtfs.customVolumeMounts }} +{{ tpl (include "artifactory.rtfs.customVolumeMounts" .) . | indent 12 }} + {{- end }} + env: + - name: JF_SHARED_NODE_ID + valueFrom: + fieldRef: + fieldPath: metadata.name + {{- if .Values.rtfs.javaOpts }} + - name: EXTRA_JAVA_OPTS + value: {{ .Values.rtfs.javaOpts }} + {{- end }} + {{- if or .Values.database.secrets.user .Values.database.user }} + - name: JF_SHARED_DATABASE_USERNAME + valueFrom: + secretKeyRef: + {{- if .Values.database.secrets.user }} + name: {{ tpl .Values.database.secrets.user.name . }} + key: {{ tpl .Values.database.secrets.user.key . }} + {{- else if .Values.database.user }} + {{- if not .Values.artifactory.unifiedSecretInstallation }} + name: {{ template "artifactory-ha.fullname" . }}-database-creds + {{- else }} + name: "{{ template "artifactory.unifiedSecretPrependReleaseName" . }}-unified-secret" + {{- end }} + key: db-user + {{- end }} + {{- end }} + {{ if or .Values.database.secrets.password .Values.database.password .Values.postgresql.enabled }} + - name: JF_SHARED_DATABASE_PASSWORD + valueFrom: + secretKeyRef: + {{- if .Values.database.secrets.password }} + name: {{ tpl .Values.database.secrets.password.name . }} + key: {{ tpl .Values.database.secrets.password.key . }} + {{- else if .Values.database.password }} + {{- if not .Values.artifactory.unifiedSecretInstallation }} + name: {{ template "artifactory-ha.fullname" . }}-database-creds + {{- else }} + name: "{{ template "artifactory.unifiedSecretPrependReleaseName" . }}-unified-secret" + {{- end }} + key: db-password + {{- else if .Values.postgresql.enabled }} + name: {{ .Release.Name }}-postgresql + key: postgresql-password + {{- end }} + {{- end }} + {{- if or .Values.database.secrets.url .Values.database.url }} + - name: JF_SHARED_DATABASE_URL + valueFrom: + secretKeyRef: + {{- if .Values.database.secrets.url }} + name: {{ tpl .Values.database.secrets.url.name . }} + key: {{ tpl .Values.database.secrets.url.key . }} + {{- else if .Values.database.url }} + {{- if not .Values.artifactory.unifiedSecretInstallation }} + name: {{ template "artifactory-ha.fullname" . }}-database-creds + {{- else }} + name: "{{ template "artifactory.unifiedSecretPrependReleaseName" . }}-unified-secret" + {{- end }} + key: db-url + {{- end }} + {{- end }} + {{- if or .Values.artifactory.masterKey .Values.global.masterKey .Values.artifactory.masterKeySecretName .Values.global.masterKeySecretName }} + - name: JF_SHARED_SECURITY_MASTERKEY + valueFrom: + secretKeyRef: + {{- if or (not .Values.artifactory.unifiedSecretInstallation) .Values.artifactory.masterKeySecretName .Values.global.masterKeySecretName }} + name: {{ include "artifactory-ha.masterKeySecretName" . }} + {{- else }} + name: "{{ template "artifactory.unifiedSecretPrependReleaseName" . }}-unified-secret" + {{- end }} + key: master-key + {{- end }} + {{- if or .Values.artifactory.joinKey .Values.global.joinKey .Values.artifactory.joinKeySecretName .Values.global.joinKeySecretName }} + - name: JF_SHARED_SECURITY_JOINKEY + valueFrom: + secretKeyRef: + {{- if or (not .Values.artifactory.unifiedSecretInstallation) .Values.artifactory.joinKeySecretName .Values.global.joinKeySecretName }} + name: {{ include "artifactory-ha.joinKeySecretName" . }} + {{- else }} + name: "{{ template "artifactory.unifiedSecretPrependReleaseName" . }}-unified-secret" + {{- end }} + key: join-key + {{- end }} +{{- with .Values.rtfs.extraEnvironmentVariables }} +{{ tpl (toYaml .) $ | indent 12 }} +{{- end }} + resources: {{- toYaml .Values.rtfs.resources | nindent 12 }} + {{- if .Values.rtfs.startupProbe.enabled }} + startupProbe: +{{ tpl .Values.rtfs.startupProbe.config . | indent 12 }} + {{- end }} + {{- if .Values.rtfs.livenessProbe.enabled }} + livenessProbe: +{{ tpl .Values.rtfs.livenessProbe.config . | indent 12 }} + {{- end }} + {{- if .Values.rtfs.readinessProbe.enabled }} + readinessProbe: +{{ tpl .Values.rtfs.readinessProbe.config . | indent 12 }} + {{- end }} + {{- with .Values.rtfs.lifecycle }} + lifecycle: +{{ toYaml . | indent 12 }} + {{- end }} + - name: router + image: {{ include "artifactory-ha.getImageInfoByValue" (list . "router") }} + imagePullPolicy: {{ .Values.router.image.imagePullPolicy }} + resources: {{- toYaml .Values.router.resources | nindent 12 }} + {{- if .Values.rtfs.containerSecurityContext.enabled }} + securityContext: {{- omit .Values.rtfs.containerSecurityContext "enabled" | toYaml | nindent 12 }} + {{- end }} + command: + - '/bin/bash' + - '-c' + - > + exec /opt/jfrog/router/app/bin/entrypoint-router.sh + env: + - name: JF_ROUTER_TOPOLOGY_LOCAL_REQUIREDSERVICETYPES + value: "jfrtfs" + - name: JF_SHARED_JFROGURL + value: {{ tpl (include "rtfs.jfrogUrl" .) . }} + {{- if or .Values.artifactory.masterKey .Values.global.masterKey .Values.artifactory.masterKeySecretName .Values.global.masterKeySecretName }} + - name: JF_SHARED_SECURITY_MASTERKEY + valueFrom: + secretKeyRef: + {{- if or (not .Values.artifactory.unifiedSecretInstallation) .Values.artifactory.masterKeySecretName .Values.global.masterKeySecretName }} + name: {{ include "artifactory-ha.masterKeySecretName" . }} + {{- else }} + name: "{{ template "artifactory.unifiedSecretPrependReleaseName" . }}-unified-secret" + {{- end }} + key: master-key + {{- end }} + {{- if or .Values.artifactory.joinKey .Values.global.joinKey .Values.artifactory.joinKeySecretName .Values.global.joinKeySecretName }} + - name: JF_SHARED_SECURITY_JOINKEY + valueFrom: + secretKeyRef: + {{- if or (not .Values.artifactory.unifiedSecretInstallation) .Values.artifactory.joinKeySecretName .Values.global.joinKeySecretName }} + name: {{ include "artifactory-ha.joinKeySecretName" . }} + {{- else }} + name: "{{ template "artifactory.unifiedSecretPrependReleaseName" . }}-unified-secret" + {{- end }} + key: join-key + {{- end }} +{{- with .Values.router.extraEnvironmentVariables }} +{{ tpl (toYaml .) $ | indent 12 }} +{{- end }} + ports: + - name: http-router + containerPort: {{ .Values.router.internalPort }} + volumeMounts: + - name: data + mountPath: {{ .Values.router.persistence.mountPath | quote }} + {{- if .Values.router.extraVolumeMounts }} + {{- toYaml .Values.router.extraVolumeMounts | nindent 12 }} + {{- end }} + {{- if .Values.router.startupProbe.enabled }} + startupProbe: +{{ tpl .Values.router.startupProbe.config . | indent 12 }} + {{- end }} + {{- if .Values.router.readinessProbe.enabled }} + readinessProbe: +{{ tpl .Values.router.readinessProbe.config . | indent 12 }} + {{- end }} + {{- if .Values.router.livenessProbe.enabled }} + livenessProbe: +{{ tpl .Values.router.livenessProbe.config . | indent 12 }} + {{- end }} + {{- with .Values.router.lifecycle }} + lifecycle: +{{ toYaml . | indent 12 }} + {{- end }} + {{- if .Values.rtfs.customSidecarContainers }} +{{ tpl (include "artifactory.rtfs.customSidecarContainers" .) . | indent 8 }} + {{- end }} + volumes: + # system yaml + {{- if .Values.systemYamlOverride.existingSecret }} + - name: systemyaml + secret: + secretName: {{ .Values.systemYamlOverride.existingSecret }} + {{- end }} + + ######### unifiedSecretInstallation ########### + {{- if and .Values.artifactory.unifiedSecretInstallation (eq (include "artifactory-ha.checkDuplicateUnifiedCustomVolume" .) "false" ) }} + - name: {{ include "artifactory-ha.unifiedCustomSecretVolumeName" . }} + secret: + secretName: "{{ template "artifactory.unifiedSecretPrependReleaseName" . }}-unified-secret" + {{- else if not .Values.artifactory.unifiedSecretInstallation }} + {{- if not .Values.systemYamlOverride.existingSecret }} + - name: systemyaml + secret: + secretName: {{ template "artifactory-ha.primary.name" . }}-system-yaml + {{- end }} + {{- end }} + {{- if .Values.rtfs.customVolumes }} +{{ tpl (include "artifactory.rtfs.customVolumes" .) . | indent 8 }} + {{- end }} + - name: data + emptyDir: {} + {{- if and (.Values.rtfs.persistence.federationProperties) (not .Values.rtfs.persistence.customFederationPropertiesConfig) }} + - name: federationproperties + configMap: + name: {{ include "rtfs.fullname" . }}-properties + {{- end }} + {{- if .Values.rtfs.persistence.customFederationPropertiesConfig }} + - name: federationproperties + configMap: + name: {{ .Values.rtfs.persistence.customFederationPropertiesConfig }} + {{- end }} + {{- if .Values.rtfs.extraVolumes }} + {{- toYaml .Values.rtfs.extraVolumes | nindent 8 }} + {{- end }} + {{- with .Values.rtfs.nodeSelector }} + nodeSelector: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.rtfs.affinity }} + affinity: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.rtfs.tolerations }} + tolerations: + {{- toYaml . | nindent 8 }} + {{- end }} +{{- end }} \ No newline at end of file diff --git a/charts/jfrog/artifactory-ha/107.104.10/templates/rtfs-hpa.yaml b/charts/jfrog/artifactory-ha/107.104.10/templates/rtfs-hpa.yaml new file mode 100644 index 0000000000..a8514c5a2d --- /dev/null +++ b/charts/jfrog/artifactory-ha/107.104.10/templates/rtfs-hpa.yaml @@ -0,0 +1,32 @@ +{{- if .Values.rtfs.autoscaling.enabled }} + {{- if semverCompare ">=v1.23.0-0" .Capabilities.KubeVersion.Version }} +apiVersion: autoscaling/v2 + {{- else }} +apiVersion: autoscaling/v2beta2 + {{- end }} +kind: HorizontalPodAutoscaler +metadata: + labels: + app: {{ template "artifactory-ha.name" . }} + chart: {{ template "artifactory-ha.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + name: {{ template "rtfs.fullname" . }} +spec: + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: {{ template "rtfs.fullname" . }} + minReplicas: {{ .Values.rtfs.autoscaling.minReplicas }} + maxReplicas: {{ .Values.rtfs.autoscaling.maxReplicas }} + metrics: + - type: Resource + resource: + name: cpu + target: + type: Utilization + averageUtilization: {{ .Values.rtfs.autoscaling.targetCPUUtilizationPercentage }} + {{- if .Values.rtfs.autoscaling.metrics }} +{{ tpl (include "rtfs.metrics" .) . | indent 4 }} + {{- end }} +{{- end }} \ No newline at end of file diff --git a/charts/jfrog/artifactory-ha/107.104.10/templates/rtfs-properties-configmap.yaml b/charts/jfrog/artifactory-ha/107.104.10/templates/rtfs-properties-configmap.yaml new file mode 100644 index 0000000000..8cad47550a --- /dev/null +++ b/charts/jfrog/artifactory-ha/107.104.10/templates/rtfs-properties-configmap.yaml @@ -0,0 +1,17 @@ +{{- if and (.Values.rtfs.persistence.federationProperties) (not .Values.rtfs.persistence.customFederationPropertiesConfig) .Values.rtfs.enabled }} +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ include "rtfs.fullname" . }}-properties + namespace: {{ .Release.Namespace | quote }} + labels: + app: {{ template "artifactory-ha.name" . }} + chart: {{ template "artifactory-ha.chart" . }} + component: {{ .Values.rtfs.name }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} +data: + {{- range $key, $value := .Values.rtfs.persistence.federationProperties }} + {{ $key }}: {{ tpl ($value | default "") . | quote }} + {{- end }} + {{- end }} \ No newline at end of file diff --git a/charts/jfrog/artifactory-ha/107.104.10/templates/rtfs-service.yaml b/charts/jfrog/artifactory-ha/107.104.10/templates/rtfs-service.yaml new file mode 100644 index 0000000000..684c204401 --- /dev/null +++ b/charts/jfrog/artifactory-ha/107.104.10/templates/rtfs-service.yaml @@ -0,0 +1,41 @@ +{{- if .Values.rtfs.enabled -}} +apiVersion: v1 +kind: Service +metadata: + name: {{ include "rtfs.fullname" . }} + labels: + app: {{ template "artifactory-ha.name" . }} + chart: {{ template "artifactory-ha.chart" . }} + component: {{ .Values.rtfs.name }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} +{{- if .Values.rtfs.service.labels }} +{{ toYaml .Values.rtfs.service.labels | indent 4 }} +{{- end }} +{{- with .Values.rtfs.service.annotations }} + annotations: +{{ toYaml . | indent 4 }} +{{- end }} +spec: + type: {{ .Values.rtfs.service.type }} + {{- if eq .Values.rtfs.service.type "LoadBalancer" }} + {{- if not (empty .Values.rtfs.service.loadBalancerIP) }} + loadBalancerIP: {{ .Values.rtfs.service.loadBalancerIP }} + {{- end }} + {{- if .Values.rtfs.service.loadBalancerSourceRanges }} + loadBalancerSourceRanges: {{- toYaml .Values.rtfs.service.loadBalancerSourceRanges | nindent 4 }} + {{- end }} + {{- end }} + {{- if (or (eq .Values.rtfs.service.type "LoadBalancer") (eq .Values.rtfs.service.type "NodePort")) }} + externalTrafficPolicy: {{ .Values.rtfs.service.externalTrafficPolicy | quote }} + {{- end }} + ports: + - port: {{ .Values.router.externalPort }} + targetPort: {{ .Values.router.internalPort }} + protocol: TCP + name: http-router + selector: + app: {{ template "artifactory-ha.name" . }} + release: {{ .Release.Name }} + component: {{ .Values.rtfs.name }} +{{- end }} \ No newline at end of file diff --git a/charts/jfrog/artifactory-ha/107.104.10/values.yaml b/charts/jfrog/artifactory-ha/107.104.10/values.yaml new file mode 100644 index 0000000000..d7de2e7238 --- /dev/null +++ b/charts/jfrog/artifactory-ha/107.104.10/values.yaml @@ -0,0 +1,2133 @@ +## Default values for artifactory-ha. +## This is a YAML-formatted file. +## Beware when changing values here. You should know what you are doing! +## Access the values with {{ .Values.key.subkey }} +global: + # imageRegistry: releases-docker.jfrog.io + # imagePullSecrets: + # - myRegistryKeySecretName + ## Chart.AppVersion can be overidden using global.versions.artifactory or .Values.artifactory.image.tag + ## Note: Order of preference is 1) global.versions 2) .Values.artifactory.image.tag 3) Chart.AppVersion + ## This applies also for nginx images (.Values.nginx.image.tag) + versions: {} + # artifactory: + # initContainers: + # rtfs: + # joinKey: + # masterKey: + # joinKeySecretName: + # masterKeySecretName: + + ## Note: tags customInitContainersBegin,customInitContainers,customVolumes,customVolumeMounts,customSidecarContainers can be used both from global and application level simultaneously + # customInitContainersBegin: | + + # customInitContainers: | + + # customVolumes: | + + # customVolumeMounts: | + + # customSidecarContainers: | + + ## certificates added to this secret will be copied to $JFROG_HOME/artifactory/var/etc/security/keys/trusted directory + customCertificates: + enabled: false + # certificateSecretName: + ## Applies to artifactory and nginx pods + nodeSelector: {} +## String to partially override artifactory-ha.fullname template (will maintain the release name) +# nameOverride: + +## String to fully override artifactory-ha.fullname template +# fullnameOverride: + +# Init containers +initContainers: + image: + registry: releases-docker.jfrog.io + repository: ubi9/ubi-minimal + tag: 9.5.1733767867 + pullPolicy: IfNotPresent + resources: + requests: + memory: "50Mi" + cpu: "10m" + limits: + memory: "1Gi" + cpu: "1" +installer: + type: + platform: +## The installerInfo is intentionally commented out and the previous content has been moved under `files/installer-info.json` +## To override the content in `files/installer-info.json`, Uncomment the `installerInfo` and add relevant data +# installerInfo: '{}' + +# For supporting pulling from private registries +# imagePullSecrets: +# - myRegistryKeySecretName + +## Artifactory systemYaml override +## This is for advanced usecases where users wants to provide their own systemYaml for configuring artifactory +## Refer: https://www.jfrog.com/confluence/display/JFROG/Artifactory+System+YAML +## Note: This will override existing (default) .Values.artifactory.systemYaml in values.yaml +## Alternatively, systemYaml can be overidden via customInitContainers using external sources like vaults, external repositories etc. Please refer customInitContainer section below for an example. +## Note: Order of preference is 1) customInitContainers 2) systemYamlOverride existingSecret 3) default systemYaml in values.yaml +systemYamlOverride: + ## You can use a pre-existing secret by specifying existingSecret + existingSecret: + ## The dataKey should be the name of the secret data key created. + dataKey: +## Role Based Access Control +## Ref: https://kubernetes.io/docs/admin/authorization/rbac/ +rbac: + create: false + role: + ## Rules to create. It follows the role specification + rules: + - apiGroups: + - '' + resources: + - services + - endpoints + - pods + verbs: + - get + - watch + - list +## Service Account +## Ref: https://kubernetes.io/docs/admin/service-accounts-admin/ +## +serviceAccount: + create: false + ## The name of the ServiceAccount to use. + ## If not set and create is true, a name is generated using the fullname template + name: + annotations: {} + ## Explicitly mounts the API token for the service account to the pods + automountServiceAccountToken: false +ingress: + enabled: false + defaultBackend: + enabled: true + ## Used to create an Ingress record. + hosts: [] + ## Useful in controllers where ImplementationSpecific is considered as a Regex Match and, not a Prefix match + pathType: ImplementationSpecific + routerPath: / + artifactoryPath: /artifactory/ + rtfsPath: /artifactory/service/rtfs/ + className: "" + annotations: {} + # kubernetes.io/tls-acme: "true" + # nginx.ingress.kubernetes.io/proxy-body-size: "0" + labels: {} + # traffic-type: external + # traffic-type: internal + tls: [] + ## Secrets must be manually created in the namespace. + # - secretName: chart-example-tls + # hosts: + # - artifactory.domain.example + + ## Additional ingress rules + additionalRules: [] + ## This is an experimental feature, enabling this feature will route all traffic through the Router. + disableRouterBypass: false +## Allows to add custom ingress +customIngress: "" +## There is a need to separate HTTP1.1 traffic from gRPC (HTTP2) to benefit +## 1. Use the Nginx keepalive for HTTP1.1 +## 2. Keep gRPC over the HTTP2 connections only +ingressGrpc: + enabled: false + name: grpc + defaultBackend: + enabled: true + ## Used to create an Ingress record. + hosts: [] + ## Useful in controllers where ImplementationSpecific is considered as a Regex Match and, not a Prefix match + pathType: ImplementationSpecific + grpcPath: /com.jfrog + className: "" + ## For supporting GRPC protocol traffic sent to the backend it is necessary to change the backend protocol + ## Example: nginx.ingress.kubernetes.io/backend-protocol: GRPCS + annotations: {} + # kubernetes.io/tls-acme: "true" + # nginx.ingress.kubernetes.io/proxy-body-size: "0" + labels: {} + # traffic-type: external + # traffic-type: internal + tls: [] + ## Secrets must be manually created in the namespace. + # - secretName: chart-example-tls + # hosts: + # - artifactory.domain.example + + ## Additional ingress rules + additionalRules: [] +networkpolicy: [] +# Allows all ingress and egress +# - name: artifactory +# podSelector: +# matchLabels: +# app: artifactory-ha +# egress: +# - {} +# ingress: +# - {} +## Uncomment to allow only artifactory pods to communicate with postgresql (if postgresql.enabled is true) +# - name: postgresql +# podSelector: +# matchLabels: +# app: postgresql +# ingress: +# - from: +# - podSelector: +# matchLabels: +# app: artifactory-ha + +## Database configurations +## Use the wait-for-db init container. Set to false to skip +waitForDatabase: true +## Configuration values for the postgresql dependency +## ref: https://github.com/bitnami/charts/blob/master/bitnami/postgresql/README.md +## +postgresql: + enabled: true + image: + registry: releases-docker.jfrog.io + repository: bitnami/postgresql + tag: 15.6.0-debian-11-r16 + postgresqlUsername: artifactory + postgresqlPassword: "" + postgresqlDatabase: artifactory + postgresqlExtendedConf: + listenAddresses: "*" + maxConnections: "1500" + persistence: + enabled: true + size: 200Gi + # existingClaim: + service: + port: 5432 + primary: + nodeSelector: {} + affinity: {} + tolerations: [] + readReplicas: + nodeSelector: {} + affinity: {} + tolerations: [] + resources: {} + securityContext: + enabled: true + containerSecurityContext: + enabled: true + # requests: + # memory: "512Mi" + # cpu: "100m" + # limits: + # memory: "1Gi" + # cpu: "500m" +## If NOT using the PostgreSQL in this chart (postgresql.enabled=false), +## you MUST specify custom database details here or Artifactory will NOT start +database: + ## To run Artifactory with any database other than PostgreSQL allowNonPostgresql set to true. + allowNonPostgresql: false + type: + driver: + ## If you set the url, leave host and port empty + url: + ## If you would like this chart to create the secret containing the db + ## password, use these values + user: + password: + ## If you have existing Kubernetes secrets containing db credentials, use + ## these values + secrets: {} + # user: + # name: "rds-artifactory" + # key: "db-user" + # password: + # name: "rds-artifactory" + # key: "db-password" + # url: + # name: "rds-artifactory" + # key: "db-url" +## You can use a pre-existing secret with keys license_token and iam_role by specifying licenseConfigSecretName +## Example : Create a generic secret using `kubectl create secret generic --from-literal=license_token=${TOKEN} --from-literal=iam_role=${ROLE_ARN}` +aws: + license: + enabled: false + licenseConfigSecretName: + region: us-east-1 +## Container Security Context +## ref: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/#set-the-security-context-for-a-container +## @param containerSecurityContext.enabled Enabled containers' Security Context +## @param containerSecurityContext.runAsNonRoot Set container's Security Context runAsNonRoot +## @param containerSecurityContext.privileged Set container's Security Context privileged +## @param containerSecurityContext.allowPrivilegeEscalation Set container's Security Context allowPrivilegeEscalation +## @param containerSecurityContext.capabilities.drop List of capabilities to be dropped +## @param containerSecurityContext.seccompProfile.type Set container's Security Context seccomp profile +## +containerSecurityContext: + enabled: true + runAsNonRoot: true + privileged: false + allowPrivilegeEscalation: false + seccompProfile: + type: RuntimeDefault + capabilities: + drop: + - ALL +## The following router settings are to configure only when splitServicesToContainers set to true +router: + name: router + image: + registry: releases-docker.jfrog.io + repository: jfrog/router + tag: 7.149.1 + pullPolicy: IfNotPresent + serviceRegistry: + ## Service registry (Access) TLS verification skipped if enabled + insecure: false + internalPort: 8082 + externalPort: 8082 + tlsEnabled: false + ## Extra environment variables that can be used to tune router to your needs. + ## Uncomment and set value as needed + extraEnvironmentVariables: + # - name: MY_ENV_VAR + # value: "" + resources: {} + # requests: + # memory: "100Mi" + # cpu: "100m" + # limits: + # memory: "1Gi" + # cpu: "1" + + ## Add lifecycle hooks for router container + lifecycle: + ## From Artifactory versions 7.52.x, Wait for Artifactory to complete any open uploads or downloads before terminating + preStop: + exec: + command: ["sh", "-c", "while [[ $(curl --fail --silent --connect-timeout 2 http://localhost:8081/artifactory/api/v1/system/liveness) =~ OK ]]; do echo Artifactory is still alive; sleep 2; done"] + # postStart: + # exec: + # command: ["/bin/sh", "-c", "echo Hello from the postStart handler"] + ## Add custom volumesMounts + customVolumeMounts: | + # - name: custom-script + # mountPath: /scripts/script.sh + # subPath: script.sh + livenessProbe: + enabled: true + config: | + exec: + command: + - sh + - -c + - curl -s -k --fail --max-time {{ .Values.probes.timeoutSeconds }} {{ include "artifactory-ha.scheme" . }}://localhost:{{ .Values.router.internalPort }}/router/api/v1/system/liveness + initialDelaySeconds: {{ if semverCompare " prepended. + unifiedSecretPrependReleaseName: true + image: + registry: releases-docker.jfrog.io + repository: jfrog/artifactory-pro + # tag: + pullPolicy: IfNotPresent + ## ref: https://kubernetes.io/docs/tasks/administer-cluster/configure-multiple-schedulers/ + schedulerName: + ## Create a priority class for the Artifactory pods or use an existing one + ## NOTE - Maximum allowed value of a user defined priority is 1000000000 + priorityClass: + create: false + value: 1000000000 + ## Override default name + # name: + ## Use an existing priority class + # existingPriorityClass: + ## Delete the db.properties file in ARTIFACTORY_HOME/etc/db.properties + deleteDBPropertiesOnStartup: true + database: + maxOpenConnections: 80 + tomcat: + maintenanceConnector: + port: 8091 + connector: + maxThreads: 200 + sendReasonPhrase: false + extraConfig: 'acceptCount="400"' + ## certificates added to this secret will be copied to $JFROG_HOME/artifactory/var/etc/security/keys/trusted directory + customCertificates: + enabled: false + # certificateSecretName: + ## Support for metrics is only available for Artifactory 7.7.x (appVersions) and above. + ## To enable set `.Values.artifactory.metrics.enabled` to `true` + ## Note: Depricated `openMetrics` as part of 7.87.x and renamed to `metrics` + ## Refer - https://www.jfrog.com/confluence/display/JFROG/Open+Metrics + metrics: + enabled: false + ## Settings for pushing metrics to Insight - enable filebeat to true + filebeat: + enabled: false + log: + enabled: false + ## Log level for filebeat. Possible values: debug, info, warning, or error. + level: "info" + ## Elasticsearch details for filebeat to connect + elasticsearch: + url: "Elasticsearch url where JFrog Insight is installed For example, http://:8082" + username: "" + password: "" + ## Support for Cold Artifact Storage + ## set 'coldStorage.enabled' to 'true' only for Artifactory instance that you are designating as the Cold instance + ## Refer - https://jfrog.com/help/r/jfrog-platform-administration-documentation/setting-up-cold-artifact-storage + coldStorage: + enabled: false + ## Support for workers + ## set 'worker.enabled' to 'true' to enable artifactory addon that executes workers on artifactory events + worker: + enabled: false + ## This directory is intended for use with NFS eventual configuration for HA + ## When enabling this section, The system.yaml will include haDataDir section. + ## The location of Artifactory Data directory and Artifactory Filestore will be modified accordingly and will be shared among all nodes. + ## It's recommended to leave haDataDir disabled, and the default BinarystoreXml will set the Filestore location as configured in artifactory.persistence.nfs.dataDir. + haDataDir: + enabled: false + path: + haBackupDir: + enabled: false + path: + ## Files to copy to ARTIFACTORY_HOME/ on each Artifactory startup + ## Note : From 107.46.x chart versions, copyOnEveryStartup is not needed for binarystore.xml, it is always copied via initContainers + copyOnEveryStartup: + ## Absolute path + # - source: /artifactory_bootstrap/artifactory.cluster.license + ## Relative to ARTIFACTORY_HOME/ + # target: etc/artifactory/ + + ## Sidecar containers for tailing Artifactory logs + loggers: [] + # - access-audit.log + # - access-request.log + # - access-security-audit.log + # - access-service.log + # - artifactory-access.log + # - artifactory-event.log + # - artifactory-import-export.log + # - artifactory-request.log + # - artifactory-service.log + # - frontend-request.log + # - frontend-service.log + # - metadata-request.log + # - metadata-service.log + # - router-request.log + # - router-service.log + # - router-traefik.log + # - derby.log + + ## Loggers containers resources + loggersResources: {} + # requests: + # memory: "10Mi" + # cpu: "10m" + # limits: + # memory: "100Mi" + # cpu: "50m" + + ## Sidecar containers for tailing Tomcat (catalina) logs + catalinaLoggers: [] + # - tomcat-catalina.log + # - tomcat-localhost.log + + ## Tomcat (catalina) loggers resources + catalinaLoggersResources: {} + # requests: + # memory: "10Mi" + # cpu: "10m" + # limits: + # memory: "100Mi" + # cpu: "50m" + + ## Migration support from 6.x to 7.x. + migration: + enabled: false + timeoutSeconds: 3600 + ## Extra pre-start command in migration Init Container to install JDBC driver for MySql/MariaDb/Oracle + # preStartCommand: "mkdir -p /opt/jfrog/artifactory/var/bootstrap/artifactory/tomcat/lib; cd /opt/jfrog/artifactory/var/bootstrap/artifactory/tomcat/lib && curl -o /opt/jfrog/artifactory/var/bootstrap/artifactory/tomcat/lib/mysql-connector-java-5.1.41.jar https://repo1.maven.org/maven2/mysql/mysql-connector-java/5.1.41/mysql-connector-java-5.1.41.jar" + ## Add custom init containers execution before predefined init containers + customInitContainersBegin: | + # - name: "custom-setup" + # image: {{ include "artifactory-ha.getImageInfoByValue" (list . "initContainers") }} + # imagePullPolicy: {{ .Values.initContainers.image.pullPolicy }} + # securityContext: + # runAsNonRoot: true + # allowPrivilegeEscalation: false + # capabilities: + # drop: + # - NET_RAW + # command: + # - 'sh' + # - '-c' + # - 'touch {{ .Values.artifactory.persistence.mountPath }}/example-custom-setup' + # volumeMounts: + # - mountPath: "{{ .Values.artifactory.persistence.mountPath }}" + # name: volume + ## Add custom init containers + ## Add custom init containers execution after predefined init containers + customInitContainers: | + # - name: "custom-systemyaml-setup" + # image: {{ include "artifactory-ha.getImageInfoByValue" (list . "initContainers") }} + # imagePullPolicy: {{ .Values.initContainers.image.pullPolicy }} + # securityContext: + # runAsNonRoot: true + # allowPrivilegeEscalation: false + # capabilities: + # drop: + # - NET_RAW + # command: + # - 'sh' + # - '-c' + # - 'curl -o {{ .Values.artifactory.persistence.mountPath }}/etc/system.yaml https:///systemyaml' + # volumeMounts: + # - mountPath: "{{ .Values.artifactory.persistence.mountPath }}" + # name: volume + ## Add custom sidecar containers + ## - The provided example uses a custom volume (customVolumes) + ## - The provided example shows running container as root (id 0) + customSidecarContainers: | + # - name: "sidecar-list-etc" + # image: {{ include "artifactory-ha.getImageInfoByValue" (list . "initContainers") }} + # imagePullPolicy: {{ .Values.initContainers.image.pullPolicy }} + # securityContext: + # runAsNonRoot: true + # allowPrivilegeEscalation: false + # capabilities: + # drop: + # - NET_RAW + # command: + # - 'sh' + # - '-c' + # - 'sh /scripts/script.sh' + # volumeMounts: + # - mountPath: "{{ .Values.artifactory.persistence.mountPath }}" + # name: volume + # - mountPath: "/scripts/script.sh" + # name: custom-script + # subPath: script.sh + # resources: + # requests: + # memory: "32Mi" + # cpu: "50m" + # limits: + # memory: "128Mi" + # cpu: "100m" + ## Add custom volumes + ## If .Values.artifactory.unifiedSecretInstallation is true then secret name should be '{{ template "artifactory.unifiedSecretPrependReleaseName" . }}-unified-secret'. + customVolumes: | + # - name: custom-script + # configMap: + # name: custom-script + ## Add custom volumesMounts + customVolumeMounts: | + # - name: custom-script + # mountPath: "/scripts/script.sh" + # subPath: script.sh + # - name: posthook-start + # mountPath: "/scripts/posthoook-start.sh" + # subPath: posthoook-start.sh + # - name: prehook-start + # mountPath: "/scripts/prehook-start.sh" + # subPath: prehook-start.sh + ## Add custom persistent volume mounts - Available to the entire namespace + customPersistentVolumeClaim: {} + # name: + # mountPath: + # accessModes: + # - "-" + # size: + # storageClassName: + + ## Artifactory HA requires a unique master key. Each Artifactory node must have the same master key! + ## You can generate one with the command: "openssl rand -hex 32" + ## Pass it to helm with '--set artifactory.masterKey=${MASTER_KEY}' + ## Alternatively, you can use a pre-existing secret with a key called master-key by specifying masterKeySecretName + ## IMPORTANT: You should NOT use the example masterKey for a production deployment! + ## IMPORTANT: This is a mandatory for fresh Install of 7.x (App version) + # masterKey: FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + # masterKeySecretName: + + ## Join Key to connect to other services to Artifactory. + ## IMPORTANT: Setting this value overrides the existing joinKey + ## IMPORTANT: You should NOT use the example joinKey for a production deployment! + # joinKey: EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE + ## Alternatively, you can use a pre-existing secret with a key called join-key by specifying joinKeySecretName + # joinKeySecretName: + + ## Registration Token for JFConnect + # jfConnectToken: + ## Alternatively, you can use a pre-existing secret with a key called jfconnect-token by specifying jfConnectTokenSecretName + # jfConnectTokenSecretName: + + ## Add custom secrets - secret per file + ## If .Values.artifactory.unifiedSecretInstallation is true then secret name should be '{{ template "artifactory.unifiedSecretPrependReleaseName" . }}-unified-secret' common to all secrets + customSecrets: + # - name: custom-secret + # key: custom-secret.yaml + # data: > + # custom_secret_config: + # parameter1: value1 + # parameter2: value2 + # - name: custom-secret2 + # key: custom-secret2.config + # data: | + # here the custom secret 2 config + + ## If false, all service console logs will not redirect to a common console.log + consoleLog: false + ## admin allows to set the password for the default admin user. + ## See: https://www.jfrog.com/confluence/display/JFROG/Users+and+Groups#UsersandGroups-RecreatingtheDefaultAdminUserrecreate + admin: + ip: "127.0.0.1" + username: "admin" + password: + secret: + dataKey: + ## Artifactory license. + license: + ## licenseKey is the license key in plain text. Use either this or the license.secret setting + licenseKey: + ## If artifactory.license.secret is passed, it will be mounted as + ## ARTIFACTORY_HOME/etc/artifactory.cluster.license and loaded at run time. + secret: + ## The dataKey should be the name of the secret data key created. + dataKey: + ## Create configMap with artifactory.config.import.xml and security.import.xml and pass name of configMap in following parameter + configMapName: + ## Add any list of configmaps to Artifactory + configMaps: | + # posthook-start.sh: |- + # echo "This is a post start script" + # posthook-end.sh: |- + # echo "This is a post end script" + ## List of secrets for Artifactory user plugins. + ## One Secret per plugin's files. + userPluginSecrets: + # - archive-old-artifacts + # - build-cleanup + # - webhook + # - '{{ template "my-chart.fullname" . }}' + + ## Extra pre-start command to install JDBC driver for MySql/MariaDb/Oracle + # preStartCommand: "mkdir -p /opt/jfrog/artifactory/var/bootstrap/artifactory/tomcat/lib; cd /opt/jfrog/artifactory/var/bootstrap/artifactory/tomcat/lib && curl -o /opt/jfrog/artifactory/var/bootstrap/artifactory/tomcat/lib/mysql-connector-java-5.1.41.jar https://repo1.maven.org/maven2/mysql/mysql-connector-java/5.1.41/mysql-connector-java-5.1.41.jar" + + ## Add lifecycle hooks for artifactory container + lifecycle: {} + # postStart: + # exec: + # command: ["/bin/sh", "-c", "echo Hello from the postStart handler"] + # preStop: + # exec: + # command: ["/bin/sh","-c","echo Hello from the preStop handler"] + + ## Extra environment variables that can be used to tune Artifactory to your needs. + ## Uncomment and set value as needed + extraEnvironmentVariables: + # - name: SERVER_XML_ARTIFACTORY_PORT + # value: "8081" + # - name: SERVER_XML_ARTIFACTORY_MAX_THREADS + # value: "200" + # - name: SERVER_XML_ACCESS_MAX_THREADS + # value: "50" + # - name: SERVER_XML_ARTIFACTORY_EXTRA_CONFIG + # value: "" + # - name: SERVER_XML_ACCESS_EXTRA_CONFIG + # value: "" + # - name: SERVER_XML_EXTRA_CONNECTOR + # value: "" + # - name: DB_POOL_MAX_ACTIVE + # value: "100" + # - name: DB_POOL_MAX_IDLE + # value: "10" + # - name: MY_SECRET_ENV_VAR + # valueFrom: + # secretKeyRef: + # name: my-secret-name + # key: my-secret-key + + ## System YAML entries now reside under files/system.yaml. + ## You can provide the specific values that you want to add or override under 'artifactory.extraSystemYaml'. + ## For example: + ## extraSystemYaml: + ## shared: + ## node: + ## id: my-instance + ## The entries provided under 'artifactory.extraSystemYaml' are merged with files/system.yaml to create the final system.yaml. + ## If you have already provided system.yaml under, 'artifactory.systemYaml', the values in that entry take precedence over files/system.yaml + ## You can modify specific entries with your own value under `artifactory.extraSystemYaml`, The values under extraSystemYaml overrides the values under 'artifactory.systemYaml' and files/system.yaml + extraSystemYaml: {} + ## systemYaml is intentionally commented and the previous content has been moved under files/system.yaml. + ## You have to add the all entries of the system.yaml file here, and it overrides the values in files/system.yaml. + # systemYaml: + + ## IMPORTANT: If overriding artifactory.internalPort: + ## DO NOT use port lower than 1024 as Artifactory runs as non-root and cannot bind to ports lower than 1024! + externalPort: 8082 + internalPort: 8082 + externalArtifactoryPort: 8081 + internalArtifactoryPort: 8081 + terminationGracePeriodSeconds: 30 + ## Pod Security Context + ## ref: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/ + ## @param artifactory.podSecurityContext.enabled Enable security context + ## @param artifactory.podSecurityContext.runAsNonRoot Set pod's Security Context runAsNonRoot + ## @param artifactory.podSecurityContext.runAsUser User ID for the pod + ## @param artifactory.podSecurityContext.runASGroup Group ID for the pod + ## @param artifactory.podSecurityContext.fsGroup Group ID for the pod + ## + podSecurityContext: + enabled: true + runAsNonRoot: true + runAsUser: 1030 + runAsGroup: 1030 + fsGroup: 1030 + # fsGroupChangePolicy: "Always" + # seLinuxOptions: {} + ## The following settings are to configure the frequency of the liveness and startup probes. + livenessProbe: + enabled: true + config: | + exec: + command: + - sh + - -c + - curl -s -k --fail --max-time {{ .Values.probes.timeoutSeconds }} http://localhost:{{ .Values.artifactory.tomcat.maintenanceConnector.port }}/artifactory/api/v1/system/liveness + initialDelaySeconds: {{ if semverCompare " + ## If set to "-", storageClassName: "", which disables dynamic provisioning + ## If undefined (the default) or set to null, no storageClassName spec is + ## set, choosing the default provisioner. (gp2 on AWS, standard on + ## GKE, AWS & OpenStack) + ## + # storageClassName: "-" + + ## Set the persistence storage type. This will apply the matching binarystore.xml to Artifactory config + ## Supported types are: + ## file-system (default) + ## nfs + ## google-storage + ## google-storage-v2 + ## google-storage-v2-direct (Recommended for GCS - Google Cloud Storage) + ## aws-s3-v3 + ## s3-storage-v3-direct (Recommended for AWS S3) + ## s3-storage-v3-archive + ## azure-blob + ## azure-blob-storage-direct + ## azure-blob-storage-v2-direct (Recommended for Azure Blob Storage) + type: file-system + ## Use binarystoreXml to provide a custom binarystore.xml + ## This is intentionally commented and below previous content of binarystoreXml is moved under files/binarystore.xml + ## binarystoreXml: + + ## For artifactory.persistence.type file-system + fileSystem: + ## Need to have the following set + existingSharedClaim: + enabled: false + numberOfExistingClaims: 1 + ## Should be a child directory of {{ .Values.artifactory.persistence.mountPath }} + dataDir: "{{ .Values.artifactory.persistence.mountPath }}/artifactory-data" + backupDir: "/var/opt/jfrog/artifactory-backup" + ## You may also use existing shared claims for the data and backup storage. This allows storage (NAS for example) to be used for Data and Backup dirs which are safe to share across multiple artifactory nodes. + ## You may specify numberOfExistingClaims to indicate how many of these existing shared claims to mount. (Default = 1) + ## Create PVCs with ReadWriteMany that match the naming convetions: + ## {{ template "artifactory-ha.fullname" . }}-data-pvc- + ## {{ template "artifactory-ha.fullname" . }}-backup-pvc + ## Example (using numberOfExistingClaims: 2) + ## myexample-data-pvc-0 + ## myexample-data-pvc-1 + ## myexample-backup-pvc + ## Note: While you need two PVC fronting two PVs, multiple PVs can be attached to the same storage in many cases allowing you to share an underlying drive. + ## For artifactory.persistence.type nfs + ## If using NFS as the shared storage, you must have a running NFS server that is accessible by your Kubernetes + ## cluster nodes. + ## Need to have the following set + nfs: + ## Must pass actual IP of NFS server with '--set For artifactory.persistence.nfs.ip=${NFS_IP}' + ip: + haDataMount: "/data" + haBackupMount: "/backup" + dataDir: "/var/opt/jfrog/artifactory-ha" + backupDir: "/var/opt/jfrog/artifactory-backup" + capacity: 200Gi + mountOptions: [] + ## For artifactory.persistence.type google-storage, google-storage-v2, google-storage-v2-direct + googleStorage: + ## When using GCP buckets as your binary store (Available with enterprise license only) + gcpServiceAccount: + enabled: false + ## Use either an existing secret prepared in advance or put the config (replace the content) in the values + ## ref: https://github.com/jfrog/charts/blob/master/stable/artifactory-ha/README.md#google-storage + # customSecretName: + # config: | + # { + # "type": "service_account", + # "project_id": "", + # "private_key_id": "?????", + # "private_key": "-----BEGIN PRIVATE KEY-----\n????????==\n-----END PRIVATE KEY-----\n", + # "client_email": "???@j.iam.gserviceaccount.com", + # "client_id": "???????", + # "auth_uri": "https://accounts.google.com/o/oauth2/auth", + # "token_uri": "https://oauth2.googleapis.com/token", + # "auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs", + # "client_x509_cert_url": "https://www.googleapis.com/robot/v1....." + # } + endpoint: commondatastorage.googleapis.com + httpsOnly: false + ## Set a unique bucket name + bucketName: "artifactory-ha-gcp" + ## GCP Bucket Authentication with Identity and Credential is deprecated. + ## identity: + ## credential: + path: "artifactory-ha/filestore" + bucketExists: false + useInstanceCredentials: false + enableSignedUrlRedirect: false + # signedUrlExpirySeconds: 30 + ## For artifactory.persistence.type aws-s3-v3, s3-storage-v3-direct, s3-storage-v3-archive + awsS3V3: + testConnection: false + identity: + credential: + region: + bucketName: artifactory-aws + path: artifactory/filestore + endpoint: + port: + useHttp: + maxConnections: 50 + connectionTimeout: + socketTimeout: + kmsServerSideEncryptionKeyId: + kmsKeyRegion: + kmsCryptoMode: + useInstanceCredentials: true + usePresigning: false + signatureExpirySeconds: 300 + signedUrlExpirySeconds: 30 + cloudFrontDomainName: + cloudFrontKeyPairId: + cloudFrontPrivateKey: + enableSignedUrlRedirect: false + enablePathStyleAccess: false + multiPartLimit: + multipartElementSize: + ## For artifactory.persistence.type azure-blob, azure-blob-storage-direct, azure-blob-storage-v2-direct + azureBlob: + accountName: + accountKey: + endpoint: + containerName: + multiPartLimit: 100000000 + multipartElementSize: 50000000 + testConnection: false + service: + name: artifactory + type: ClusterIP + ## @param service.ipFamilyPolicy Controller Service ipFamilyPolicy (optional, cloud specific) + ## This can be either SingleStack, PreferDualStack or RequireDualStack + ## ref: https://kubernetes.io/docs/concepts/services-networking/dual-stack/#services + ## + ipFamilyPolicy: "" + ## @param service.ipFamilies Controller Service ipFamilies (optional, cloud specific) + ## This can be either ["IPv4"], ["IPv6"], ["IPv4", "IPv6"] or ["IPv6", "IPv4"] + ## ref: https://kubernetes.io/docs/concepts/services-networking/dual-stack/#services + ## + ipFamilies: [] + ## For supporting whitelist on the Artifactory service (useful if setting service.type=LoadBalancer) + ## Set this to a list of IP CIDR ranges + ## Example: loadBalancerSourceRanges: ['10.10.10.5/32', '10.11.10.5/32'] + ## or pass from helm command line + ## Example: helm install ... --set nginx.service.loadBalancerSourceRanges='{10.10.10.5/32,10.11.10.5/32}' + loadBalancerSourceRanges: [] + annotations: {} + ## Which nodes in the cluster should be in the external load balancer pool (have external traffic routed to them) + ## Supported pool values + ## members + ## all + pool: members + ## If the type is NodePort you can set a fixed port + # nodePort: 32082 + serviceGrpc: + name: grpc + type: ClusterIP + ## @param service.ipFamilyPolicy Controller Service ipFamilyPolicy (optional, cloud specific) + ## This can be either SingleStack, PreferDualStack or RequireDualStack + ## ref: https://kubernetes.io/docs/concepts/services-networking/dual-stack/#services + ## + ipFamilyPolicy: "" + ## @param service.ipFamilies Controller Service ipFamilies (optional, cloud specific) + ## This can be either ["IPv4"], ["IPv6"], ["IPv4", "IPv6"] or ["IPv6", "IPv4"] + ## ref: https://kubernetes.io/docs/concepts/services-networking/dual-stack/#services + ## + ipFamilies: [] + ## For supporting whitelist on the Artifactory service (useful if setting service.type=LoadBalancer) + ## Set this to a list of IP CIDR ranges + ## Example: loadBalancerSourceRanges: ['10.10.10.5/32', '10.11.10.5/32'] + ## or pass from helm command line + ## Example: helm install ... --set nginx.service.loadBalancerSourceRanges='{10.10.10.5/32,10.11.10.5/32}' + loadBalancerSourceRanges: [] + annotations: {} + ## Which nodes in the cluster should be in the external load balancer pool (have external traffic routed to them) + ## Supported pool values + ## members + ## all + pool: members + ## If the type is NodePort you can set a fixed port + # nodePort: 32082 + statefulset: + annotations: {} + ssh: + enabled: false + internalPort: 1339 + externalPort: 1339 + annotations: {} + ## Spread Artifactory pods evenly across your nodes or some other topology + ## Note this applies to both the primary and replicas + topologySpreadConstraints: [] + # - maxSkew: 1 + # topologyKey: kubernetes.io/hostname + # whenUnsatisfiable: DoNotSchedule + # labelSelector: + # matchLabels: + # app: '{{ template "artifactory-ha.name" . }}' + # role: '{{ template "artifactory-ha.name" . }}' + # release: "{{ .Release.Name }}" + + ## Type specific configurations. + ## There is a difference between the primary and the member nodes. + ## Customising their resources and java parameters is done here. + primary: + name: artifactory-ha-primary + ## preStartCommand specific to the primary node, to be run after artifactory.preStartCommand + # preStartCommand: + labels: {} + annotations: {} + persistence: + ## Set existingClaim to true or false + ## If true, you must prepare a PVC with the name e.g `volume-myrelease-artifactory-ha-primary-0` + existingClaim: false + replicaCount: 3 + # minAvailable: 1 + + updateStrategy: + type: RollingUpdate + ## Resources for the primary node + resources: {} + # requests: + # memory: "1Gi" + # cpu: "500m" + # limits: + # memory: "2Gi" + # cpu: "1" + ## The following Java options are passed to the java process running Artifactory primary node. + ## You should set them according to the resources set above + javaOpts: + # xms: "1g" + # xmx: "2g" + # corePoolSize: 24 + jmx: + enabled: false + port: 9010 + host: + ssl: false + # When authenticate is true, accessFile and passwordFile are required + authenticate: false + accessFile: + passwordFile: + # other: "" + nodeSelector: {} + tolerations: [] + affinity: {} + ## Only used if "affinity" is empty + podAntiAffinity: + ## Valid values are "soft" or "hard"; any other value indicates no anti-affinity + type: "soft" + topologyKey: "kubernetes.io/hostname" + node: + name: artifactory-ha-member + ## preStartCommand specific to the member node, to be run after artifactory.preStartCommand + # preStartCommand: + labels: {} + persistence: + ## Set existingClaim to true or false + ## If true, you must prepare a PVC with the name e.g `volume-myrelease-artifactory-ha-member-0` + existingClaim: false + replicaCount: 0 + updateStrategy: + type: RollingUpdate + minAvailable: 1 + ## Resources for the member nodes + resources: {} + # requests: + # memory: "1Gi" + # cpu: "500m" + # limits: + # memory: "2Gi" + # cpu: "1" + ## The following Java options are passed to the java process running Artifactory member nodes. + ## You should set them according to the resources set above + javaOpts: + # xms: "1g" + # xmx: "2g" + # corePoolSize: 24 + jmx: + enabled: false + port: 9010 + host: + ssl: false + # When authenticate is true, accessFile and passwordFile are required + authenticate: false + accessFile: + passwordFile: + # other: "" + nodeSelector: {} + ## Wait for Artifactory primary + waitForPrimaryStartup: + enabled: true + ## Setting time will override the built in test and will just wait the set time + time: + tolerations: [] + ## Complete specification of the "affinity" of the member nodes; if this is non-empty, + ## "podAntiAffinity" values are not used. + affinity: {} + ## Only used if "affinity" is empty + podAntiAffinity: + ## Valid values are "soft" or "hard"; any other value indicates no anti-affinity + type: "soft" + topologyKey: "kubernetes.io/hostname" +frontend: + name: frontend + enabled: true + internalPort: 8070 + ## Extra environment variables that can be used to tune frontend to your needs. + ## Uncomment and set value as needed + extraEnvironmentVariables: + # - name: MY_ENV_VAR + # value: "" + resources: {} + # requests: + # memory: "100Mi" + # cpu: "100m" + # limits: + # memory: "1Gi" + # cpu: "1" + ## Session settings + session: + ## Time in minutes after which the frontend token will need to be refreshed + timeoutMinutes: '30' + ## Add lifecycle hooks for frontend container + lifecycle: {} + # postStart: + # exec: + # command: ["/bin/sh", "-c", "echo Hello from the postStart handler"] + # preStop: + # exec: + # command: ["/bin/sh","-c","echo Hello from the preStop handler"] + + ## The following settings are to configure the frequency of the liveness and startup probes when splitServicesToContainers set to true + livenessProbe: + enabled: true + config: | + exec: + command: + - sh + - -c + - curl --fail --max-time {{ .Values.probes.timeoutSeconds }} http://localhost:{{ .Values.frontend.internalPort }}/api/v1/system/liveness + initialDelaySeconds: {{ if semverCompare " --cert=ca.crt --key=ca.private.key` + # customCertificatesSecretName: + + ## When resetAccessCAKeys is true, Access will regenerate the CA certificate and matching private key + # resetAccessCAKeys: false + database: + maxOpenConnections: 80 + tomcat: + connector: + maxThreads: 50 + sendReasonPhrase: false + extraConfig: 'acceptCount="100"' + livenessProbe: + enabled: true + config: | + exec: + command: + - sh + - -c + - curl --fail --max-time {{ .Values.probes.timeoutSeconds }} http://localhost:8040/access/api/v1/system/liveness + initialDelaySeconds: {{ if semverCompare " --cert=tls.crt --key=tls.key` + # customCertificatesSecretName: + + ## The following settings are to configure the frequency of the liveness and startup probes when splitServicesToContainers set to true + livenessProbe: + enabled: true + config: | + exec: + command: + - sh + - -c + - curl --fail --max-time {{ .Values.probes.timeoutSeconds }} http://localhost:{{ .Values.jfconnect.internalPort }}/api/v1/system/liveness + initialDelaySeconds: {{ if semverCompare " /var/opt/jfrog/nginx/message"] + # preStop: + # exec: + # command: ["/bin/sh","-c","nginx -s quit; while killall -0 nginx; do sleep 1; done"] + + ## Sidecar containers for tailing Nginx logs + loggers: [] + # - access.log + # - error.log + + ## Loggers containers resources + loggersResources: {} + # requests: + # memory: "64Mi" + # cpu: "25m" + # limits: + # memory: "128Mi" + # cpu: "50m" + + ## Logs options + logs: + stderr: false + stdout: false + level: warn + ## A list of custom ports to expose on the NGINX pod. Follows the conventional Kubernetes yaml syntax for container ports. + customPorts: [] + # - containerPort: 8066 + # name: docker + + ## The nginx main conf was moved to files/nginx-main-conf.yaml. This key is commented out to keep support for the old configuration + # mainConf: | + + ## The nginx artifactory conf was moved to files/nginx-artifactory-conf.yaml. This key is commented out to keep support for the old configuration + # artifactoryConf: | + customInitContainers: "" + customSidecarContainers: "" + customVolumes: "" + customVolumeMounts: "" + customCommand: + ## allows overwriting the command for the nginx container. + ## defaults to [ 'nginx', '-g', 'daemon off;' ] + + service: + ## For minikube, set this to NodePort, elsewhere use LoadBalancer + type: LoadBalancer + ssloffload: false + ## @param service.ssloffloadForceHttps Only enabled when service.ssloffload is set to True. + ## Force all requests from NGINX to the upstream server are over HTTPS, even when SSL offloading is enabled. + ## This is useful in environments where internal traffic must remain secure with https only. + ssloffloadForceHttps: false + ## @param service.ipFamilyPolicy Controller Service ipFamilyPolicy (optional, cloud specific) + ## This can be either SingleStack, PreferDualStack or RequireDualStack + ## ref: https://kubernetes.io/docs/concepts/services-networking/dual-stack/#services + ## + ipFamilyPolicy: "" + ## @param service.ipFamilies Controller Service ipFamilies (optional, cloud specific) + ## This can be either ["IPv4"], ["IPv6"], ["IPv4", "IPv6"] or ["IPv6", "IPv4"] + ## ref: https://kubernetes.io/docs/concepts/services-networking/dual-stack/#services + ## + ipFamilies: [] + ## For supporting whitelist on the Nginx LoadBalancer service + ## Set this to a list of IP CIDR ranges + ## Example: loadBalancerSourceRanges: ['10.10.10.5/32', '10.11.10.5/32'] + ## or pass from helm command line + ## Example: helm install ... --set nginx.service.loadBalancerSourceRanges='{10.10.10.5/32,10.11.10.5/32}' + loadBalancerSourceRanges: [] + ## Provide static ip address + loadBalancerIP: + ## There are two available options: "Cluster" (default) and "Local". + externalTrafficPolicy: Cluster + labels: {} + # label-key: label-value + ## If the type is NodePort you can set a fixed port + # nodePort: 32082 + ## A list of custom ports to be exposed on nginx service. Follows the conventional Kubernetes yaml syntax for service ports. + customPorts: [] + # - port: 8066 + # targetPort: 8066 + # protocol: TCP + # name: docker + + annotations: {} + ## Renamed nginx internalPort 80,443 to 8080,8443 to support openshift + http: + enabled: true + externalPort: 80 + internalPort: 8080 + https: + enabled: true + externalPort: 443 + internalPort: 8443 + ## DEPRECATED: The following will be replaced by L1065-L1076 in a future release + # externalPortHttp: 80 + # internalPortHttp: 8080 + # externalPortHttps: 443 + # internalPortHttps: 8443 + + ssh: + internalPort: 1339 + externalPort: 1339 + ## The following settings are to configure the frequency of the liveness and readiness probes. + livenessProbe: + enabled: true + config: | + exec: + command: + - sh + - -c + - curl -s -k --fail --max-time {{ .Values.probes.timeoutSeconds }} {{ include "nginx.scheme" . }}://localhost:{{ include "nginx.port" . }}/ + initialDelaySeconds: {{ if semverCompare " + ## If set to "-", storageClassName: "", which disables dynamic provisioning + ## If undefined (the default) or set to null, no storageClassName spec is + ## set, choosing the default provisioner. (gp2 on AWS, standard on + ## GKE, AWS & OpenStack) + ## + # storageClassName: "-" + resources: {} + # requests: + # memory: "250Mi" + # cpu: "100m" + # limits: + # memory: "250Mi" + # cpu: "500m" + + nodeSelector: {} + tolerations: [] + affinity: {} +## Filebeat Sidecar container +## The provided filebeat configuration is for Artifactory logs. It assumes you have a logstash installed and configured properly. +filebeat: + enabled: false + name: artifactory-filebeat + image: + repository: "docker.elastic.co/beats/filebeat" + version: 7.16.2 + logstashUrl: "logstash:5044" + terminationGracePeriod: 10 + livenessProbe: + exec: + command: + - sh + - -c + - | + #!/usr/bin/env bash -e + curl --fail 127.0.0.1:5066 + failureThreshold: 3 + initialDelaySeconds: 10 + periodSeconds: 10 + timeoutSeconds: 5 + readinessProbe: + exec: + command: + - sh + - -c + - | + #!/usr/bin/env bash -e + filebeat test output + failureThreshold: 3 + initialDelaySeconds: 10 + periodSeconds: 10 + timeoutSeconds: 5 + resources: {} + # requests: + # memory: "100Mi" + # cpu: "100m" + # limits: + # memory: "100Mi" + # cpu: "100m" + + filebeatYml: | + logging.level: info + path.data: {{ .Values.artifactory.persistence.mountPath }}/log/filebeat + name: artifactory-filebeat + queue.spool: + file: + permissions: 0760 + filebeat.inputs: + - type: log + enabled: true + close_eof: ${CLOSE:false} + paths: + - {{ .Values.artifactory.persistence.mountPath }}/log/*.log + fields: + service: "jfrt" + log_type: "artifactory" + output: + logstash: + hosts: ["{{ .Values.filebeat.logstashUrl }}"] + extraEnvironmentVariables: {} + # - name: MY_ENV_VAR + # value: "" +## RTFS deployment +rtfs: + name: rtfs + enabled: false + image: + registry: releases-docker.jfrog.io + repository: jfrog/artifactory-federation + pullPolicy: IfNotPresent + tag: 1.4.18 + jfrogUrl: "" + replicaCount: 1 + internalRestPort: 8025 + apiContext: artifactory/service/rtfs + persistence: + mountPath: "/var/opt/jfrog/federation" + federationProperties: {} + ## Apply horizontal pod auto scaling on distribution pods + ## Ref: https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/ + autoscaling: + enabled: false + minReplicas: 1 + maxReplicas: 3 + targetCPUUtilizationPercentage: 70 + metrics: | + # - type: Resource + # resource: + # name: memory + # target: + # type: Utilization + # averageUtilization: 75 # Target memory usage is 75% of the allocated memory per pod + ## @param federationProperties Configuration to be added to the config map, overriding Spring configuration. + ## @param customFederationPropertiesConfig Specifies the name of a custom configMap to use instead of the default one. + ## The name of the custom configMap will be initialized from the value of customFederationPropertiesConfig. + # customFederationPropertiesConfig: + javaOpts: "" + service: + type: ClusterIP + restPort: 8025 + grpcPort: 8026 + ## @param service.loadBalancerSourceRanges Address(es) that are allowed when service is `LoadBalancer` + ## https://kubernetes.io/docs/tasks/access-application-cluster/configure-cloud-provider-firewall/#restrict-access-for-loadbalancer-service + ## e.g: + ## loadBalancerSourceRanges: + ## - 10.10.10.0/24 + ## + loadBalancerSourceRanges: [] + ## @param service.externalIPs Set the ExternalIPs + ## + externalIPs: [] + ## @param service.externalTrafficPolicy Enable client source IP preservation + ## ref https://kubernetes.io/docs/tasks/access-application-cluster/create-external-load-balancer/#preserving-the-client-source-ip + ## + externalTrafficPolicy: Cluster + ## @param service.loadBalancerIP Set the LoadBalancerIP + ## + loadBalancerIP: "" + ## @param service.labels Service labels. Evaluated as a template + labels: {} + ## @param service.annotations Service annotations. Evaluated as a template + ## Example: + ## annotations: + ## service.beta.kubernetes.io/aws-load-balancer-internal: 0.0.0.0/0 + annotations: {} + livenessProbe: + enabled: true + config: | + httpGet: + path: /{{ .Values.rtfs.apiContext }}/api/v1/system/liveness + port: {{ .Values.rtfs.internalRestPort }} + scheme: HTTP + initialDelaySeconds: 10 + periodSeconds: 10 + timeoutSeconds: {{ .Values.probes.timeoutSeconds }} + failureThreshold: 5 + successThreshold: 1 + readinessProbe: + enabled: true + config: | + httpGet: + path: /router/api/v1/system/readiness + port: {{ .Values.router.internalPort }} + scheme: HTTP + initialDelaySeconds: 10 + periodSeconds: 5 + timeoutSeconds: {{ .Values.probes.timeoutSeconds }} + failureThreshold: 30 + successThreshold: 1 + startupProbe: + enabled: true + config: | + httpGet: + path: /{{ .Values.rtfs.apiContext }}/api/v1/system/readiness + port: {{ .Values.rtfs.internalRestPort }} + scheme: HTTP + initialDelaySeconds: 120 + periodSeconds: 5 + timeoutSeconds: {{ .Values.probes.timeoutSeconds }} + failureThreshold: 30 + successThreshold: 1 + resources: {} + # We usually recommend not to specify default resources and to leave this as a conscious + # choice for the user. This also increases chances charts run on environments with little + # resources, such as Minikube. If you do want to specify resources, uncomment the following + # lines, adjust them as necessary, and remove the curly braces after 'resources:'. + # limits: + # cpu: 100m + # memory: 128Mi + # requests: + # cpu: 100m + # memory: 128Mi + + # Add lifecycle hooks for observability container + lifecycle: {} + # postStart: + # exec: + # command: ["/bin/sh", "-c", "echo Hello from the postStart handler"] + # preStop: + # exec: + # command: ["/bin/sh","-c","echo Hello from the preStop handler"] + + annotations: {} + deployment: + annotations: {} + labels: {} + podSecurityContext: + enabled: true + runAsNonRoot: true + runAsUser: 1030 + runAsGroup: 1030 + fsGroup: 1030 + containerSecurityContext: + enabled: true + runAsNonRoot: true + allowPrivilegeEscalation: false + seccompProfile: + type: RuntimeDefault + capabilities: + drop: + - ALL + nodeSelector: {} + tolerations: [] + affinity: {} + customInitContainers: "" + customSidecarContainers: "" + customVolumes: "" + customVolumeMounts: "" + ## @param extraEnvironmentVariables that can be used to tune PDN Server to your needs. + ## Example: + ## extraEnvironmentVariables: + ## - name: MY_ENV_VAR + ## value: "" + extraEnvironmentVariables: [] +## Allows to add additional kubernetes resources +## Use --- as a separator between multiple resources +## For an example, refer - https://github.com/jfrog/log-analytics-prometheus/blob/master/helm/artifactory-ha-values.yaml +additionalResources: "" +## Adding entries to a Pod's /etc/hosts file +## For an example, refer - https://kubernetes.io/docs/concepts/services-networking/add-entries-to-pod-etc-hosts-with-host-aliases +hostAliases: [] +# - ip: "127.0.0.1" +# hostnames: +# - "foo.local" +# - "bar.local" +# - ip: "10.1.2.3" +# hostnames: +# - "foo.remote" +# - "bar.remote" +## Toggling this feature is seamless and requires helm upgrade +## will enable all microservices to run in different containers in a single pod (by default it is true) +splitServicesToContainers: true +## Specify common probes parameters +probes: + timeoutSeconds: 5 diff --git a/charts/jfrog/artifactory-ha/107.104.9/Chart.yaml b/charts/jfrog/artifactory-ha/107.104.9/Chart.yaml index 5bb11cb76a..9b4f72dd82 100644 --- a/charts/jfrog/artifactory-ha/107.104.9/Chart.yaml +++ b/charts/jfrog/artifactory-ha/107.104.9/Chart.yaml @@ -2,7 +2,6 @@ annotations: artifactoryServiceVersion: 7.104.17 catalog.cattle.io/certified: partner catalog.cattle.io/display-name: JFrog Artifactory HA - catalog.cattle.io/featured: "2" catalog.cattle.io/kube-version: '>= 1.19.0-0' catalog.cattle.io/release-name: artifactory-ha metadataVersion: 7.118.2 diff --git a/charts/jfrog/artifactory-jcr/107.104.10/CHANGELOG.md b/charts/jfrog/artifactory-jcr/107.104.10/CHANGELOG.md new file mode 100644 index 0000000000..a4fccae84b --- /dev/null +++ b/charts/jfrog/artifactory-jcr/107.104.10/CHANGELOG.md @@ -0,0 +1,213 @@ +# JFrog Container Registry Chart Changelog +All changes to this chart will be documented in this file. + +## [107.104.10] - Feb 14, 2024 +* Fixed an issue by disabling a pro-only service (Onemodel) +* Updated federation key to rtfs in cpp, jcr and oss chart + +## [107.103.0] - Dec 5, 2024 +* Removed obsolete values [GH-1932](https://github.com/jfrog/charts/pull/1932) + +## [107.81.0] - Feb 20, 2024 +* Updated `artifactory.installerInfo` content + +## [107.80.0] - Feb 1, 2024 +* Updated README.md to create a namespace using `--create-namespace` as part of helm install + +## [107.74.0] - Nov 23, 2023 +* **IMPORTANT** +* Added min kubeVersion ">= 1.19.0-0" in chart.yaml + +## [107.66.0] - Jul 20, 2023 +* Disabled federation services when splitServicesToContainers=true + +## [107.45.0] - Aug 25, 2022 +* Included event service as mandatory and remove the flag from values.yaml + +## [107.41.0] - Jul 22, 2022 +* Bumping chart version to align with app version +* Disabled jfconnect and event services when splitServicesToContainers=true + +## [107.19.4] - May 27, 2021 +* Bumping chart version to align with app version +* Update dependency Artifactory chart version to 107.19.4 + +## [4.0.0] - Apr 22, 2021 +* **Breaking change:** +* Increased default postgresql persistence size to `200Gi` +* Update postgresql tag version to `13.2.0-debian-10-r55` +* Update postgresql chart version to `10.3.18` in chart.yaml - [10.x Upgrade Notes](https://github.com/bitnami/charts/tree/master/bitnami/postgresql#to-1000) +* If this is a new deployment or you already use an external database (`postgresql.enabled=false`), these changes **do not affect you**! +* If this is an upgrade and you are using the default PostgreSQL (`postgresql.enabled=true`), you need to pass previous 9.x/10.x/12.x's postgresql.image.tag, previous postgresql.persistence.size and databaseUpgradeReady=true +* **IMPORTANT** +* This chart is only helm v3 compatible. +* Update dependency Artifactory chart version to 12.0.0 (Artifactory 7.18.3) + +## [3.8.0] - Apr 5, 2021 +* **IMPORTANT** +* Added `charts.jfrog.io` as default JFrog Helm repository +* Update dependency Artifactory chart version to 11.13.0 (Artifactory 7.17.5) + +## [3.7.0] - Mar 31, 2021 +* Update dependency Artifactory chart version to 11.12.2 (Artifactory 7.17.4) + +## [3.6.0] - Mar 15, 2021 +* Update dependency Artifactory chart version to 11.10.0 (Artifactory 7.16.3) + +## [3.5.1] - Mar 03, 2021 +* Update dependency Artifactory chart version to 11.9.3 (Artifactory 7.15.4) + +## [3.5.0] - Feb 18, 2021 +* Update dependency Artifactory chart version to 11.9.0 (Artifactory 7.15.3) + +## [3.4.1] - Feb 08, 2021 +* Update dependency Artifactory chart version to 11.8.0 (Artifactory 7.12.8) + +## [3.4.0] - Jan 4, 2020 +* Update dependency Artifactory chart version to 11.7.4 (Artifactory 7.12.5) + +## [3.3.1] - Dec 1, 2020 +* Update dependency Artifactory chart version to 11.5.4 (Artifactory 7.11.5) + +## [3.3.0] - Nov 23, 2020 +* Update dependency Artifactory chart version to 11.5.2 (Artifactory 7.11.2) + +## [3.2.2] - Nov 9, 2020 +* Update dependency Artifactory chart version to 11.4.5 (Artifactory 7.10.6) + +## [3.2.1] - Nov 2, 2020 +* Update dependency Artifactory chart version to 11.4.4 (Artifactory 7.10.5) + +## [3.2.0] - Oct 19, 2020 +* Update dependency Artifactory chart version to 11.4.0 (Artifactory 7.10.2) + +## [3.1.0] - Sep 30, 2020 +* Update dependency Artifactory chart version to 11.1.0 (Artifactory 7.9.0) + +## [3.0.2] - Sep 23, 2020 +* Updates readme + +## [3.0.1] - Sep 15, 2020 +* Update dependency Artifactory chart version to 11.0.1 (Artifactory 7.7.8) + +## [3.0.0] - Sep 14, 2020 +* **Breaking change:** Added `image.registry` and changed `image.version` to `image.tag` for docker images +* Update dependency Artifactory chart version to 11.0.0 (Artifactory 7.7.3) + +## [2.5.1] - Jul 29, 2020 +* Update dependency Artifactory chart version to 10.0.12 (Artifactory 7.6.3) + +## [2.5.0] - Jul 10, 2020 +* Update dependency Artifactory chart version to 10.0.3 (Artifactory 7.6.2) +* **IMPORTANT** +* Added ChartCenter Helm repository in README + +## [2.4.0] - Jun 30, 2020 +* Update dependency Artifactory chart version to 9.6.0 (Artifactory 7.6.1) + +## [2.3.1] - Jun 12, 2020 +* Update dependency Artifactory chart version to 9.5.2 (Artifactory 7.5.7) + +## [2.3.0] - Jun 1, 2020 +* Update dependency Artifactory chart version to 9.5.0 (Artifactory 7.5.5) + +## [2.2.5] - May 27, 2020 +* Update dependency Artifactory chart version to 9.4.9 (Artifactory 7.4.3) + +## [2.2.4] - May 20, 2020 +* Update dependency Artifactory chart version to 9.4.6 (Artifactory 7.4.3) + +## [2.2.3] - May 07, 2020 +* Update dependency Artifactory chart version to 9.4.5 (Artifactory 7.4.3) +* Add `installerInfo` string format + +## [2.2.2] - Apr 28, 2020 +* Update dependency Artifactory chart version to 9.4.4 (Artifactory 7.4.3) + +## [2.2.1] - Apr 27, 2020 +* Update dependency Artifactory chart version to 9.4.3 (Artifactory 7.4.1) + +## [2.2.0] - Apr 14, 2020 +* Update dependency Artifactory chart version to 9.4.0 (Artifactory 7.4.1) + +## [2.2.0] - Apr 14, 2020 +* Update dependency Artifactory chart version to 9.4.0 (Artifactory 7.4.1) + +## [2.1.6] - Apr 13, 2020 +* Update dependency Artifactory chart version to 9.3.1 (Artifactory 7.3.2) + +## [2.1.5] - Apr 8, 2020 +* Update dependency Artifactory chart version to 9.2.8 (Artifactory 7.3.2) + +## [2.1.4] - Mar 30, 2020 +* Update dependency Artifactory chart version to 9.2.3 (Artifactory 7.3.2) + +## [2.1.3] - Mar 30, 2020 +* Update dependency Artifactory chart version to 9.2.1 (Artifactory 7.3.2) + +## [2.1.2] - Mar 26, 2020 +* Update dependency Artifactory chart version to 9.1.5 (Artifactory 7.3.2) + +## [2.1.1] - Mar 25, 2020 +* Update dependency Artifactory chart version to 9.1.4 (Artifactory 7.3.2) + +## [2.1.0] - Mar 23, 2020 +* Update dependency Artifactory chart version to 9.1.3 (Artifactory 7.3.2) + +## [2.0.13] - Mar 19, 2020 +* Update dependency Artifactory chart version to 9.0.28 (Artifactory 7.2.1) + +## [2.0.12] - Mar 17, 2020 +* Update dependency Artifactory chart version to 9.0.26 (Artifactory 7.2.1) + +## [2.0.11] - Mar 11, 2020 +* Unified charts public release + +## [2.0.10] - Mar 8, 2020 +* Update dependency Artifactory chart version to 9.0.20 (Artifactory 7.2.1) + +## [2.0.9] - Feb 26, 2020 +* Update dependency Artifactory chart version to 9.0.15 (Artifactory 7.2.1) + +## [2.0.0] - Feb 12, 2020 +* Update dependency Artifactory chart version to 9.0.0 (Artifactory 7.0.0) + +## [1.1.0] - Jan 19, 2020 +* Update dependency Artifactory chart version to 8.4.1 (Artifactory 6.17.0) + +## [1.1.1] - Feb 3, 2020 +* Update dependency Artifactory chart version to 8.4.4 + +## [1.1.0] - Jan 19, 2020 +* Update dependency Artifactory chart version to 8.4.1 (Artifactory 6.17.0) + +## [1.0.1] - Dec 31, 2019 +* Update dependency Artifactory chart version to 8.3.5 + +## [1.0.0] - Dec 23, 2019 +* Update dependency Artifactory chart version to 8.3.3 + +## [0.2.1] - Dec 12, 2019 +* Update dependency Artifactory chart version to 8.3.1 + +## [0.2.0] - Dec 1, 2019 +* Updated Artifactory version to 6.16.0 + +## [0.1.5] - Nov 28, 2019 +* Update dependency Artifactory chart version to 8.2.6 + +## [0.1.4] - Nov 20, 2019 +* Update Readme + +## [0.1.3] - Nov 20, 2019 +* Fix JCR logo url +* Update dependency to Artifactory 8.2.2 chart + +## [0.1.2] - Nov 20, 2019 +* Update JCR logo + +## [0.1.1] - Nov 20, 2019 +* Add `appVersion` to Chart.yaml + +## [0.1.0] - Nov 20, 2019 +* Initial release of the JFrog Container Registry helm chart diff --git a/charts/jfrog/artifactory-jcr/107.104.10/Chart.yaml b/charts/jfrog/artifactory-jcr/107.104.10/Chart.yaml new file mode 100644 index 0000000000..ea2353180f --- /dev/null +++ b/charts/jfrog/artifactory-jcr/107.104.10/Chart.yaml @@ -0,0 +1,30 @@ +annotations: + catalog.cattle.io/certified: partner + catalog.cattle.io/display-name: JFrog Container Registry + catalog.cattle.io/kube-version: '>= 1.19.0-0' + catalog.cattle.io/release-name: artifactory-jcr +apiVersion: v2 +appVersion: 7.104.10 +dependencies: +- name: artifactory + repository: file://charts/artifactory + version: 107.104.10 +description: JFrog Container Registry +home: https://jfrog.com/container-registry/ +icon: file://assets/icons/artifactory-jcr.png +keywords: +- artifactory +- jfrog +- container +- registry +- devops +- jfrog-container-registry +kubeVersion: '>= 1.19.0-0' +maintainers: +- email: helm@jfrog.com + name: Chart Maintainers at JFrog +name: artifactory-jcr +sources: +- https://github.com/jfrog/charts +type: application +version: 107.104.10 diff --git a/charts/jfrog/artifactory-jcr/107.104.10/LICENSE b/charts/jfrog/artifactory-jcr/107.104.10/LICENSE new file mode 100644 index 0000000000..8dada3edaf --- /dev/null +++ b/charts/jfrog/artifactory-jcr/107.104.10/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright {yyyy} {name of copyright owner} + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/charts/jfrog/artifactory-jcr/107.104.10/README.md b/charts/jfrog/artifactory-jcr/107.104.10/README.md new file mode 100644 index 0000000000..c0051e61d1 --- /dev/null +++ b/charts/jfrog/artifactory-jcr/107.104.10/README.md @@ -0,0 +1,125 @@ +# JFrog Container Registry Helm Chart + +JFrog Container Registry is a free Artifactory edition with Docker and Helm repositories support. + +**Heads up: Our Helm Chart docs are moving to our main documentation site. For Artifactory installers, see [Installing Artifactory](https://www.jfrog.com/confluence/display/JFROG/Installing+Artifactory).** + +## Prerequisites Details + +* Kubernetes 1.19+ + +## Chart Details +This chart will do the following: + +* Deploy JFrog Container Registry +* Deploy an optional Nginx server +* Deploy an optional PostgreSQL Database +* Optionally expose Artifactory with Ingress [Ingress documentation](https://kubernetes.io/docs/concepts/services-networking/ingress/) + +## Installing the Chart + +### Add JFrog Helm repository + +Before installing JFrog helm charts, you need to add the [JFrog helm repository](https://charts.jfrog.io) to your helm client. + +```bash +helm repo add jfrog https://charts.jfrog.io +helm repo update +``` + +### Install Chart +To install the chart with the release name `jfrog-container-registry`: +```bash +helm upgrade --install jfrog-container-registry --set artifactory.postgresql.postgresqlPassword= jfrog/artifactory-jcr --namespace artifactory-jcr --create-namespace +``` + +### Accessing JFrog Container Registry +**NOTE:** If using artifactory or nginx service type `LoadBalancer`, it might take a few minutes for JFrog Container Registry's public IP to become available. + +### Updating JFrog Container Registry +Once you have a new chart version, you can upgrade your deployment with +```bash +helm upgrade jfrog-container-registry jfrog/artifactory-jcr --namespace artifactory-jcr --create-namespace +``` + +### Special Upgrade Notes +#### Artifactory upgrade from 6.x to 7.x (App Version) +Arifactory 6.x to 7.x upgrade requires a one time migration process. This is done automatically on pod startup if needed. +It's possible to configure the migration timeout with the following configuration in extreme cases. The provided default should be more than enough for completion of the migration. +```yaml +artifactory: + artifactory: + # Migration support from 6.x to 7.x + migration: + enabled: true + timeoutSeconds: 3600 +``` +* Note: If you are upgrading from 1.x to 3.x and above chart versions, please delete the existing statefulset of postgresql before upgrading the chart due to breaking changes in postgresql subchart. +```bash +kubectl delete statefulsets -postgresql +``` +* For more details about artifactory chart upgrades refer [here](https://github.com/jfrog/charts/blob/master/stable/artifactory/UPGRADE_NOTES.md) + +### Deleting JFrog Container Registry + +```bash +helm delete jfrog-container-registry --namespace artifactory-jcr +``` + +This will delete your JFrog Container Registry deployment.
    +**NOTE:** You might have left behind persistent volumes. You should explicitly delete them with +```bash +kubectl delete pvc ... +kubectl delete pv ... +``` + +## Database +The JFrog Container Registry chart comes with PostgreSQL deployed by default.
    +For details on the PostgreSQL configuration or customising the database, Look at the options described in the [Artifactory helm chart](https://github.com/jfrog/charts/tree/master/stable/artifactory). + +### Ingress and TLS +To get Helm to create an ingress object with a hostname, add these two lines to your Helm command: +```bash +helm upgrade --install jfrog-container-registry \ + --set artifactory.nginx.enabled=false \ + --set artifactory.ingress.enabled=true \ + --set artifactory.ingress.hosts[0]="artifactory.company.com" \ + --set artifactory.artifactory.service.type=NodePort \ + jfrog/artifactory-jcr --namespace artifactory-jcr --create-namespace +``` + +To manually configure TLS, first create/retrieve a key & certificate pair for the address(es) you wish to protect. Then create a TLS secret in the namespace: + +```bash +kubectl create secret tls artifactory-tls --cert=path/to/tls.cert --key=path/to/tls.key +``` + +Include the secret's name, along with the desired hostnames, in the Artifactory Ingress TLS section of your custom `values.yaml` file: + +```yaml +artifactory: + artifactory: + ingress: + ## If true, Artifactory Ingress will be created + ## + enabled: true + + ## Artifactory Ingress hostnames + ## Must be provided if Ingress is enabled + ## + hosts: + - jfrog-container-registry.domain.com + annotations: + kubernetes.io/tls-acme: "true" + ## Artifactory Ingress TLS configuration + ## Secrets must be manually created in the namespace + ## + tls: + - secretName: artifactory-tls + hosts: + - jfrog-container-registry.domain.com +``` + +## Useful links +https://www.jfrog.com +https://www.jfrog.com/confluence/ diff --git a/charts/jfrog/artifactory-jcr/107.104.10/app-readme.md b/charts/jfrog/artifactory-jcr/107.104.10/app-readme.md new file mode 100644 index 0000000000..9d9b7d85fc --- /dev/null +++ b/charts/jfrog/artifactory-jcr/107.104.10/app-readme.md @@ -0,0 +1,18 @@ +# JFrog Container Registry Helm Chart + +Universal Repository Manager supporting all major packaging formats, build tools and CI servers. + +## Chart Details +This chart will do the following: + +* Deploy JFrog Container Registry +* Deploy an optional Nginx server +* Optionally expose Artifactory with Ingress [Ingress documentation](https://kubernetes.io/docs/concepts/services-networking/ingress/) + + +## Useful links +Blog: [Herd Trust Into Your Rancher Labs Multi-Cloud Strategy with Artifactory](https://jfrog.com/blog/herd-trust-into-your-rancher-labs-multi-cloud-strategy-with-artifactory/) + +## Activate Your Artifactory Instance +Don't have a license? Please send an email to [rancher-jfrog-licenses@jfrog.com](mailto:rancher-jfrog-licenses@jfrog.com) to get it. + diff --git a/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/.helmignore b/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/.helmignore new file mode 100644 index 0000000000..b6e97f07fb --- /dev/null +++ b/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/.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 +OWNERS + +tests/ \ No newline at end of file diff --git a/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/CHANGELOG.md b/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/CHANGELOG.md new file mode 100644 index 0000000000..4f9f83626e --- /dev/null +++ b/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/CHANGELOG.md @@ -0,0 +1,1411 @@ +# JFrog Artifactory Chart Changelog +All changes to this chart will be documented in this file. + +## [107.104.10] - Feb 17, 2025 +* Added new RTFS service +* Added new Topology service +* Added new Onemodel service +* Added `artifactory.servicePrependReleaseName` support to prepend release name to fix mismatch between statefulSet and service +* Added customVolumeMounts support for frontend,event,evidence,jfconnect,topology,observability containers +* Added ingress and nginx routing support for rtfs service context +* Added recommended sizing extraEnvironmentVariables for access container +* Added default extra javaOpts support in system yaml for topology +* Modified the RTFS chart and the topology probe values +* Fixing secret based annotations for RTFS deployment +* Fixed `shared` block in system.yaml to include all properties +* Fixed RTFS jfrogUrl issue for platform chart +* Fixed disabling onemodel using `onemodel.enabled=false` +* Added a condition to enable onemodel only on pro chart +* Removed unwanted database support from rtfs +* Added hpa support for RTFS service + +## [107.102.0] - Nov 26, 2024 +* Remove the Xms and Xmx with InitialRAMPercentage and MaxRAMPercentage if they are available in extra_java_options + +## [107.98.0] - Nov 06, 2024 +* Add support for `extraEnvironmentVariables` on filebeat Sidecar [GH-1377](https://github.com/jfrog/charts/pull/1377) +* Support for SSL offload HTTPS proto override in Nginx service (ClusterIP, LoadBalancer) layer. Introduced `nginx.service.ssloffloadForceHttps` field with boolean type. [GH-1906](https://github.com/jfrog/charts/pull/1906) +* Enable Access workers integration when artifactory.worker.enabled is true +* Added `signedUrlExpirySeconds` option to artifactory.persistence.type of `google-storage`, `google-storage-v2`, and `google-storage-v2-direct` [GH-1858](https://github.com/jfrog/charts/pull/1858) +* Added support to bootstrap jfconnect custom certs to jfconnect trusted directory +* Fixed the type of `.Values.artifactory.persistence.googleStorage.signedUrlExpirySeconds` in binarystore.xml from boolean to integer +* Added support for modifying `pathType` in ingress + +## [107.96.0] - Sep 10, 2024 +* Merged Artifactory sizing templates to a single file per size + +## [107.94.0] - Aug 14, 2024 +* Fixed #Expose rtfs port only when it is enabled + +## [107.93.0] - Aug 9, 2024 +* Added support for worker via `artifactory.worker.enabled` flag +* Fixed creation of duplicate objects in statefulSet + +## [107.92.0] - July 31, 2024 +* Updating the example link for downloading the DB driver +* Adding dedicated ingress and service path for GRPC protocol + +## [107.91.0] - July 18, 2024 +* Remove X-JFrog-Override-Base-Url port when using default `443/80` ports + +## [107.90.0] - July 18, 2024 +* Fixed #adding colon in image registry which breaks deployment [GH-1892](https://github.com/jfrog/charts/pull/1892) +* Added new `nginx.hosts` to use Nginx server_name directive instead of `ingress.hosts` +* Added a deprecation notice of ingress.hosts when `ngnix.enabled` is true +* Added new evidence service +* Corrected database connection values based on sizing +* **IMPORTANT** +* Separate access from artifactory tomcat to run on its own dedicated tomcat + * With this change access will be running in its own dedicated container + * This will give the ability to control resources and java options specific to access + Can be done by passing the following, + `access.javaOpts.other` + `access.resources` + `access.extraEnvironmentVariables` +* Added Binary Provider recommendations + +## [107.89.0] - June 7, 2024 +* Fix the indentation of the commented-out sections in the values.yaml file +* Fixed sizing values by removing `JF_SHARED_NODE_HAENABLED` in xsmall/small configurations + +## [107.88.0] - May 29, 2024 +* **IMPORTANT** +* Refactored `nginx.artifactoryConf` and `nginx.mainConf` configuration (moved to files/nginx-artifactory-conf.yaml and files/nginx-main-conf.yaml instead of keys in values.yaml) + +## [107.87.0] - May 29, 2024 +* Renamed `.Values.artifactory.openMetrics` to `.Values.artifactory.metrics` + +## [107.85.0] - May 29, 2024 +* Changed `migration.enabled` to false by default. For 6.x to 7.x migration, this flag needs to be set to `true` + +## [107.84.0] - May 29, 2024 +* Added image section for `initContainers` instead of `initContainerImage` +* Renamed `router.image.imagePullPolicy` to `router.image.pullPolicy` +* Removed image section for `loggers` +* Added support for `global.verisons.initContainers` to override `initContainers.image.tag` +* Fixed an issue with extraSystemYaml merge +* **IMPORTANT** +* Renamed `artifactory.setSecurityContext` to `artifactory.podSecurityContext` +* Renamed `artifactory.uid` to `artifactory.podSecurityContext.runAsUser` +* Renamed `artifactory.gid` to `artifactory.podSecurityContext.runAsGroup` and `artifactory.podSecurityContext.fsGroup` +* Renamed `artifactory.fsGroupChangePolicy` to `artifactory.podSecurityContext.fsGroupChangePolicy` +* Renamed `artifactory.seLinuxOptions` to `artifactory.podSecurityContext.seLinuxOptions` +* Added flag `allowNonPostgresql` defaults to false +* Update postgresql tag version to `15.6.0-debian-12-r5` +* Added a check if `initContainerImage` exists +* Fixed an issue to generate unified secret to support artifactory fullname [GH-1882](https://github.com/jfrog/charts/issues/1882) +* Fixed an issue template render on loggers [GH-1883](https://github.com/jfrog/charts/issues/1883) +* Fixed resource constraints for "setup" initContainer of nginx deployment [GH-962] (https://github.com/jfrog/charts/issues/962) +* Added .Values.artifactory.unifiedSecretPrependReleaseName` for unified secret to prepend release name +* Fixed maxCacheSize and cacheProviderDir mix up under azure-blob-storage-v2-direct template in binarystore.xml + +## [107.82.0] - Mar 04, 2024 +* Added `disableRouterBypass` flag as experimental feature, to disable the artifactoryPath /artifactory/ and route all traffic through the Router. +* Removed Replicator service + +## [107.81.0] - Feb 20, 2024 +* **IMPORTANT** +* Refactored systemYaml configuration (moved to files/system.yaml instead of key in values.yaml) +* Added ability to provide `extraSystemYaml` configuration in values.yaml which will merge with the existing system yaml when `systemYamlOverride` is not given [GH-1848](https://github.com/jfrog/charts/pull/1848) +* Added option to modify the new cache configs, maxFileSizeLimit and skipDuringUpload +* Added IPV4/IPV6 Dualstack flag support for Artifactory and nginx service +* Added `singleStackIPv6Cluster` flag, which manages the Nginx configuration to enable listening on IPv6 and proxying. +* Fixing broken link for creating additional kubernetes resources. Refer [here](https://github.com/jfrog/log-analytics-prometheus/blob/master/helm/artifactory-values.yaml) +* Refactored installerInfo configuration (moved to files/installer-info.json instead of key in values.yaml) + +## [107.80.0] - Feb 20, 2024 +* Updated README.md to create a namespace using `--create-namespace` as part of helm install + +## [107.79.0] - Feb 20, 2024 +* **IMPORTANT** +* Added `unifiedSecretInstallation` flag which enables single unified secret holding all internal (chart) secrets to `true` by default +* Added support for azure-blob-storage-v2-direct config +* Added option to set Nginx to write access_log to container STDOUT +* **Important change:** +* Update postgresql tag version to `15.2.0-debian-11-r23` +* If this is a new deployment or you already use an external database (`postgresql.enabled=false`), these changes **do not affect you**! +* If this is an upgrade and you are using the default bundles PostgreSQL (`postgresql.enabled=true`), you need to pass previous 9.x/10.x/12.x/13.x's postgresql.image.tag, previous postgresql.persistence.size and databaseUpgradeReady=true + +## [107.77.0] - April 22, 2024 +* Removed integration service +* Added recommended postgresql sizing configurations under sizing directory +* Updated artifactory-federation (probes, port, embedded mode) +* Fixed - Removed duplicate keys of the sizing yaml file +* Fixing broken nginx port [GH-1860](https://github.com/jfrog/charts/issues/1860) +* Added nginx.customCommand to use custom commands for the nginx container + +## [107.76.0] - Dec 13, 2023 +* Added connectionTimeout and socketTimeout paramaters under AWSS3 binarystore section +* Reduced nginx startupProbe initialDelaySeconds + +## [107.74.0] - Nov 30, 2023 +* Added recommended sizing configurations under sizing directory, please refer [here](README.md/#apply-sizing-configurations-to-the-chart) +* **IMPORTANT** +* Added min kubeVersion ">= 1.19.0-0" in chart.yaml + +## [107.70.0] - Nov 30, 2023 +* Fixed - StatefulSet pod annotations changed from range to toYaml [GH-1828](https://github.com/jfrog/charts/issues/1828) +* Fixed - Invalid format for awsS3V3 `multiPartLimit,multipartElementSize` in binarystore.xml. +* Fixed - SecurityContext with runAsGroup in artifactory [GH-1838](https://github.com/jfrog/charts/issues/1838) +* Added support for custom labels in the Nginx pods [GH-1836](https://github.com/jfrog/charts/pull/1836) +* Added podSecurityContext and containerSecurityContext for nginx +* Added support for nginx on openshift, set `podSecurityContext` and `containerSecurityContext` to false +* Renamed nginx internalPort 80,443 to 8080,8443 to support openshift + +## [107.69.0] - Sep 18, 2023 +* Adjust rtfs context +* Fixed - Metadata service does not respect customVolumeMounts for DB CAs [GH-1815](https://github.com/jfrog/charts/issues/1815) + +## [107.68.8] - Sep 18, 2023 +* Reverted - Enabled `unifiedSecretInstallation` by default [GH-1819](https://github.com/jfrog/charts/issues/1819) +* Removed openshift condition check from NOTES.txt + +## [107.68.7] - Aug 28, 2023 +* Enabled `unifiedSecretInstallation` by default + +## [107.67.0] - Aug 28, 2023 +* Add 'extraJavaOpts' and 'port' values to federation service + +## [107.66.0] - Aug 28, 2023 +* Added federation service container in artifactory +* Add rtfs service to ingress in artifactory + +## [107.64.0] - Aug 28, 2023 +* Added support to configure event.webhooks within generated system.yaml +* Fixed an issue to generate ssl certificate should support artifactory fullname +* Added binarystore.xml template to persistence storage type `nfs`. The default Filestore location configured according to artifactory.persistence.nfs.dataDir. +* Added 'multiPartLimit' and 'multipartElementSize' parameters to awsS3V3 binary providers. +* Increased default Artifactory Tomcat acceptCount config to 400 +* Fixed Illegal Strict-Transport-Security header in nginx config + +## [107.63.0] - Aug 28, 2023 +* Added support for Openshift by adding the securityContext in container level. +* **IMPORTANT** +* Disable securityContext in container and pod level to deploy postgres on openshift. +* Fixed support for fsGroup in non openshift environemnt and runAsGroup in openshift environment. +* Fixed - Helm Template Error when using artifactory.loggers [GH-1791](https://github.com/jfrog/charts/issues/1791) +* Removed the nginx disable condition for openshift +* Fixed jfconnect disabling as micro-service on splitcontainers [GH-1806](https://github.com/jfrog/charts/issues/1806) + +## [107.62.0] - Jun 5, 2023 +* Upgraded to autoscaling/v2 +* Added support for 'port' and 'useHttp' parameters for s3-storage-v3 binary provider [GH-1767](https://github.com/jfrog/charts/issues/1767) + +## [107.61.0] - May 31, 2023 +* Added new binary provider `google-storage-v2-direct` +* Added missing parameter 'enableSignedUrlRedirect' to 'googleStorage' + +## [107.60.0] - May 31, 2023 +* Enabled `splitServicesToContainers` to true by default +* Updated the recommended values for small, medium and large installations to support the 'splitServicesToContainers' + +## [107.59.0] - May 31, 2023 +* Fixed reference of `terminationGracePeriodSeconds` +* Added Support for Cold Artifact Storage as part of the systemYaml configuration (disabled by default) +* Added new binary provider `s3-storage-v3-archive` +* Fixed jfconnect disabling as micro-service on non-splitcontainers +* Fixed wrong cache-fs provider ID of cluster-s3-storage-v3 in the binarystore.xml [GH-1772](https://github.com/jfrog/charts/issues/1772) + +## [107.58.0] - Mar 23, 2023 +* Updated postgresql multi-arch tag version to `13.10.0-debian-11-r14` +* Removed obselete remove-lost-found initContainer` +* Added env JF_SHARED_NODE_HAENABLED under frontend when running in the container split mode + +## [107.57.0] - Mar 02, 2023 +* Updated initContainerImage and logger image to `ubi9/ubi-minimal:9.1.0.1793` + +## [107.55.0] - Jan 31, 2023 +* Updated initContainerImage and logger image to `ubi9/ubi-minimal:9.1.0.1760` +* Adding a custom preStop to Artifactory router for allowing graceful termination to complete + +## [107.53.0] - Jan 20, 2023 +* Updated initContainerImage and logger image to `ubi8/ubi-minimal:8.7.1049` + +## [107.50.0] - Jan 20, 2023 +* Updated postgresql tag version to `13.9.0-debian-11-11` +* Fixed an issue for capabilities check of ingress +* Updated jfrogUrl text path in migrate.sh file +* Added a note that from 107.46.x chart versions, `copyOnEveryStartup` is not needed for binarystore.xml, it is always copied via initContainers. For more Info, Refer [GH-1723](https://github.com/jfrog/charts/issues/1723) + +## [107.49.0] - Jan 16, 2023 +* Added support for setting `seLinuxOptions` in `securityContext` [GH-1699](https://github.com/jfrog/charts/pull/1699) +* Added option to enable/disable proxy_request_buffering and proxy_buffering_off [GH-1686](https://github.com/jfrog/charts/pull/1686) +* Updated initContainerImage and logger image to `ubi8/ubi-minimal:8.7.1049` + +## [107.48.0] - Oct 27, 2022 +* Updated router version to `7.51.0` + +## [107.47.0] - Sep 29, 2022 +* Updated initContainerImage to `ubi8/ubi-minimal:8.6-941` +* Added support for annotations for artifactory statefulset and nginx deployment [GH-1665](https://github.com/jfrog/charts/pull/1665) +* Updated router version to `7.49.0` + +## [107.46.0] - Sep 14, 2022 +* **IMPORTANT** +* Added support for lifecycle hooks for all containers, changed `artifactory.postStartCommand` to `.Values.artifactory.lifecycle.postStart.exec.command` +* Updated initContainerImage to `ubi8/ubi-minimal:8.6-902` +* Update nginx configuration to allow websocket requests when using pipelines +* Fixed an issue to allow artifactory to make direct API calls to store instead via jfconnect service when `splitServicesToContainers=true` +* Refactor binarystore.xml configuration (moved to `files/binarystore.xml` instead of key in values.yaml) +* Added new binary providers `cluster-s3-storage-v3`, `s3-storage-v3-direct`, `azure-blob-storage-direct`, `google-storage-v2` +* Deprecated (removed) `aws-s3` binary provider [JetS3t library](https://www.jfrog.com/confluence/display/JFROG/Configuring+the+Filestore#ConfiguringtheFilestore-BinaryProvider) +* Deprecated (removed) `google-storage` binary provider and force persistence storage type `google-storage` to work with `google-storage-v2` only +* Copy binarystore.xml in init Container to fix existing persistence on file system in clear text +* Removed obselete `.Values.artifactory.binarystore.enabled` key +* Removed `newProbes.enabled`, default to new probes +* Added nginx.customCommand using inotifyd to reload nginx's config upon ssl secret or configmap changes [GH-1640](https://github.com/jfrog/charts/pull/1640) + +## [107.43.0] - Aug 25, 2022 +* Added flag `artifactory.replicator.ingress.enabled` to enable/disable ingress for replicator +* Updated initContainerImage to `ubi8/ubi-minimal:8.6-854` +* Updated router version to `7.45.0` +* Added flag `artifactory.schedulerName` to set for the pods the value of schedulerName field [GH-1606](https://github.com/jfrog/charts/issues/1606) +* Enabled TLS based on access or router in values.yaml + +## [107.42.0] - Aug 25, 2022 +* Enabled database creds secret to use from unified secret +* Updated router version to `7.42.0` +* Fix duplicate volumes for userPluginSecrets [GH-1650] (https://github.com/jfrog/charts/issues/1650) +* Added support to truncate (> 63 chars) for unifiedCustomSecretVolumeName + +## [107.41.0] - June 27, 2022 +* Added support for nginx.terminationGracePeriodSeconds [GH-1645](https://github.com/jfrog/charts/issues/1645) +* Use an alternate command for `find` to copy custom certificates +* Added support for circle of trust using `circleOfTrustCertificatesSecret` secret name [GH-1623](https://github.com/jfrog/charts/pull/1623) + +## [107.40.0] - June 16, 2022 +* Added support for PodDisruptionBudget [GH-1618](https://github.com/jfrog/charts/issues/1618) +* From artifactory 7.38.x, joinKey can be retrived from Admin > User Management > Settings in UI +* Allow templating for pod annotations [GH-1634](https://github.com/jfrog/charts/pull/1634) +* Fixed `customPersistentPodVolumeClaim` name to `customPersistentVolumeClaim` +* Added flags to control enable/disable infra services in splitServicesToContainers + +## [107.39.0] - May 31, 2022 +* Fix default `artifactory.async.corePoolSize` [GH-1612](https://github.com/jfrog/charts/issues/1612) +* Added support of nginx annotations +* Reduce startupProbe `initialDelaySeconds` +* Align all liveness and readiness probes failureThreshold to `5` seconds +* Added new flag `unifiedSecretInstallation` to enables single unified secret holding all the artifactory secrets +* Updated router version to `7.38.0` +* Add support for NFS config with directories `haBackupDir` and `haDataDir` +* Fixed - disable jfconnect on oss/jcr/cpp flavours [GH-1630](https://github.com/jfrog/charts/issues/1630) + +## [107.38.0] - May 04, 2022 +* Added support for `global.nodeSelector` to artifactory and nginx pods +* Updated router version to `7.36.1` +* Added support for custom global probes timeout +* Updated frontend container command +* Added topologySpreadConstraints to artifactory and nginx, and add lifecycle hooks to nginx [GH-1596](https://github.com/jfrog/charts/pull/1596) +* Added support of extraEnvironmentVariables for all infra services containers +* Enabled the consumption (jfconnect) flag by default +* Fix jfconnect disabling on non-splitcontainers + +## [107.37.0] - Mar 08, 2022 +* Added support for customPorts in nginx deployment +* Bugfix - Wrong proxy_pass configurations for /artifactory/ in the default artifactory.conf +* Added signedUrlExpirySeconds option to artifactory.persistence.type aws-S3-V3 +* Updated router version to `7.35.0` +* Added useInstanceCredentials,enableSignedUrlRedirect option to google-storage-v2 +* Changed dependency charts repo to `charts.jfrog.io` + +## [107.36.0] - Mar 03, 2022 +* Remove pdn tracker which starts replicator service +* Added silent option for curl probes +* Added readiness health check for the artifactory container for k8s version < 1.20 +* Fix property file migration issue to system.yaml 6.x to 7.x + +## [107.35.0] - Feb 08, 2022 +* Updated router version to `7.32.1` + +## [107.33.0] - Jan 11, 2022 +* Add more user friendly support for anti-affinity +* Pod anti-affinity is now enabled by default (soft rule) +* Readme fixes +* Added support for setting `fsGroupChangePolicy` +* Added nginx customInitContainers, customVolumes, customSidecarContainers [GH-1565](https://github.com/jfrog/charts/pull/1565) +* Updated router version to `7.30.0` + +## [107.32.0] - Dec 22, 2021 +* Updated logger image to `jfrog/ubi-minimal:8.5-204` +* Added default `8091` as `artifactory.tomcat.maintenanceConnector.port` for probes check +* Refactored probes to replace httpGet probes with basic exec + curl +* Refactored `database-creds` secret to create only when database values are passed +* Added new endpoints for probes `/artifactory/api/v1/system/liveness` and `/artifactory/api/v1/system/readiness` +* Enabled `newProbes:true` by default to use these endpoints +* Fix filebeat sidecar spool file permissions +* Updated filebeat sidecar container to `7.16.2` + +## [107.31.0] - Dec 17, 2021 +* Added support for HorizontalPodAutoscaler apiVersion `autoscaling/v2beta2` +* Remove integration service feature flag to make it mandatory service +* Update postgresql tag version to `13.4.0-debian-10-r39` +* Fixed `artifactory.resources` indentation in `migration-artifactory` init container [GH-1562](https://github.com/jfrog/charts/issues/1562) +* Refactored `router.requiredServiceTypes` to support platform chart + +## [107.30.0] - Nov 30, 2021 +* Fixed incorrect permission for filebeat.yaml +* Updated healthcheck (liveness/readiness) api for integration service +* Disable readiness health check for the artifactory container when running in the container split mode +* Ability to start replicator on enabling pdn tracker + +## [107.29.0] - Nov 26, 2021 +* Added integration service container in artifactory +* Add support for Ingress Class Name in Ingress Spec [GH-1516](https://github.com/jfrog/charts/pull/1516) +* Fixed chart values to use curl instead of wget [GH-1529](https://github.com/jfrog/charts/issues/1529) +* Updated nginx config to allow websockets when pipelines is enabled +* Moved router.topology.local.requireqservicetypes from system.yaml to router as environment variable +* Added jfconnect in system.yaml +* Updated artifactory container’s health probes to use artifactory api on rt-split +* Updated initContainerImage to `jfrog/ubi-minimal:8.5-204` +* Updated router version to `7.28.2` +* Set Jfconnect enabled to `false` in the artifactory container when running in the container split mode + +## [107.28.0] - Nov 11, 2021 +* Added default values cpu and memeory in initContainers +* Updated router version to `7.26.0` +* Updated (`rbac.create` and `serviceAccount.create` to false by default) for least privileges +* Fixed incorrect data type for `Values.router.serviceRegistry.insecure` in default values.yaml [GH-1514](https://github.com/jfrog/charts/pull/1514/files) +* **IMPORTANT** +* Changed init-container images from `alpine` to `ubi8/ubi-minimal` +* Added support for AWS License Manager using `.Values.aws.licenseConfigSecretName` + +## [107.27.0] - Oct 6, 2021 +* **Breaking change** +* Aligned probe structure (moved probes variables under config block) +* Added support for new probes(set to false by default) +* Bugfix - Invalid format for `multiPartLimit,multipartElementSize,maxCacheSize` in binarystore.xml [GH-1466](https://github.com/jfrog/charts/issues/1466) +* Added missioncontrol container in artifactory +* Dropped NET_RAW capability for the containers +* Added resources to migration-artifactory init container +* Added resources to all rt split containers +* Updated router version to `7.25.1` +* Added support for Ingress networking.k8s.io/v1/Ingress for k8s >=1.22 [GH-1487](https://github.com/jfrog/charts/pull/1487) +* Added min kubeVersion ">= 1.14.0-0" in chart.yaml +* Update alpine tag version to `3.14.2` +* Update busybox tag version to `1.33.1` +* Artifactory chart support for cluster license + +## [107.26.0] - Aug 23, 2021 +* Added Observability container (only when `splitServicesToContainers` is enabled) +* Support for high availability (when replicaCount > 1) +* Added min kubeVersion ">= 1.12.0-0" in chart.yaml + +## [107.25.0] - Aug 13, 2021 +* Updated readme of chart to point to wiki. Refer [Installing Artifactory](https://www.jfrog.com/confluence/display/JFROG/Installing+Artifactory) +* Added startupProbe and livenessProbe for RT-split containers +* Updated router version to 7.24.1 +* Added security hardening fixes +* Enabled startup probes for k8s >= 1.20.x +* Changed network policy to allow all ingress and egress traffic +* Added Observability changes +* Added support for global.versions.router (only when `splitServicesToContainers` is enabled) + +## [107.24.0] - July 27, 2021 +* Support global and product specific tags at the same time +* Added support for artifactory containers split + +## [107.23.0] - July 8, 2021 +* Bug fix - logger sideCar picks up Wrong File in helm +* Allow filebeat metrics configuration in values.yaml + +## [107.22.0] - July 6, 2021 +* Update alpine tag version to `3.14.0` +* Added `nodePort` support to artifactory-service and nginx-service templates +* Removed redundant `terminationGracePeriodSeconds` in statefulset +* Increased `startupProbe.failureThreshold` time + +## [107.21.3] - July 2, 2021 +* Added ability to change sendreasonphrase value in server.xml via system yaml + +## [107.19.3] - May 20, 2021 +* Fix broken support for startupProbe for k8s < 1.18.x +* Added support for `nameOverride` and `fullnameOverride` in values.yaml + +## [107.18.6] - April 29, 2021 +* Bumping chart version to align with app version +* Add `securityContext` option on nginx container + +## [12.0.0] - April 22, 2021 +* **Breaking change:** +* Increased default postgresql persistence size to `200Gi` +* Update postgresql tag version to `13.2.0-debian-10-r55` +* Update postgresql chart version to `10.3.18` in chart.yaml - [10.x Upgrade Notes](https://github.com/bitnami/charts/tree/master/bitnami/postgresql#to-1000) +* If this is a new deployment or you already use an external database (`postgresql.enabled=false`), these changes **do not affect you**! +* If this is an upgrade and you are using the default PostgreSQL (`postgresql.enabled=true`), you need to pass previous 9.x/10.x/12.x's postgresql.image.tag, previous postgresql.persistence.size and databaseUpgradeReady=true +* **IMPORTANT** +* This chart is only helm v3 compatible. +* Fixed filebeat-configmap naming +* Explicitly set ServiceAccount `automountServiceAccountToken` to 'true' +* Update alpine tag version to `3.13.5` + +## [11.13.2] - April 15, 2021 +* Updated Artifactory version to 7.17.9 - [Release Notes](https://www.jfrog.com/confluence/display/JFROG/Artifactory+Release+Notes#ArtifactoryReleaseNotes-Artifactory7.17.9) + +## [11.13.1] - April 6, 2021 +* Updated Artifactory version to 7.17.6 - [Release Notes](https://www.jfrog.com/confluence/display/JFROG/Artifactory+Release+Notes#ArtifactoryReleaseNotes-Artifactory7.17.6) +* Update alpine tag version to `3.13.4` + +## [11.13.0] - April 5, 2021 +* **IMPORTANT** +* Added `charts.jfrog.io` as default JFrog Helm repository +* Updated Artifactory version to 7.17.5 - [Release Notes](https://www.jfrog.com/confluence/display/JFROG/Artifactory+Release+Notes#ArtifactoryReleaseNotes-Artifactory7.17.5) + +## [11.12.2] - Mar 31, 2021 +* Updated Artifactory version to 7.17.4 - [Release Notes](https://www.jfrog.com/confluence/display/JFROG/Artifactory+Release+Notes#ArtifactoryReleaseNotes-Artifactory7.17.4) + +## [11.12.1] - Mar 30, 2021 +* Updated Artifactory version to 7.17.3 +* Add `timeoutSeconds` to all exec probes - Please refer [here](https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/#configure-probes) + +## [11.12.0] - Mar 24, 2021 +* Updated Artifactory version to 7.17.2 +* Optimized startupProbe time + +## [11.11.0] - Mar 18, 2021 +* Add support to startupProbe + +## [11.10.0] - Mar 15, 2021 +* Updated Artifactory version to 7.16.3 + +## [11.9.5] - Mar 09, 2021 +* Added HSTS header to nginx conf + +## [11.9.4] - Mar 9, 2021 +* Removed bintray URL references in the chart + +## [11.9.3] - Mar 04, 2021 +* Updated Artifactory version to 7.15.4 - [Release Notes](https://www.jfrog.com/confluence/display/JFROG/Artifactory+Release+Notes#ArtifactoryReleaseNotes-Artifactory7.15.4) + +## [11.9.2] - Mar 04, 2021 +* Fixed creation of nginx-certificate-secret when Nginx is disabled + +## [11.9.1] - Feb 19, 2021 +* Update busybox tag version to `1.32.1` + +## [11.9.0] - Feb 18, 2021 +* Updated Artifactory version to 7.15.3 - [Release Notes](https://www.jfrog.com/confluence/display/JFROG/Artifactory+Release+Notes#ArtifactoryReleaseNotes-Artifactory7.15.3) +* Add option to specify update strategy for Artifactory statefulset + +## [11.8.1] - Feb 11, 2021 +* Exposed "multiPartLimit" and "multipartElementSize" for the Azure Blob Storage Binary Provider + +## [11.8.0] - Feb 08, 2021 +* Updated Artifactory version to 7.12.8 - [Release Notes](https://www.jfrog.com/confluence/display/JFROG/Artifactory+Release+Notes#ArtifactoryReleaseNotes-Artifactory7.12.8) +* Support for custom certificates using secrets +* **Important:** Switched docker images download from `docker.bintray.io` to `releases-docker.jfrog.io` +* Update alpine tag version to `3.13.1` + +## [11.7.8] - Jan 25, 2021 +* Add support for hostAliases + +## [11.7.7] - Jan 11, 2021 +* Fix failures when using creds file for configurating google storage + +## [11.7.6] - Jan 11, 2021 +* Updated Artifactory version to 7.12.6 - [Release Notes](https://www.jfrog.com/confluence/display/JFROG/Artifactory+Release+Notes#ArtifactoryReleaseNotes-Artifactory7.12.6) + +## [11.7.5] - Jan 07, 2021 +* Added support for optional tracker dedicated ingress `.Values.artifactory.replicator.trackerIngress.enabled` (defaults to false) + +## [11.7.4] - Jan 04, 2021 +* Fixed gid support for statefulset + +## [11.7.3] - Dec 31, 2020 +* Added gid support for statefulset +* Add setSecurityContext flag to allow securityContext block to be removed from artifactory statefulset + +## [11.7.2] - Dec 29, 2020 +* **Important:** Removed `.Values.metrics` and `.Values.fluentd` (Fluentd and Prometheus integrations) +* Add support for creating additional kubernetes resources - [refer here](https://github.com/jfrog/log-analytics-prometheus/blob/master/artifactory-values.yaml) +* Updated Artifactory version to 7.12.5 + +## [11.7.1] - Dec 21, 2020 +* Updated Artifactory version to 7.12.3 + +## [11.7.0] - Dec 18, 2020 +* Updated Artifactory version to 7.12.2 +* Added `.Values.artifactory.openMetrics.enabled` + +## [11.6.1] - Dec 11, 2020 +* Added configurable `.Values.global.versions.artifactory` in values.yaml + +## [11.6.0] - Dec 10, 2020 +* Update postgresql tag version to `12.5.0-debian-10-r25` +* Fixed `artifactory.persistence.googleStorage.endpoint` from `storage.googleapis.com` to `commondatastorage.googleapis.com` +* Updated chart maintainers email + +## [11.5.5] - Dec 4, 2020 +* **Important:** Renamed `.Values.systemYaml` to `.Values.systemYamlOverride` + +## [11.5.4] - Dec 1, 2020 +* Improve error message returned when attempting helm upgrade command + +## [11.5.3] - Nov 30, 2020 +* Updated Artifactory version to 7.11.5 - [Release Notes](https://www.jfrog.com/confluence/display/JFROG/Artifactory+Release+Notes#ArtifactoryReleaseNotes-Artifactory7.11) + +## [11.5.2] - Nov 23, 2020 +* Updated Artifactory version to 7.11.2 - [Release Notes](https://www.jfrog.com/confluence/display/JFROG/Artifactory+Release+Notes#ArtifactoryReleaseNotes-Artifactory7.11) +* Updated port namings on services and pods to allow for istio protocol discovery +* Change semverCompare checks to support hosted Kubernetes +* Add flag to disable creation of ServiceMonitor when enabling prometheus metrics +* Prevent the PostHook command to be executed if the user did not specify a command in the values file +* Fix issue with tls file generation when nginx.https.enabled is false + +## [11.5.1] - Nov 19, 2020 +* Updated Artifactory version to 7.11.2 +* Bugfix - access.config.import.xml override Access Federation configurations + +## [11.5.0] - Nov 17, 2020 +* Updated Artifactory version to 7.11.1 +* Update alpine tag version to `3.12.1` + +## [11.4.6] - Nov 10, 2020 +* Pass system.yaml via external secret for advanced usecases +* Added support for custom ingress +* Bugfix - stateful set not picking up changes to database secrets + +## [11.4.5] - Nov 9, 2020 +* Updated Artifactory version to 7.10.6 - [Release Notes](https://www.jfrog.com/confluence/display/JFROG/Artifactory+Release+Notes#ArtifactoryReleaseNotes-Artifactory7.10.6) + +## [11.4.4] - Nov 2, 2020 +* Add enablePathStyleAccess property for aws-s3-v3 binary provider template + +## [11.4.3] - Nov 2, 2020 +* Updated Artifactory version to 7.10.5 - [Release Notes](https://www.jfrog.com/confluence/display/JFROG/Artifactory+Release+Notes#ArtifactoryReleaseNotes-Artifactory7.10.5) + +## [11.4.2] - Oct 22, 2020 +* Chown bug fix where Linux capability cannot chown all files causing log line warnings +* Fix Frontend timeout linting issue + +## [11.4.1] - Oct 20, 2020 +* Add flag to disable prepare-custom-persistent-volume init container + +## [11.4.0] - Oct 19, 2020 +* Updated Artifactory version to 7.10.2 - [Release Notes](https://www.jfrog.com/confluence/display/JFROG/Artifactory+Release+Notes#ArtifactoryReleaseNotes-Artifactory7.10.2) + +## [11.3.2] - Oct 15, 2020 +* Add support to specify priorityClassName for nginx deployment + +## [11.3.1] - Oct 9, 2020 +* Add support for customInitContainersBegin + +## [11.3.0] - Oct 7, 2020 +* Updated Artifactory version to 7.9.1 +* **Breaking change:** Fix `storageClass` to correct `storageClassName` in values.yaml + +## [11.2.0] - Oct 5, 2020 +* Expose Prometheus metrics via a ServiceMonitor +* Parse log files for metric data with Fluentd + +## [11.1.0] - Sep 30, 2020 +* Updated Artifactory version to 7.9.0 - [Release Notes](https://www.jfrog.com/confluence/display/JFROG/Artifactory+Release+Notes#ArtifactoryReleaseNotes-Artifactory7.9) +* Added support for resources in init container + +## [11.0.11] - Sep 25, 2020 +* Update to use linux capability CAP_CHOWN instead of root base init container to avoid any use of root containers to pass Redhat security requirements + +## [11.0.10] - Sep 28, 2020 +* Setting chart coordinates in migitation yaml + +## [11.0.9] - Sep 25, 2020 +* Update filebeat version to `7.9.2` + +## [11.0.8] - Sep 24, 2020 +* Fixed broken issue - when setting `waitForDatabase: false` container startup still waits for DB + +## [11.0.7] - Sep 22, 2020 +* Readme updates + +## [11.0.6] - Sep 22, 2020 +* Fix lint issue in migitation yaml + +## [11.0.5] - Sep 22, 2020 +* Fix broken migitation yaml + +## [11.0.4] - Sep 21, 2020 +* Added mitigation yaml for Artifactory - [More info](https://github.com/jfrog/chartcenter/blob/master/docs/securitymitigationspec.md) + +## [11.0.3] - Sep 17, 2020 +* Added configurable session(UI) timeout in frontend microservice + +## [11.0.2] - Sep 17, 2020 +* Added proper required text to be shown while postgres upgrades + +## [11.0.1] - Sep 14, 2020 +* Updated Artifactory version to 7.7.8 - [Release Notes](https://www.jfrog.com/confluence/display/JFROG/Artifactory+Release+Notes#ArtifactoryReleaseNotes-Artifactory7.7.8) + +## [11.0.0] - Sep 2, 2020 +* **Breaking change:** Changed `imagePullSecrets`values from string to list. +* **Breaking change:** Added `image.registry` and changed `image.version` to `image.tag` for docker images +* Added support for global values +* Updated maintainers in chart.yaml +* Update postgresql tag version to `12.3.0-debian-10-r71` +* Update postgresql chart version to `9.3.4` in requirements.yaml - [9.x Upgrade Notes](https://github.com/bitnami/charts/tree/master/bitnami/postgresql#900) +* **IMPORTANT** +* If this is a new deployment or you already use an external database (`postgresql.enabled=false`), these changes **do not affect you**! +* If this is an upgrade and you are using the default PostgreSQL (`postgresql.enabled=true`), you need to pass previous 9.x/10.x's postgresql.image.tag and databaseUpgradeReady=true + +## [10.1.0] - Aug 13, 2020 +* Updated Artifactory version to 7.7.3 - [Release Notes](https://www.jfrog.com/confluence/display/JFROG/Artifactory+Release+Notes#ArtifactoryReleaseNotes-Artifactory7.7) + +## [10.0.15] - Aug 10, 2020 +* Added enableSignedUrlRedirect for persistent storage type aws-s3-v3. + +## [10.0.14] - Jul 31, 2020 +* Update the README section on Nginx SSL termination to reflect the actual YAML structure. + +## [10.0.13] - Jul 30, 2020 +* Added condition to disable the migration scripts. + +## [10.0.12] - Jul 28, 2020 +* Document Artifactory node affinity. + +## [10.0.11] - Jul 28, 2020 +* Added maxConnections for persistent storage type aws-s3-v3. + +## [10.0.10] - Jul 28, 2020 +* Bugfix / support for userPluginSecrets with Artifactory 7 + +## [10.0.9] - Jul 27, 2020 +* Add tpl to external database secrets +* Modified `scheme` to `artifactory.scheme` + +## [10.0.8] - Jul 23, 2020 +* Added condition to disable the migration init container. + +## [10.0.7] - Jul 21, 2020 +* Updated Artifactory Chart to add node and primary labels to pods and service objects. + +## [10.0.6] - Jul 20, 2020 +* Support custom CA and certificates + +## [10.0.5] - Jul 13, 2020 +* Updated Artifactory version to 7.6.3 - https://www.jfrog.com/confluence/display/JFROG/Artifactory+Release+Notes#ArtifactoryReleaseNotes-Artifactory7.6.3 +* Fixed Mysql database jar path in `preStartCommand` in README + +## [10.0.4] - Jul 10, 2020 +* Move some postgresql values to where they should be according to the subchart + +## [10.0.3] - Jul 8, 2020 +* Set Artifactory access client connections to the same value as the access threads + +## [10.0.2] - Jul 6, 2020 +* Updated Artifactory version to 7.6.2 +* **IMPORTANT** +* Added ChartCenter Helm repository in README + +## [10.0.1] - Jul 01, 2020 +* Add dedicated ingress object for Replicator service when enabled + +## [10.0.0] - Jun 30, 2020 +* Update postgresql tag version to `10.13.0-debian-10-r38` +* Update alpine tag version to `3.12` +* Update busybox tag version to `1.31.1` +* **IMPORTANT** +* If this is a new deployment or you already use an external database (`postgresql.enabled=false`), these changes **do not affect you**! +* If this is an upgrade and you are using the default PostgreSQL (`postgresql.enabled=true`), you need to pass postgresql.image.tag=9.6.18-debian-10-r7 and databaseUpgradeReady=true + +## [9.6.0] - Jun 29, 2020 +* Updated Artifactory version to 7.6.1 - https://www.jfrog.com/confluence/display/JFROG/Artifactory+Release+Notes#ArtifactoryReleaseNotes-Artifactory7.6.1 +* Add tpl for external database secrets + +## [9.5.5] - Jun 25, 2020 +* Stop loading the Nginx stream module because it is now a core module + +## [9.5.4] - Jun 25, 2020 +* Notes.txt update - add --namespace parameter + +## [9.5.3] - Jun 11, 2020 +* Support list of custom secrets + +## [9.5.2] - Jun 12, 2020 +* Updated Artifactory version to 7.5.7 - https://www.jfrog.com/confluence/display/JFROG/Artifactory+Release+Notes#ArtifactoryReleaseNotes-Artifactory7.5.7 + +## [9.5.1] - Jun 8, 2020 +* Readme update - configuring Artifactory with oracledb + +## [9.5.0] - Jun 1, 2020 +* Updated Artifactory version to 7.5.5 - https://www.jfrog.com/confluence/display/JFROG/Artifactory+Release+Notes#ArtifactoryReleaseNotes-Artifactory7.5 +* Fixes bootstrap configMap permission issue +* Update postgresql tag version to `9.6.18-debian-10-r7` + +## [9.4.9] - May 27, 2020 +* Added Tomcat maxThreads & acceptCount + +## [9.4.8] - May 25, 2020 +* Fixed postgresql README `image` Parameters + +## [9.4.7] - May 24, 2020 +* Fixed typo in README regarding migration timeout + +## [9.4.6] - May 19, 2020 +* Added metadata maxOpenConnections + +## [9.4.5] - May 07, 2020 +* Fix `installerInfo` string format + +## [9.4.4] - Apr 27, 2020 +* Updated Artifactory version to 7.4.3 + +## [9.4.3] - Apr 26, 2020 +* Change order of the customInitContainers to run before the "migration-artifactory" initContainer. + +## [9.4.2] - Apr 24, 2020 +* Fix `artifactory.persistence.awsS3V3.useInstanceCredentials` incorrect conditional logic +* Bump postgresql tag version to `9.6.17-debian-10-r72` in values.yaml + +## [9.4.1] - Apr 16, 2020 +* Custom volumes in migration init container. + +## [9.4.0] - Apr 14, 2020 +* Updated Artifactory version to 7.4.1 + +## [9.3.1] - April 13, 2020 +* Update README with helm v3 commands + +## [9.3.0] - April 10, 2020 +* Use dependency charts from `https://charts.bitnami.com/bitnami` +* Bump postgresql chart version to `8.7.3` in requirements.yaml +* Bump postgresql tag version to `9.6.17-debian-10-r21` in values.yaml + +## [9.2.9] - Apr 8, 2020 +* Added recommended ingress annotation to avoid 413 errors + +## [9.2.8] - Apr 8, 2020 +* Moved migration scripts under `files` directory +* Support preStartCommand in migration Init container as `artifactory.migration.preStartCommand` + +## [9.2.7] - Apr 6, 2020 +* Fix cache size (should be 5gb instead of 50gb since volume claim is only 20gb). + +## [9.2.6] - Apr 1, 2020 +* Support masterKey and joinKey as secrets + +## [9.2.5] - Apr 1, 2020 +* Fix readme use to `-hex 32` instead of `-hex 16` + +## [9.2.4] - Mar 31, 2020 +* Change the way the artifactory `command:` is set so it will properly pass a SIGTERM to java + +## [9.2.3] - Mar 29, 2020 +* Add Nginx log options: stderr as logfile and log level + +## [9.2.2] - Mar 30, 2020 +* Use the same defaulting mechanism used for the artifactory version used elsewhere in the chart + +## [9.2.1] - Mar 29, 2020 +* Fix loggers sidecars configurations to support new file system layout and new log names + +## [9.2.0] - Mar 29, 2020 +* Fix broken admin user bootstrap configuration +* **Breaking change:** renamed `artifactory.accessAdmin` to `artifactory.admin` + +## [9.1.5] - Mar 26, 2020 +* Fix volumeClaimTemplate issue + +## [9.1.4] - Mar 25, 2020 +* Fix volume name used by filebeat container + +## [9.1.3] - Mar 24, 2020 +* Use `postgresqlExtendedConf` for setting custom PostgreSQL configuration (instead of `postgresqlConfiguration`) + +## [9.1.2] - Mar 22, 2020 +* Support for SSL offload in Nginx service(LoadBalancer) layer. Introduced `nginx.service.ssloffload` field with boolean type. + +## [9.1.1] - Mar 23, 2020 +* Moved installer info to values.yaml so it is fully customizable + +## [9.1.0] - Mar 23, 2020 +* Updated Artifactory version to 7.3.2 + +## [9.0.29] - Mar 20, 2020 +* Add support for masterKey trim during 6.x to 7.x migration if 6.x masterKey is 32 hex (64 characters) + +## [9.0.28] - Mar 18, 2020 +* Increased Nginx proxy_buffers size + +## [9.0.27] - Mar 17, 2020 +* Changed all single quotes to double quotes in values files +* useInstanceCredentials variable was declared in S3 settings but not used in chart. Now it is being used. + +## [9.0.26] - Mar 17, 2020 +* Fix rendering of Service Account annotations + +## [9.0.25] - Mar 16, 2020 +* Update Artifactory readme with extra ingress annotations needed for Artifactory to be set as SSO provider + +## [9.0.24] - Mar 16, 2020 +* Add Unsupported message from 6.18 to 7.2.x (migration) + +## [9.0.23] - Mar 12, 2020 +* Fix README.md rendering issue + +## [9.0.22] - Mar 11, 2020 +* Upgrade Docs update + +## [9.0.21] - Mar 11, 2020 +* Unified charts public release + +## [9.0.20] - Mar 6, 2020 +* Fix path to `/artifactory_bootstrap` +* Add support for controlling the name of the ingress and allow to set more than one cname + +## [9.0.19] - Mar 4, 2020 +* Add support for disabling `consoleLog` in `system.yaml` file + +## [9.0.18] - Feb 28, 2020 +* Add support to process `valueFrom` for extraEnvironmentVariables + +## [9.0.17] - Feb 26, 2020 +* Fix join key secret naming + +## [9.0.16] - Feb 26, 2020 +* Store join key to secret + +## [9.0.15] - Feb 26, 2020 +* Updated Artifactory version to 7.2.1 + +## [9.0.10] - Feb 07, 2020 +* Remove protection flag `databaseUpgradeReady` which was added to check internal postgres upgrade + +## [9.0.0] - Feb 07, 2020 +* Updated Artifactory version to 7.0.0 + +## [8.4.8] - Feb 13, 2020 +* Add support for SSH authentication to Artifactory + +## [8.4.7] - Feb 11, 2020 +* Change Artifactory service port name to be hard-coded to `http` instead of using `{{ .Release.Name }}` + +## [8.4.6] - Feb 9, 2020 +* Add support for `tpl` in the `postStartCommand` + +## [8.4.5] - Feb 4, 2020 +* Support customisable Nginx kind + +## [8.4.4] - Feb 2, 2020 +* Add a comment stating that it is recommended to use an external PostgreSQL with a static password for production installations + +## [8.4.3] - Jan 30, 2020 +* Add the option to configure resources for the logger containers + +## [8.4.2] - Jan 26, 2020 +* Improve `database.user` and `database.password` logic in order to support more use cases and make the configuration less repetitive + +## [8.4.1] - Jan 19, 2020 +* Fix replicator port config in nginx replicator configmap + +## [8.4.0] - Jan 19, 2020 +* Updated Artifactory version to 6.17.0 + +## [8.3.6] - Jan 16, 2020 +* Added example for external nginx-ingress + +## [8.3.5] - Dec 30, 2019 +* Fix for nginx probes failing when launched with http disabled + +## [8.3.4] - Dec 24, 2019 +* Better support for custom `artifactory.internalPort` + +## [8.3.3] - Dec 23, 2019 +* Mark empty map values with `{}` + +## [8.3.2] - Dec 16, 2019 +* Fix for toggling nginx service ports + +## [8.3.1] - Dec 12, 2019 +* Add support for toggling nginx service ports + +## [8.3.0] - Dec 1, 2019 +* Updated Artifactory version to 6.16.0 + +## [8.2.6] - Nov 28, 2019 +* Add support for using existing PriorityClass + +## [8.2.5] - Nov 27, 2019 +* Add support for PriorityClass + +## [8.2.4] - Nov 21, 2019 +* Add an option to use a file system cache-fs with the file-system binarystore template + +## [8.2.3] - Nov 20, 2019 +* Update Artifactory Readme + +## [8.2.2] - Nov 20, 2019 +* Update Artfactory logo + +## [8.2.1] - Nov 18, 2019 +* Add the option to provide service account annotations (in order to support stuff like https://docs.aws.amazon.com/eks/latest/userguide/specify-service-account-role.html) + +## [8.2.0] - Nov 18, 2019 +* Updated Artifactory version to 6.15.0 + +## [8.1.11] - Nov 17, 2019 +* Do not provide a default master key. Allow it to be auto generated by Artifactory on first startup + +## [8.1.10] - Nov 17, 2019 +* Fix creation of double slash in nginx artifactory configuration + +## [8.1.9] - Nov 14, 2019 +* Set explicit `postgresql.postgresqlPassword=""` to avoid helm v3 error + +## [8.1.8] - Nov 12, 2019 +* Updated Artifactory version to 6.14.1 + +## [8.1.7] - Nov 9, 2019 +* Additional documentation for masterKey + +## [8.1.6] - Nov 10, 2019 +* Update PostgreSQL chart version to 7.0.1 +* Use formal PostgreSQL configuration format + +## [8.1.5] - Nov 8, 2019 +* Add support `artifactory.service.loadBalancerSourceRanges` for whitelisting when setting `artifactory.service.type=LoadBalancer` + +## [8.1.4] - Nov 6, 2019 +* Add support for any type of environment variable by using `extraEnvironmentVariables` as-is + +## [8.1.3] - Nov 6, 2019 +* Add nodeselector support for Postgresql + +## [8.1.2] - Nov 5, 2019 +* Add support for the aws-s3-v3 filestore, which adds support for pod IAM roles + +## [8.1.1] - Nov 4, 2019 +* When using `copyOnEveryStartup`, make sure that the target base directories are created before copying the files + +## [8.1.0] - Nov 3, 2019 +* Updated Artifactory version to 6.14.0 + +## [8.0.1] - Nov 3, 2019 +* Make sure the artifactory pod exits when one of the pre-start stages fail + +## [8.0.0] - Oct 27, 2019 +**IMPORTANT - BREAKING CHANGES!**
    +**DOWNTIME MIGHT BE REQUIRED FOR AN UPGRADE!** +* If this is a new deployment or you already use an external database (`postgresql.enabled=false`), these changes **do not affect you**! +* If this is an upgrade and you are using the default PostgreSQL (`postgresql.enabled=true`), must use the upgrade instructions in [UPGRADE_NOTES.md](UPGRADE_NOTES.md)! +* PostgreSQL sub chart was upgraded to version `6.5.x`. This version is **not backward compatible** with the old version (`0.9.5`)! +* Note the following **PostgreSQL** Helm chart changes + * The chart configuration has changed! See [values.yaml](values.yaml) for the new keys used + * **PostgreSQL** is deployed as a StatefulSet + * See [PostgreSQL helm chart](https://hub.helm.sh/charts/stable/postgresql) for all available configurations + +## [7.18.3] - Oct 24, 2019 +* Change the preStartCommand to support templating + +## [7.18.2] - Oct 21, 2019 +* Add support for setting `artifactory.labels` +* Add support for setting `nginx.labels` + +## [7.18.1] - Oct 10, 2019 +* Updated Artifactory version to 6.13.1 + +## [7.18.0] - Oct 7, 2019 +* Updated Artifactory version to 6.13.0 + +## [7.17.5] - Sep 24, 2019 +* Option to skip wait-for-db init container with '--set waitForDatabase=false' + +## [7.17.4] - Sep 11, 2019 +* Updated Artifactory version to 6.12.2 + +## [7.17.3] - Sep 9, 2019 +* Updated Artifactory version to 6.12.1 + +## [7.17.2] - Aug 22, 2019 +* Fix the nginx server_name directive used with ingress.hosts + +## [7.17.1] - Aug 21, 2019 +* Enable the Artifactory container's liveness and readiness probes + +## [7.17.0] - Aug 21, 2019 +* Updated Artifactory version to 6.12.0 + +## [7.16.11] - Aug 14, 2019 +* Updated Artifactory version to 6.11.6 + +## [7.16.10] - Aug 11, 2019 +* Fix Ingress routing and add an example + +## [7.16.9] - Aug 5, 2019 +* Do not mount `access/etc/bootstrap.creds` unless user specifies a custom password or secret (Access already generates a random password if not provided one) +* If custom `bootstrap.creds` is provided (using keys or custom secret), prepare it with an init container so the temp file does not persist + +## [7.16.8] - Aug 4, 2019 +* Improve binarystore config + 1. Convert to a secret + 2. Move config to values.yaml + 3. Support an external secret + +## [7.16.7] - Jul 29, 2019 +* Don't create the nginx configmaps when nginx.enabled is false + +## [7.16.6] - Jul 24, 2019 +* Simplify nginx setup and shorten initial wait for probes + +## [7.16.5] - Jul 22, 2019 +* Change Ingress API to be compatible with recent kubernetes versions + +## [7.16.4] - Jul 22, 2019 +* Updated Artifactory version to 6.11.3 + +## [7.16.3] - Jul 11, 2019 +* Add ingress.hosts to the Nginx server_name directive when ingress is enabled to help with Docker repository sub domain configuration + +## [7.16.2] - Jul 3, 2019 +* Fix values key in reverse proxy example + +## [7.16.1] - Jul 1, 2019 +* Updated Artifactory version to 6.11.1 + +## [7.16.0] - Jun 27, 2019 +* Update Artifactory version to 6.11 and add restart to Artifactory when bootstrap.creds file has been modified + +## [7.15.8] - Jun 27, 2019 +* Add the option for changing nginx config using values.yaml and remove outdated reverse proxy documentation + +## [7.15.6] - Jun 24, 2019 +* Update chart maintainers + +## [7.15.5] - Jun 24, 2019 +* Change Nginx to point to the artifactory externalPort + +## [7.15.4] - Jun 23, 2019 +* Add the option to provide an IP for the access-admin endpoints + +## [7.15.3] - Jun 23, 2019 +* Add values files for small, medium and large installations + +## [7.15.2] - Jun 20, 2019 +* Add missing terminationGracePeriodSeconds to values.yaml + +## [7.15.1] - Jun 19, 2019 +* Updated Artifactory version to 6.10.4 + +## [7.15.0] - Jun 17, 2019 +* Use configmaps for nginx configuration and remove nginx postStart command + +## [7.14.8] - Jun 18, 2019 +* Add the option to provide additional ingress rules + +## [7.14.7] - Jun 14, 2019 +* Updated readme with improved external database setup example + +## [7.14.6] - Jun 11, 2019 +* Updated Artifactory version to 6.10.3 +* Updated installer-info template + +## [7.14.5] - Jun 6, 2019 +* Updated Google Cloud Storage API URL and https settings + +## [7.14.4] - Jun 5, 2019 +* Delete the db.properties file on Artifactory startup + +## [7.14.3] - Jun 3, 2019 +* Updated Artifactory version to 6.10.2 + +## [7.14.2] - May 21, 2019 +* Updated Artifactory version to 6.10.1 + +## [7.14.1] - May 19, 2019 +* Fix missing logger image tag + +## [7.14.0] - May 7, 2019 +* Updated Artifactory version to 6.10.0 + +## [7.13.21] - May 5, 2019 +* Add support for setting `artifactory.async.corePoolSize` + +## [7.13.20] - May 2, 2019 +* Remove unused property `artifactory.releasebundle.feature.enabled` + +## [7.13.19] - May 1, 2019 +* Fix indentation issue with the replicator system property + +## [7.13.18] - Apr 30, 2019 +* Add support for JMX monitoring + +## [7.13.17] - Apr 25, 2019 +* Added support for `cacheProviderDir` + +## [7.13.16] - Apr 18, 2019 +* Changing API StatefulSet version to `v1` and permission fix for custom `artifactory.conf` for Nginx + +## [7.13.15] - Apr 16, 2019 +* Updated documentation for Reverse Proxy Configuration + +## [7.13.14] - Apr 15, 2019 +* Added support for `customVolumeMounts` + +## [7.13.13] - Aprl 12, 2019 +* Added support for `bucketExists` flag for googleStorage + +## [7.13.12] - Apr 11, 2019 +* Replace `curl` examples with `wget` due to the new base image + +## [7.13.11] - Aprl 07, 2019 +* Add support for providing the Artifactory license as a parameter + +## [7.13.10] - Apr 10, 2019 +* Updated Artifactory version to 6.9.1 + +## [7.13.9] - Aprl 04, 2019 +* Add support for templated extraEnvironmentVariables + +## [7.13.8] - Aprl 07, 2019 +* Change network policy API group + +## [7.13.7] - Aprl 04, 2019 +* Bugfix for userPluginSecrets + +## [7.13.6] - Apr 4, 2019 +* Add information about upgrading Artifactory with auto-generated postgres password + +## [7.13.5] - Aprl 03, 2019 +* Added installer info + +## [7.13.4] - Aprl 03, 2019 +* Allow secret names for user plugins to contain template language + +## [7.13.3] - Apr 02, 2019 +* Allow NetworkPolicy configurations (defaults to allow all) + +## [7.13.2] - Aprl 01, 2019 +* Add support for user plugin secret + +## [7.13.1] - Mar 27, 2019 +* Add the option to copy a list of files to ARTIFACTORY_HOME on startup + +## [7.13.0] - Mar 26, 2019 +* Updated Artifactory version to 6.9.0 + +## [7.12.18] - Mar 25, 2019 +* Add CI tests for persistence, ingress support and nginx + +## [7.12.17] - Mar 22, 2019 +* Add the option to change the default access-admin password + +## [7.12.16] - Mar 22, 2019 +* Added support for `.Probe.path` to customise the paths used for health probes + +## [7.12.15] - Mar 21, 2019 +* Added support for `artifactory.customSidecarContainers` to create custom sidecar containers +* Added support for `artifactory.customVolumes` to create custom volumes + +## [7.12.14] - Mar 21, 2019 +* Make ingress path configurable + +## [7.12.13] - Mar 19, 2019 +* Move the copy of bootstrap config from postStart to preStart + +## [7.12.12] - Mar 19, 2019 +* Fix existingClaim example + +## [7.12.11] - Mar 18, 2019 +* Add information about nginx persistence + +## [7.12.10] - Mar 15, 2019 +* Wait for nginx configuration file before using it + +## [7.12.9] - Mar 15, 2019 +* Revert securityContext changes since they were causing issues + +## [7.12.8] - Mar 15, 2019 +* Fix issue #247 (init container failing to run) + +## [7.12.7] - Mar 14, 2019 +* Updated Artifactory version to 6.8.7 +* Add support for Artifactory-CE for C++ + +## [7.12.6] - Mar 13, 2019 +* Move securityContext to container level + +## [7.12.5] - Mar 11, 2019 +* Updated Artifactory version to 6.8.6 + +## [7.12.4] - Mar 8, 2019 +* Fix existingClaim option + +## [7.12.3] - Mar 5, 2019 +* Updated Artifactory version to 6.8.4 + +## [7.12.2] - Mar 4, 2019 +* Add support for catalina logs sidecars + +## [7.12.1] - Feb 27, 2019 +* Updated Artifactory version to 6.8.3 + +## [7.12.0] - Feb 25, 2019 +* Add nginx support for tail sidecars + +## [7.11.1] - Feb 20, 2019 +* Added support for enterprise storage + +## [7.10.2] - Feb 19, 2019 +* Updated Artifactory version to 6.8.2 + +## [7.10.1] - Feb 17, 2019 +* Updated Artifactory version to 6.8.1 +* Add example of `SERVER_XML_EXTRA_CONNECTOR` usage + +## [7.10.0] - Feb 15, 2019 +* Updated Artifactory version to 6.8.0 + +## [7.9.6] - Feb 13, 2019 +* Updated Artifactory version to 6.7.3 + +## [7.9.5] - Feb 12, 2019 +* Add support for tail sidecars to view logs from k8s api + +## [7.9.4] - Feb 6, 2019 +* Fix support for customizing statefulset `terminationGracePeriodSeconds` + +## [7.9.3] - Feb 5, 2019 +* Add instructions on how to deploy Artifactory with embedded Derby database + +## [7.9.2] - Feb 5, 2019 +* Add support for customizing statefulset `terminationGracePeriodSeconds` + +## [7.9.1] - Feb 3, 2019 +* Updated Artifactory version to 6.7.2 + +## [7.9.0] - Jan 23, 2019 +* Updated Artifactory version to 6.7.0 + +## [7.8.9] - Jan 22, 2019 +* Added support for `artifactory.customInitContainers` to create custom init containers + +## [7.8.8] - Jan 17, 2019 +* Added support of values ingress.labels + +## [7.8.7] - Jan 16, 2019 +* Mount replicator.yaml (config) directly to /replicator_extra_conf + +## [7.8.6] - Jan 13, 2019 +* Fix documentation about nginx group id + +## [7.8.5] - Jan 13, 2019 +* Updated Artifactory version to 6.6.5 + +## [7.8.4] - Jan 8, 2019 +* Make artifactory.replicator.publicUrl required when the replicator is enabled + +## [7.8.3] - Jan 1, 2019 +* Updated Artifactory version to 6.6.3 +* Add support for `artifactory.extraEnvironmentVariables` to pass more environment variables to Artifactory + +## [7.8.2] - Dec 28, 2018 +* Fix location `replicator.yaml` is copied to + +## [7.8.1] - Dec 27, 2018 +* Updated Artifactory version to 6.6.1 + +## [7.8.0] - Dec 20, 2018 +* Updated Artifactory version to 6.6.0 + +## [7.7.13] - Dec 17, 2018 +* Updated Artifactory version to 6.5.13 + +## [7.7.12] - Dec 12, 2018 +* Fix documentation about Artifactory license setup using secret + +## [7.7.11] - Dec 10, 2018 +* Fix issue when using existing claim + +## [7.7.10] - Dec 5, 2018 +* Remove Distribution certificates creation. + +## [7.7.9] - Nov 30, 2018 +* Updated Artifactory version to 6.5.9 + +## [7.7.8] - Nov 29, 2018 +* Updated postgresql version to 9.6.11 + +## [7.7.7] - Nov 27, 2018 +* Updated Artifactory version to 6.5.8 + +## [7.7.6] - Nov 19, 2018 +* Added support for configMap to use custom Reverse Proxy Configuration with Nginx + +## [7.7.5] - Nov 14, 2018 +* Fix location of `nodeSelector`, `affinity` and `tolerations` + +## [7.7.4] - Nov 14, 2018 +* Updated Artifactory version to 6.5.3 + +## [7.7.3] - Nov 12, 2018 +* Support artifactory.preStartCommand for running command before entrypoint starts + +## [7.7.2] - Nov 7, 2018 +* Support database.url parameter (DB_URL) + +## [7.7.1] - Oct 29, 2018 +* Change probes port to 8040 (so they will not be blocked when all tomcat threads on 8081 are exhausted) + +## [7.7.0] - Oct 28, 2018 +* Update postgresql chart to version 0.9.5 to be able and use `postgresConfig` options + +## [7.6.8] - Oct 23, 2018 +* Fix providing external secret for database credentials + +## [7.6.7] - Oct 23, 2018 +* Allow user to configure externalTrafficPolicy for Loadbalancer + +## [7.6.6] - Oct 22, 2018 +* Updated ingress annotation support (with examples) to support docker registry v2 + +## [7.6.5] - Oct 21, 2018 +* Updated Artifactory version to 6.5.2 + +## [7.6.4] - Oct 19, 2018 +* Allow providing pre-existing secret containing master key +* Allow arbitrary annotations on primary and member node pods +* Enforce size limits when using local storage with `emptyDir` +* Allow providing pre-existing secrets containing external database credentials + +## [7.6.3] - Oct 18, 2018 +* Updated Artifactory version to 6.5.1 + +## [7.6.2] - Oct 17, 2018 +* Add Apache 2.0 license + +## [7.6.1] - Oct 11, 2018 +* Supports master-key in the secrets and stateful-set +* Allows ingress default `backend` to be enabled or disabled (defaults to enabled) + +## [7.6.0] - Oct 11, 2018 +* Updated Artifactory version to 6.5.0 + +## [7.5.4] - Oct 9, 2018 +* Quote ingress hosts to support wildcard names + +## [7.5.3] - Oct 4, 2018 +* Add PostgreSQL resources template + +## [7.5.2] - Oct 2, 2018 +* Add `helm repo add jfrog https://charts.jfrog.io` to README + +## [7.5.1] - Oct 2, 2018 +* Set Artifactory to 6.4.1 + +## [7.5.0] - Sep 27, 2018 +* Set Artifactory to 6.4.0 + +## [7.4.3] - Sep 26, 2018 +* Add ci/test-values.yaml + +## [7.4.2] - Sep 2, 2018 +* Updated Artifactory version to 6.3.2 +* Removed unused PVC + +## [7.4.0] - Aug 22, 2018 +* Added support to run as non root +* Updated Artifactory version to 6.2.0 + +## [7.3.0] - Aug 22, 2018 +* Enabled RBAC Support +* Added support for PostStartCommand (To download Database JDBC connector) +* Increased postgresql max_connections +* Added support for `nginx.conf` ConfigMap +* Updated Artifactory version to 6.1.0 diff --git a/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/Chart.lock b/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/Chart.lock new file mode 100644 index 0000000000..8064c323b5 --- /dev/null +++ b/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/Chart.lock @@ -0,0 +1,6 @@ +dependencies: +- name: postgresql + repository: https://charts.jfrog.io/ + version: 10.3.18 +digest: sha256:404ce007353baaf92a6c5f24b249d5b336c232e5fd2c29f8a0e4d0095a09fd53 +generated: "2022-03-08T08:53:16.293311+05:30" diff --git a/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/Chart.yaml b/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/Chart.yaml new file mode 100644 index 0000000000..2abc29463f --- /dev/null +++ b/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/Chart.yaml @@ -0,0 +1,24 @@ +apiVersion: v2 +appVersion: 7.104.10 +dependencies: +- condition: postgresql.enabled + name: postgresql + repository: https://charts.jfrog.io/ + version: 10.3.18 +description: Universal Repository Manager supporting all major packaging formats, + build tools and CI servers. +home: https://www.jfrog.com/artifactory/ +icon: https://raw.githubusercontent.com/jfrog/charts/master/stable/artifactory/logo/artifactory-logo.png +keywords: +- artifactory +- jfrog +- devops +kubeVersion: '>= 1.19.0-0' +maintainers: +- email: installers@jfrog.com + name: Chart Maintainers at JFrog +name: artifactory +sources: +- https://github.com/jfrog/charts +type: application +version: 107.104.10 diff --git a/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/LICENSE b/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/LICENSE new file mode 100644 index 0000000000..8dada3edaf --- /dev/null +++ b/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright {yyyy} {name of copyright owner} + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/README.md b/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/README.md new file mode 100644 index 0000000000..783101786a --- /dev/null +++ b/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/README.md @@ -0,0 +1,60 @@ +# JFrog Artifactory Helm Chart + +**IMPORTANT!** Our Helm Chart docs have moved to our main documentation site. Below you will find the basic instructions for installing, uninstalling, and deleting Artifactory. For all other information, refer to [Installing Artifactory](https://www.jfrog.com/confluence/display/JFROG/Installing+Artifactory#InstallingArtifactory-HelmInstallation). + +## Prerequisites +* Kubernetes 1.19+ +* Artifactory Pro trial license [get one from here](https://www.jfrog.com/artifactory/free-trial/) + +## Chart Details +This chart will do the following: + +* Deploy Artifactory-Pro/Artifactory-Edge (or OSS/CE if custom image is set) +* Deploy a PostgreSQL database using the stable/postgresql chart (can be changed) **NOTE:** For production grade installations it is recommended to use an external PostgreSQL. +* Deploy an optional Nginx server +* Optionally expose Artifactory with Ingress [Ingress documentation](https://kubernetes.io/docs/concepts/services-networking/ingress/) + +## Installing the Chart + +### Add JFrog Helm repository + +Before installing JFrog helm charts, you need to add the [JFrog helm repository](https://charts.jfrog.io) to your helm client + +```bash +helm repo add jfrog https://charts.jfrog.io +helm repo update +``` + +### Install Chart +To install the chart with the release name `artifactory`: +```bash +helm upgrade --install artifactory jfrog/artifactory --namespace artifactory --create-namespace +``` + +### Apply Sizing configurations to the Chart +Note that sizings with more than one replica require an enterprise license for HA . Refer [here](https://jfrog.com/help/r/jfrog-installation-setup-documentation/high-availability) +To apply the chart with recommended sizing configurations : +For small configurations : +```bash +helm upgrade --install artifactory jfrog/artifactory -f sizing/artifactory-small.yaml --namespace artifactory --create-namespace +``` + +## Uninstalling Artifactory + +Uninstall is supported only on Helm v3 and on. + +Uninstall Artifactory using the following command. + +```bash +helm uninstall artifactory && sleep 90 && kubectl delete pvc -l app=artifactory +``` + +## Deleting Artifactory + +**IMPORTANT:** Deleting Artifactory will also delete your data volumes and you will lose all of your data. You must back up all this information before deletion. You do not need to uninstall Artifactory before deleting it. + +To delete Artifactory use the following command. + +```bash +helm delete artifactory --namespace artifactory +``` diff --git a/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/charts/postgresql/.helmignore b/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/charts/postgresql/.helmignore new file mode 100644 index 0000000000..f0c1319444 --- /dev/null +++ b/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/charts/postgresql/.helmignore @@ -0,0 +1,21 @@ +# 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 diff --git a/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/charts/postgresql/Chart.lock b/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/charts/postgresql/Chart.lock new file mode 100644 index 0000000000..3687f52df5 --- /dev/null +++ b/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/charts/postgresql/Chart.lock @@ -0,0 +1,6 @@ +dependencies: +- name: common + repository: https://charts.bitnami.com/bitnami + version: 1.4.2 +digest: sha256:dce0349883107e3ff103f4f17d3af4ad1ea3c7993551b1c28865867d3e53d37c +generated: "2021-03-30T09:13:28.360322819Z" diff --git a/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/charts/postgresql/Chart.yaml b/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/charts/postgresql/Chart.yaml new file mode 100644 index 0000000000..4b197b2071 --- /dev/null +++ b/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/charts/postgresql/Chart.yaml @@ -0,0 +1,29 @@ +annotations: + category: Database +apiVersion: v2 +appVersion: 11.11.0 +dependencies: +- name: common + repository: https://charts.bitnami.com/bitnami + version: 1.x.x +description: Chart for PostgreSQL, an object-relational database management system + (ORDBMS) with an emphasis on extensibility and on standards-compliance. +home: https://github.com/bitnami/charts/tree/master/bitnami/postgresql +icon: https://bitnami.com/assets/stacks/postgresql/img/postgresql-stack-220x234.png +keywords: +- postgresql +- postgres +- database +- sql +- replication +- cluster +maintainers: +- email: containers@bitnami.com + name: Bitnami +- email: cedric@desaintmartin.fr + name: desaintmartin +name: postgresql +sources: +- https://github.com/bitnami/bitnami-docker-postgresql +- https://www.postgresql.org/ +version: 10.3.18 diff --git a/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/charts/postgresql/README.md b/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/charts/postgresql/README.md new file mode 100644 index 0000000000..63d3605bb8 --- /dev/null +++ b/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/charts/postgresql/README.md @@ -0,0 +1,770 @@ +# PostgreSQL + +[PostgreSQL](https://www.postgresql.org/) is an object-relational database management system (ORDBMS) with an emphasis on extensibility and on standards-compliance. + +For HA, please see [this repo](https://github.com/bitnami/charts/tree/master/bitnami/postgresql-ha) + +## TL;DR + +```console +$ helm repo add bitnami https://charts.bitnami.com/bitnami +$ helm install my-release bitnami/postgresql +``` + +## Introduction + +This chart bootstraps a [PostgreSQL](https://github.com/bitnami/bitnami-docker-postgresql) deployment on a [Kubernetes](http://kubernetes.io) cluster using the [Helm](https://helm.sh) package manager. + +Bitnami charts can be used with [Kubeapps](https://kubeapps.com/) for deployment and management of Helm Charts in clusters. This chart has been tested to work with NGINX Ingress, cert-manager, fluentd and Prometheus on top of the [BKPR](https://kubeprod.io/). + +## Prerequisites + +- Kubernetes 1.12+ +- Helm 3.1.0 +- PV provisioner support in the underlying infrastructure + +## Installing the Chart +To install the chart with the release name `my-release`: + +```console +$ helm install my-release bitnami/postgresql +``` + +The command deploys PostgreSQL on the Kubernetes cluster in the default configuration. The [Parameters](#parameters) section lists the parameters that can be configured during installation. + +> **Tip**: List all releases using `helm list` + +## Uninstalling the Chart + +To uninstall/delete the `my-release` deployment: + +```console +$ helm delete my-release +``` + +The command removes all the Kubernetes components but PVC's associated with the chart and deletes the release. + +To delete the PVC's associated with `my-release`: + +```console +$ kubectl delete pvc -l release=my-release +``` + +> **Note**: Deleting the PVC's will delete postgresql data as well. Please be cautious before doing it. + +## Parameters + +The following tables lists the configurable parameters of the PostgreSQL chart and their default values. + +| Parameter | Description | Default | +|-----------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------------------------------------------------------------| +| `global.imageRegistry` | Global Docker Image registry | `nil` | +| `global.postgresql.postgresqlDatabase` | PostgreSQL database (overrides `postgresqlDatabase`) | `nil` | +| `global.postgresql.postgresqlUsername` | PostgreSQL username (overrides `postgresqlUsername`) | `nil` | +| `global.postgresql.existingSecret` | Name of existing secret to use for PostgreSQL passwords (overrides `existingSecret`) | `nil` | +| `global.postgresql.postgresqlPassword` | PostgreSQL admin password (overrides `postgresqlPassword`) | `nil` | +| `global.postgresql.servicePort` | PostgreSQL port (overrides `service.port`) | `nil` | +| `global.postgresql.replicationPassword` | Replication user password (overrides `replication.password`) | `nil` | +| `global.imagePullSecrets` | Global Docker registry secret names as an array | `[]` (does not add image pull secrets to deployed pods) | +| `global.storageClass` | Global storage class for dynamic provisioning | `nil` | +| `image.registry` | PostgreSQL Image registry | `docker.io` | +| `image.repository` | PostgreSQL Image name | `bitnami/postgresql` | +| `image.tag` | PostgreSQL Image tag | `{TAG_NAME}` | +| `image.pullPolicy` | PostgreSQL Image pull policy | `IfNotPresent` | +| `image.pullSecrets` | Specify Image pull secrets | `nil` (does not add image pull secrets to deployed pods) | +| `image.debug` | Specify if debug values should be set | `false` | +| `nameOverride` | String to partially override common.names.fullname template with a string (will prepend the release name) | `nil` | +| `fullnameOverride` | String to fully override common.names.fullname template with a string | `nil` | +| `volumePermissions.enabled` | Enable init container that changes volume permissions in the data directory (for cases where the default k8s `runAsUser` and `fsUser` values do not work) | `false` | +| `volumePermissions.image.registry` | Init container volume-permissions image registry | `docker.io` | +| `volumePermissions.image.repository` | Init container volume-permissions image name | `bitnami/bitnami-shell` | +| `volumePermissions.image.tag` | Init container volume-permissions image tag | `"10"` | +| `volumePermissions.image.pullPolicy` | Init container volume-permissions image pull policy | `Always` | +| `volumePermissions.securityContext.*` | Other container security context to be included as-is in the container spec | `{}` | +| `volumePermissions.securityContext.runAsUser` | User ID for the init container (when facing issues in OpenShift or uid unknown, try value "auto") | `0` | +| `usePasswordFile` | Have the secrets mounted as a file instead of env vars | `false` | +| `ldap.enabled` | Enable LDAP support | `false` | +| `ldap.existingSecret` | Name of existing secret to use for LDAP passwords | `nil` | +| `ldap.url` | LDAP URL beginning in the form `ldap[s]://host[:port]/basedn[?[attribute][?[scope][?[filter]]]]` | `nil` | +| `ldap.server` | IP address or name of the LDAP server. | `nil` | +| `ldap.port` | Port number on the LDAP server to connect to | `nil` | +| `ldap.scheme` | Set to `ldaps` to use LDAPS. | `nil` | +| `ldap.tls` | Set to `1` to use TLS encryption | `nil` | +| `ldap.prefix` | String to prepend to the user name when forming the DN to bind | `nil` | +| `ldap.suffix` | String to append to the user name when forming the DN to bind | `nil` | +| `ldap.search_attr` | Attribute to match against the user name in the search | `nil` | +| `ldap.search_filter` | The search filter to use when doing search+bind authentication | `nil` | +| `ldap.baseDN` | Root DN to begin the search for the user in | `nil` | +| `ldap.bindDN` | DN of user to bind to LDAP | `nil` | +| `ldap.bind_password` | Password for the user to bind to LDAP | `nil` | +| `replication.enabled` | Enable replication | `false` | +| `replication.user` | Replication user | `repl_user` | +| `replication.password` | Replication user password | `repl_password` | +| `replication.readReplicas` | Number of read replicas replicas | `1` | +| `replication.synchronousCommit` | Set synchronous commit mode. Allowed values: `on`, `remote_apply`, `remote_write`, `local` and `off` | `off` | +| `replication.numSynchronousReplicas` | Number of replicas that will have synchronous replication. Note: Cannot be greater than `replication.readReplicas`. | `0` | +| `replication.applicationName` | Cluster application name. Useful for advanced replication settings | `my_application` | +| `existingSecret` | Name of existing secret to use for PostgreSQL passwords. The secret has to contain the keys `postgresql-password` which is the password for `postgresqlUsername` when it is different of `postgres`, `postgresql-postgres-password` which will override `postgresqlPassword`, `postgresql-replication-password` which will override `replication.password` and `postgresql-ldap-password` which will be used to authenticate on LDAP. The value is evaluated as a template. | `nil` | +| `postgresqlPostgresPassword` | PostgreSQL admin password (used when `postgresqlUsername` is not `postgres`, in which case`postgres` is the admin username). | _random 10 character alphanumeric string_ | +| `postgresqlUsername` | PostgreSQL user (creates a non-admin user when `postgresqlUsername` is not `postgres`) | `postgres` | +| `postgresqlPassword` | PostgreSQL user password | _random 10 character alphanumeric string_ | +| `postgresqlDatabase` | PostgreSQL database | `nil` | +| `postgresqlDataDir` | PostgreSQL data dir folder | `/bitnami/postgresql` (same value as persistence.mountPath) | +| `extraEnv` | Any extra environment variables you would like to pass on to the pod. The value is evaluated as a template. | `[]` | +| `extraEnvVarsCM` | Name of a Config Map containing extra environment variables you would like to pass on to the pod. The value is evaluated as a template. | `nil` | +| `postgresqlInitdbArgs` | PostgreSQL initdb extra arguments | `nil` | +| `postgresqlInitdbWalDir` | PostgreSQL location for transaction log | `nil` | +| `postgresqlConfiguration` | Runtime Config Parameters | `nil` | +| `postgresqlExtendedConf` | Extended Runtime Config Parameters (appended to main or default configuration) | `nil` | +| `pgHbaConfiguration` | Content of pg_hba.conf | `nil (do not create pg_hba.conf)` | +| `postgresqlSharedPreloadLibraries` | Shared preload libraries (comma-separated list) | `pgaudit` | +| `postgresqlMaxConnections` | Maximum total connections | `nil` | +| `postgresqlPostgresConnectionLimit` | Maximum total connections for the postgres user | `nil` | +| `postgresqlDbUserConnectionLimit` | Maximum total connections for the non-admin user | `nil` | +| `postgresqlTcpKeepalivesInterval` | TCP keepalives interval | `nil` | +| `postgresqlTcpKeepalivesIdle` | TCP keepalives idle | `nil` | +| `postgresqlTcpKeepalivesCount` | TCP keepalives count | `nil` | +| `postgresqlStatementTimeout` | Statement timeout | `nil` | +| `postgresqlPghbaRemoveFilters` | Comma-separated list of patterns to remove from the pg_hba.conf file | `nil` | +| `customStartupProbe` | Override default startup probe | `nil` | +| `customLivenessProbe` | Override default liveness probe | `nil` | +| `customReadinessProbe` | Override default readiness probe | `nil` | +| `audit.logHostname` | Add client hostnames to the log file | `false` | +| `audit.logConnections` | Add client log-in operations to the log file | `false` | +| `audit.logDisconnections` | Add client log-outs operations to the log file | `false` | +| `audit.pgAuditLog` | Add operations to log using the pgAudit extension | `nil` | +| `audit.clientMinMessages` | Message log level to share with the user | `nil` | +| `audit.logLinePrefix` | Template string for the log line prefix | `nil` | +| `audit.logTimezone` | Timezone for the log timestamps | `nil` | +| `configurationConfigMap` | ConfigMap with the PostgreSQL configuration files (Note: Overrides `postgresqlConfiguration` and `pgHbaConfiguration`). The value is evaluated as a template. | `nil` | +| `extendedConfConfigMap` | ConfigMap with the extended PostgreSQL configuration files. The value is evaluated as a template. | `nil` | +| `initdbScripts` | Dictionary of initdb scripts | `nil` | +| `initdbUser` | PostgreSQL user to execute the .sql and sql.gz scripts | `nil` | +| `initdbPassword` | Password for the user specified in `initdbUser` | `nil` | +| `initdbScriptsConfigMap` | ConfigMap with the initdb scripts (Note: Overrides `initdbScripts`). The value is evaluated as a template. | `nil` | +| `initdbScriptsSecret` | Secret with initdb scripts that contain sensitive information (Note: can be used with `initdbScriptsConfigMap` or `initdbScripts`). The value is evaluated as a template. | `nil` | +| `service.type` | Kubernetes Service type | `ClusterIP` | +| `service.port` | PostgreSQL port | `5432` | +| `service.nodePort` | Kubernetes Service nodePort | `nil` | +| `service.annotations` | Annotations for PostgreSQL service | `{}` (evaluated as a template) | +| `service.loadBalancerIP` | loadBalancerIP if service type is `LoadBalancer` | `nil` | +| `service.loadBalancerSourceRanges` | Address that are allowed when svc is LoadBalancer | `[]` (evaluated as a template) | +| `schedulerName` | Name of the k8s scheduler (other than default) | `nil` | +| `shmVolume.enabled` | Enable emptyDir volume for /dev/shm for primary and read replica(s) Pod(s) | `true` | +| `shmVolume.chmod.enabled` | Run at init chmod 777 of the /dev/shm (ignored if `volumePermissions.enabled` is `false`) | `true` | +| `persistence.enabled` | Enable persistence using PVC | `true` | +| `persistence.existingClaim` | Provide an existing `PersistentVolumeClaim`, the value is evaluated as a template. | `nil` | +| `persistence.mountPath` | Path to mount the volume at | `/bitnami/postgresql` | +| `persistence.subPath` | Subdirectory of the volume to mount at | `""` | +| `persistence.storageClass` | PVC Storage Class for PostgreSQL volume | `nil` | +| `persistence.accessModes` | PVC Access Mode for PostgreSQL volume | `[ReadWriteOnce]` | +| `persistence.size` | PVC Storage Request for PostgreSQL volume | `8Gi` | +| `persistence.annotations` | Annotations for the PVC | `{}` | +| `persistence.selector` | Selector to match an existing Persistent Volume (this value is evaluated as a template) | `{}` | +| `commonAnnotations` | Annotations to be added to all deployed resources (rendered as a template) | `{}` | +| `primary.podAffinityPreset` | PostgreSQL primary pod affinity preset. Ignored if `primary.affinity` is set. Allowed values: `soft` or `hard` | `""` | +| `primary.podAntiAffinityPreset` | PostgreSQL primary pod anti-affinity preset. Ignored if `primary.affinity` is set. Allowed values: `soft` or `hard` | `soft` | +| `primary.nodeAffinityPreset.type` | PostgreSQL primary node affinity preset type. Ignored if `primary.affinity` is set. Allowed values: `soft` or `hard` | `""` | +| `primary.nodeAffinityPreset.key` | PostgreSQL primary node label key to match Ignored if `primary.affinity` is set. | `""` | +| `primary.nodeAffinityPreset.values` | PostgreSQL primary node label values to match. Ignored if `primary.affinity` is set. | `[]` | +| `primary.affinity` | Affinity for PostgreSQL primary pods assignment | `{}` (evaluated as a template) | +| `primary.nodeSelector` | Node labels for PostgreSQL primary pods assignment | `{}` (evaluated as a template) | +| `primary.tolerations` | Tolerations for PostgreSQL primary pods assignment | `[]` (evaluated as a template) | +| `primary.anotations` | Map of annotations to add to the statefulset (postgresql primary) | `{}` | +| `primary.labels` | Map of labels to add to the statefulset (postgresql primary) | `{}` | +| `primary.podAnnotations` | Map of annotations to add to the pods (postgresql primary) | `{}` | +| `primary.podLabels` | Map of labels to add to the pods (postgresql primary) | `{}` | +| `primary.priorityClassName` | Priority Class to use for each pod (postgresql primary) | `nil` | +| `primary.extraInitContainers` | Additional init containers to add to the pods (postgresql primary) | `[]` | +| `primary.extraVolumeMounts` | Additional volume mounts to add to the pods (postgresql primary) | `[]` | +| `primary.extraVolumes` | Additional volumes to add to the pods (postgresql primary) | `[]` | +| `primary.sidecars` | Add additional containers to the pod | `[]` | +| `primary.service.type` | Allows using a different service type for primary | `nil` | +| `primary.service.nodePort` | Allows using a different nodePort for primary | `nil` | +| `primary.service.clusterIP` | Allows using a different clusterIP for primary | `nil` | +| `primaryAsStandBy.enabled` | Whether to enable current cluster's primary as standby server of another cluster or not. | `false` | +| `primaryAsStandBy.primaryHost` | The Host of replication primary in the other cluster. | `nil` | +| `primaryAsStandBy.primaryPort ` | The Port of replication primary in the other cluster. | `nil` | +| `readReplicas.podAffinityPreset` | PostgreSQL read only pod affinity preset. Ignored if `primary.affinity` is set. Allowed values: `soft` or `hard` | `""` | +| `readReplicas.podAntiAffinityPreset` | PostgreSQL read only pod anti-affinity preset. Ignored if `primary.affinity` is set. Allowed values: `soft` or `hard` | `soft` | +| `readReplicas.nodeAffinityPreset.type` | PostgreSQL read only node affinity preset type. Ignored if `primary.affinity` is set. Allowed values: `soft` or `hard` | `""` | +| `readReplicas.nodeAffinityPreset.key` | PostgreSQL read only node label key to match Ignored if `primary.affinity` is set. | `""` | +| `readReplicas.nodeAffinityPreset.values` | PostgreSQL read only node label values to match. Ignored if `primary.affinity` is set. | `[]` | +| `readReplicas.affinity` | Affinity for PostgreSQL read only pods assignment | `{}` (evaluated as a template) | +| `readReplicas.nodeSelector` | Node labels for PostgreSQL read only pods assignment | `{}` (evaluated as a template) | +| `readReplicas.anotations` | Map of annotations to add to the statefulsets (postgresql readReplicas) | `{}` | +| `readReplicas.resources` | CPU/Memory resource requests/limits override for readReplicass. Will fallback to `values.resources` if not defined. | `{}` | +| `readReplicas.labels` | Map of labels to add to the statefulsets (postgresql readReplicas) | `{}` | +| `readReplicas.podAnnotations` | Map of annotations to add to the pods (postgresql readReplicas) | `{}` | +| `readReplicas.podLabels` | Map of labels to add to the pods (postgresql readReplicas) | `{}` | +| `readReplicas.priorityClassName` | Priority Class to use for each pod (postgresql readReplicas) | `nil` | +| `readReplicas.extraInitContainers` | Additional init containers to add to the pods (postgresql readReplicas) | `[]` | +| `readReplicas.extraVolumeMounts` | Additional volume mounts to add to the pods (postgresql readReplicas) | `[]` | +| `readReplicas.extraVolumes` | Additional volumes to add to the pods (postgresql readReplicas) | `[]` | +| `readReplicas.sidecars` | Add additional containers to the pod | `[]` | +| `readReplicas.service.type` | Allows using a different service type for readReplicas | `nil` | +| `readReplicas.service.nodePort` | Allows using a different nodePort for readReplicas | `nil` | +| `readReplicas.service.clusterIP` | Allows using a different clusterIP for readReplicas | `nil` | +| `readReplicas.persistence.enabled` | Whether to enable readReplicas replicas persistence | `true` | +| `terminationGracePeriodSeconds` | Seconds the pod needs to terminate gracefully | `nil` | +| `resources` | CPU/Memory resource requests/limits | Memory: `256Mi`, CPU: `250m` | +| `securityContext.*` | Other pod security context to be included as-is in the pod spec | `{}` | +| `securityContext.enabled` | Enable security context | `true` | +| `securityContext.fsGroup` | Group ID for the pod | `1001` | +| `containerSecurityContext.*` | Other container security context to be included as-is in the container spec | `{}` | +| `containerSecurityContext.enabled` | Enable container security context | `true` | +| `containerSecurityContext.runAsUser` | User ID for the container | `1001` | +| `serviceAccount.enabled` | Enable service account (Note: Service Account will only be automatically created if `serviceAccount.name` is not set) | `false` | +| `serviceAccount.name` | Name of existing service account | `nil` | +| `networkPolicy.enabled` | Enable NetworkPolicy | `false` | +| `networkPolicy.allowExternal` | Don't require client label for connections | `true` | +| `networkPolicy.explicitNamespacesSelector` | A Kubernetes LabelSelector to explicitly select namespaces from which ingress traffic could be allowed | `{}` | +| `startupProbe.enabled` | Enable startupProbe | `false` | +| `startupProbe.initialDelaySeconds` | Delay before liveness probe is initiated | 30 | +| `startupProbe.periodSeconds` | How often to perform the probe | 15 | +| `startupProbe.timeoutSeconds` | When the probe times | 5 | +| `startupProbe.failureThreshold` | Minimum consecutive failures for the probe to be considered failed after having succeeded. | 10 | +| `startupProbe.successThreshold` | Minimum consecutive successes for the probe to be considered successful after having failed. | 1 | +| `livenessProbe.enabled` | Enable livenessProbe | `true` | +| `livenessProbe.initialDelaySeconds` | Delay before liveness probe is initiated | 30 | +| `livenessProbe.periodSeconds` | How often to perform the probe | 10 | +| `livenessProbe.timeoutSeconds` | When the probe times out | 5 | +| `livenessProbe.failureThreshold` | Minimum consecutive failures for the probe to be considered failed after having succeeded. | 6 | +| `livenessProbe.successThreshold` | Minimum consecutive successes for the probe to be considered successful after having failed | 1 | +| `readinessProbe.enabled` | Enable readinessProbe | `true` | +| `readinessProbe.initialDelaySeconds` | Delay before readiness probe is initiated | 5 | +| `readinessProbe.periodSeconds` | How often to perform the probe | 10 | +| `readinessProbe.timeoutSeconds` | When the probe times out | 5 | +| `readinessProbe.failureThreshold` | Minimum consecutive failures for the probe to be considered failed after having succeeded. | 6 | +| `readinessProbe.successThreshold` | Minimum consecutive successes for the probe to be considered successful after having failed | 1 | +| `tls.enabled` | Enable TLS traffic support | `false` | +| `tls.preferServerCiphers` | Whether to use the server's TLS cipher preferences rather than the client's | `true` | +| `tls.certificatesSecret` | Name of an existing secret that contains the certificates | `nil` | +| `tls.certFilename` | Certificate filename | `""` | +| `tls.certKeyFilename` | Certificate key filename | `""` | +| `tls.certCAFilename` | CA Certificate filename. If provided, PostgreSQL will authenticate TLS/SSL clients by requesting them a certificate. | `nil` | +| `tls.crlFilename` | File containing a Certificate Revocation List | `nil` | +| `metrics.enabled` | Start a prometheus exporter | `false` | +| `metrics.service.type` | Kubernetes Service type | `ClusterIP` | +| `service.clusterIP` | Static clusterIP or None for headless services | `nil` | +| `metrics.service.annotations` | Additional annotations for metrics exporter pod | `{ prometheus.io/scrape: "true", prometheus.io/port: "9187"}` | +| `metrics.service.loadBalancerIP` | loadBalancerIP if redis metrics service type is `LoadBalancer` | `nil` | +| `metrics.serviceMonitor.enabled` | Set this to `true` to create ServiceMonitor for Prometheus operator | `false` | +| `metrics.serviceMonitor.additionalLabels` | Additional labels that can be used so ServiceMonitor will be discovered by Prometheus | `{}` | +| `metrics.serviceMonitor.namespace` | Optional namespace in which to create ServiceMonitor | `nil` | +| `metrics.serviceMonitor.interval` | Scrape interval. If not set, the Prometheus default scrape interval is used | `nil` | +| `metrics.serviceMonitor.scrapeTimeout` | Scrape timeout. If not set, the Prometheus default scrape timeout is used | `nil` | +| `metrics.prometheusRule.enabled` | Set this to true to create prometheusRules for Prometheus operator | `false` | +| `metrics.prometheusRule.additionalLabels` | Additional labels that can be used so prometheusRules will be discovered by Prometheus | `{}` | +| `metrics.prometheusRule.namespace` | namespace where prometheusRules resource should be created | the same namespace as postgresql | +| `metrics.prometheusRule.rules` | [rules](https://prometheus.io/docs/prometheus/latest/configuration/alerting_rules/) to be created, check values for an example. | `[]` | +| `metrics.image.registry` | PostgreSQL Exporter Image registry | `docker.io` | +| `metrics.image.repository` | PostgreSQL Exporter Image name | `bitnami/postgres-exporter` | +| `metrics.image.tag` | PostgreSQL Exporter Image tag | `{TAG_NAME}` | +| `metrics.image.pullPolicy` | PostgreSQL Exporter Image pull policy | `IfNotPresent` | +| `metrics.image.pullSecrets` | Specify Image pull secrets | `nil` (does not add image pull secrets to deployed pods) | +| `metrics.customMetrics` | Additional custom metrics | `nil` | +| `metrics.extraEnvVars` | Extra environment variables to add to exporter | `{}` (evaluated as a template) | +| `metrics.securityContext.*` | Other container security context to be included as-is in the container spec | `{}` | +| `metrics.securityContext.enabled` | Enable security context for metrics | `false` | +| `metrics.securityContext.runAsUser` | User ID for the container for metrics | `1001` | +| `metrics.livenessProbe.initialDelaySeconds` | Delay before liveness probe is initiated | 30 | +| `metrics.livenessProbe.periodSeconds` | How often to perform the probe | 10 | +| `metrics.livenessProbe.timeoutSeconds` | When the probe times out | 5 | +| `metrics.livenessProbe.failureThreshold` | Minimum consecutive failures for the probe to be considered failed after having succeeded. | 6 | +| `metrics.livenessProbe.successThreshold` | Minimum consecutive successes for the probe to be considered successful after having failed | 1 | +| `metrics.readinessProbe.enabled` | would you like a readinessProbe to be enabled | `true` | +| `metrics.readinessProbe.initialDelaySeconds` | Delay before liveness probe is initiated | 5 | +| `metrics.readinessProbe.periodSeconds` | How often to perform the probe | 10 | +| `metrics.readinessProbe.timeoutSeconds` | When the probe times out | 5 | +| `metrics.readinessProbe.failureThreshold` | Minimum consecutive failures for the probe to be considered failed after having succeeded. | 6 | +| `metrics.readinessProbe.successThreshold` | Minimum consecutive successes for the probe to be considered successful after having failed | 1 | +| `updateStrategy` | Update strategy policy | `{type: "RollingUpdate"}` | +| `psp.create` | Create Pod Security Policy | `false` | +| `rbac.create` | Create Role and RoleBinding (required for PSP to work) | `false` | +| `extraDeploy` | Array of extra objects to deploy with the release (evaluated as a template). | `nil` | + +Specify each parameter using the `--set key=value[,key=value]` argument to `helm install`. For example, + +```console +$ helm install my-release \ + --set postgresqlPassword=secretpassword,postgresqlDatabase=my-database \ + bitnami/postgresql +``` + +The above command sets the PostgreSQL `postgres` account password to `secretpassword`. Additionally it creates a database named `my-database`. + +> NOTE: Once this chart is deployed, it is not possible to change the application's access credentials, such as usernames or passwords, using Helm. To change these application credentials after deployment, delete any persistent volumes (PVs) used by the chart and re-deploy it, or use the application's built-in administrative tools if available. + +Alternatively, a YAML file that specifies the values for the parameters can be provided while installing the chart. For example, + +```console +$ helm install my-release -f values.yaml bitnami/postgresql +``` + +> **Tip**: You can use the default [values.yaml](values.yaml) + +## Configuration and installation details + +### [Rolling VS Immutable tags](https://docs.bitnami.com/containers/how-to/understand-rolling-tags-containers/) + +It is strongly recommended to use immutable tags in a production environment. This ensures your deployment does not change automatically if the same tag is updated with a different image. + +Bitnami will release a new chart updating its containers if a new version of the main container, significant changes, or critical vulnerabilities exist. + +### Customizing primary and read replica services in a replicated configuration + +At the top level, there is a service object which defines the services for both primary and readReplicas. For deeper customization, there are service objects for both the primary and read types individually. This allows you to override the values in the top level service object so that the primary and read can be of different service types and with different clusterIPs / nodePorts. Also in the case you want the primary and read to be of type nodePort, you will need to set the nodePorts to different values to prevent a collision. The values that are deeper in the primary.service or readReplicas.service objects will take precedence over the top level service object. + +### Change PostgreSQL version + +To modify the PostgreSQL version used in this chart you can specify a [valid image tag](https://hub.docker.com/r/bitnami/postgresql/tags/) using the `image.tag` parameter. For example, `image.tag=X.Y.Z`. This approach is also applicable to other images like exporters. + +### postgresql.conf / pg_hba.conf files as configMap + +This helm chart also supports to customize the whole configuration file. + +Add your custom file to "files/postgresql.conf" in your working directory. This file will be mounted as configMap to the containers and it will be used for configuring the PostgreSQL server. + +Alternatively, you can add additional PostgreSQL configuration parameters using the `postgresqlExtendedConf` parameter as a dict, using camelCase, e.g. {"sharedBuffers": "500MB"}. Alternatively, to replace the entire default configuration use `postgresqlConfiguration`. + +In addition to these options, you can also set an external ConfigMap with all the configuration files. This is done by setting the `configurationConfigMap` parameter. Note that this will override the two previous options. + +### Allow settings to be loaded from files other than the default `postgresql.conf` + +If you don't want to provide the whole PostgreSQL configuration file and only specify certain parameters, you can add your extended `.conf` files to "files/conf.d/" in your working directory. +Those files will be mounted as configMap to the containers adding/overwriting the default configuration using the `include_dir` directive that allows settings to be loaded from files other than the default `postgresql.conf`. + +Alternatively, you can also set an external ConfigMap with all the extra configuration files. This is done by setting the `extendedConfConfigMap` parameter. Note that this will override the previous option. + +### Initialize a fresh instance + +The [Bitnami PostgreSQL](https://github.com/bitnami/bitnami-docker-postgresql) image allows you to use your custom scripts to initialize a fresh instance. In order to execute the scripts, they must be located inside the chart folder `files/docker-entrypoint-initdb.d` so they can be consumed as a ConfigMap. + +Alternatively, you can specify custom scripts using the `initdbScripts` parameter as dict. + +In addition to these options, you can also set an external ConfigMap with all the initialization scripts. This is done by setting the `initdbScriptsConfigMap` parameter. Note that this will override the two previous options. If your initialization scripts contain sensitive information such as credentials or passwords, you can use the `initdbScriptsSecret` parameter. + +The allowed extensions are `.sh`, `.sql` and `.sql.gz`. + +### Securing traffic using TLS + +TLS support can be enabled in the chart by specifying the `tls.` parameters while creating a release. The following parameters should be configured to properly enable the TLS support in the chart: + +- `tls.enabled`: Enable TLS support. Defaults to `false` +- `tls.certificatesSecret`: Name of an existing secret that contains the certificates. No defaults. +- `tls.certFilename`: Certificate filename. No defaults. +- `tls.certKeyFilename`: Certificate key filename. No defaults. + +For example: + +* First, create the secret with the cetificates files: + + ```console + kubectl create secret generic certificates-tls-secret --from-file=./cert.crt --from-file=./cert.key --from-file=./ca.crt + ``` + +* Then, use the following parameters: + + ```console + volumePermissions.enabled=true + tls.enabled=true + tls.certificatesSecret="certificates-tls-secret" + tls.certFilename="cert.crt" + tls.certKeyFilename="cert.key" + ``` + + > Note TLS and VolumePermissions: PostgreSQL requires certain permissions on sensitive files (such as certificate keys) to start up. Due to an on-going [issue](https://github.com/kubernetes/kubernetes/issues/57923) regarding kubernetes permissions and the use of `containerSecurityContext.runAsUser`, you must enable `volumePermissions` to ensure everything works as expected. + +### Sidecars + +If you need additional containers to run within the same pod as PostgreSQL (e.g. an additional metrics or logging exporter), you can do so via the `sidecars` config parameter. Simply define your container according to the Kubernetes container spec. + +```yaml +# For the PostgreSQL primary +primary: + sidecars: + - name: your-image-name + image: your-image + imagePullPolicy: Always + ports: + - name: portname + containerPort: 1234 +# For the PostgreSQL replicas +readReplicas: + sidecars: + - name: your-image-name + image: your-image + imagePullPolicy: Always + ports: + - name: portname + containerPort: 1234 +``` + +### Metrics + +The chart optionally can start a metrics exporter for [prometheus](https://prometheus.io). The metrics endpoint (port 9187) is not exposed and it is expected that the metrics are collected from inside the k8s cluster using something similar as the described in the [example Prometheus scrape configuration](https://github.com/prometheus/prometheus/blob/master/documentation/examples/prometheus-kubernetes.yml). + +The exporter allows to create custom metrics from additional SQL queries. See the Chart's `values.yaml` for an example and consult the [exporters documentation](https://github.com/wrouesnel/postgres_exporter#adding-new-metrics-via-a-config-file) for more details. + +### Use of global variables + +In more complex scenarios, we may have the following tree of dependencies + +``` + +--------------+ + | | + +------------+ Chart 1 +-----------+ + | | | | + | --------+------+ | + | | | + | | | + | | | + | | | + v v v ++-------+------+ +--------+------+ +--------+------+ +| | | | | | +| PostgreSQL | | Sub-chart 1 | | Sub-chart 2 | +| | | | | | ++--------------+ +---------------+ +---------------+ +``` + +The three charts below depend on the parent chart Chart 1. However, subcharts 1 and 2 may need to connect to PostgreSQL as well. In order to do so, subcharts 1 and 2 need to know the PostgreSQL credentials, so one option for deploying could be deploy Chart 1 with the following parameters: + +``` +postgresql.postgresqlPassword=testtest +subchart1.postgresql.postgresqlPassword=testtest +subchart2.postgresql.postgresqlPassword=testtest +postgresql.postgresqlDatabase=db1 +subchart1.postgresql.postgresqlDatabase=db1 +subchart2.postgresql.postgresqlDatabase=db1 +``` + +If the number of dependent sub-charts increases, installing the chart with parameters can become increasingly difficult. An alternative would be to set the credentials using global variables as follows: + +``` +global.postgresql.postgresqlPassword=testtest +global.postgresql.postgresqlDatabase=db1 +``` + +This way, the credentials will be available in all of the subcharts. + +## Persistence + +The [Bitnami PostgreSQL](https://github.com/bitnami/bitnami-docker-postgresql) image stores the PostgreSQL data and configurations at the `/bitnami/postgresql` path of the container. + +Persistent Volume Claims are used to keep the data across deployments. This is known to work in GCE, AWS, and minikube. +See the [Parameters](#parameters) section to configure the PVC or to disable persistence. + +If you already have data in it, you will fail to sync to standby nodes for all commits, details can refer to [code](https://github.com/bitnami/bitnami-docker-postgresql/blob/8725fe1d7d30ebe8d9a16e9175d05f7ad9260c93/9.6/debian-9/rootfs/libpostgresql.sh#L518-L556). If you need to use those data, please covert them to sql and import after `helm install` finished. + +## NetworkPolicy + +To enable network policy for PostgreSQL, install [a networking plugin that implements the Kubernetes NetworkPolicy spec](https://kubernetes.io/docs/tasks/administer-cluster/declare-network-policy#before-you-begin), and set `networkPolicy.enabled` to `true`. + +For Kubernetes v1.5 & v1.6, you must also turn on NetworkPolicy by setting the DefaultDeny namespace annotation. Note: this will enforce policy for _all_ pods in the namespace: + +```console +$ kubectl annotate namespace default "net.beta.kubernetes.io/network-policy={\"ingress\":{\"isolation\":\"DefaultDeny\"}}" +``` + +With NetworkPolicy enabled, traffic will be limited to just port 5432. + +For more precise policy, set `networkPolicy.allowExternal=false`. This will only allow pods with the generated client label to connect to PostgreSQL. +This label will be displayed in the output of a successful install. + +## Differences between Bitnami PostgreSQL image and [Docker Official](https://hub.docker.com/_/postgres) image + +- The Docker Official PostgreSQL image does not support replication. If you pass any replication environment variable, this would be ignored. The only environment variables supported by the Docker Official image are POSTGRES_USER, POSTGRES_DB, POSTGRES_PASSWORD, POSTGRES_INITDB_ARGS, POSTGRES_INITDB_WALDIR and PGDATA. All the remaining environment variables are specific to the Bitnami PostgreSQL image. +- The Bitnami PostgreSQL image is non-root by default. This requires that you run the pod with `securityContext` and updates the permissions of the volume with an `initContainer`. A key benefit of this configuration is that the pod follows security best practices and is prepared to run on Kubernetes distributions with hard security constraints like OpenShift. +- For OpenShift, one may either define the runAsUser and fsGroup accordingly, or try this more dynamic option: volumePermissions.securityContext.runAsUser="auto",securityContext.enabled=false,containerSecurityContext.enabled=false,shmVolume.chmod.enabled=false + +### Deploy chart using Docker Official PostgreSQL Image + +From chart version 4.0.0, it is possible to use this chart with the Docker Official PostgreSQL image. +Besides specifying the new Docker repository and tag, it is important to modify the PostgreSQL data directory and volume mount point. Basically, the PostgreSQL data dir cannot be the mount point directly, it has to be a subdirectory. + +``` +image.repository=postgres +image.tag=10.6 +postgresqlDataDir=/data/pgdata +persistence.mountPath=/data/ +``` + +### Setting Pod's affinity + +This chart allows you to set your custom affinity using the `XXX.affinity` paremeter(s). Find more infomation about Pod's affinity in the [kubernetes documentation](https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#affinity-and-anti-affinity). + +As an alternative, you can use of the preset configurations for pod affinity, pod anti-affinity, and node affinity available at the [bitnami/common](https://github.com/bitnami/charts/tree/master/bitnami/common#affinities) chart. To do so, set the `XXX.podAffinityPreset`, `XXX.podAntiAffinityPreset`, or `XXX.nodeAffinityPreset` parameters. + +## Troubleshooting + +Find more information about how to deal with common errors related to Bitnami’s Helm charts in [this troubleshooting guide](https://docs.bitnami.com/general/how-to/troubleshoot-helm-chart-issues). + +## Upgrading + +It's necessary to specify the existing passwords while performing an upgrade to ensure the secrets are not updated with invalid randomly generated passwords. Remember to specify the existing values of the `postgresqlPassword` and `replication.password` parameters when upgrading the chart: + +```bash +$ helm upgrade my-release bitnami/postgresql \ + --set postgresqlPassword=[POSTGRESQL_PASSWORD] \ + --set replication.password=[REPLICATION_PASSWORD] +``` + +> Note: you need to substitute the placeholders _[POSTGRESQL_PASSWORD]_, and _[REPLICATION_PASSWORD]_ with the values obtained from instructions in the installation notes. + +### To 10.0.0 + +[On November 13, 2020, Helm v2 support was formally finished](https://github.com/helm/charts#status-of-the-project), this major version is the result of the required changes applied to the Helm Chart to be able to incorporate the different features added in Helm v3 and to be consistent with the Helm project itself regarding the Helm v2 EOL. + +**What changes were introduced in this major version?** + +- Previous versions of this Helm Chart use `apiVersion: v1` (installable by both Helm 2 and 3), this Helm Chart was updated to `apiVersion: v2` (installable by Helm 3 only). [Here](https://helm.sh/docs/topics/charts/#the-apiversion-field) you can find more information about the `apiVersion` field. +- Move dependency information from the *requirements.yaml* to the *Chart.yaml* +- After running `helm dependency update`, a *Chart.lock* file is generated containing the same structure used in the previous *requirements.lock* +- The different fields present in the *Chart.yaml* file has been ordered alphabetically in a homogeneous way for all the Bitnami Helm Chart. + +**Considerations when upgrading to this version** + +- If you want to upgrade to this version using Helm v2, this scenario is not supported as this version doesn't support Helm v2 anymore +- If you installed the previous version with Helm v2 and wants to upgrade to this version with Helm v3, please refer to the [official Helm documentation](https://helm.sh/docs/topics/v2_v3_migration/#migration-use-cases) about migrating from Helm v2 to v3 + +**Useful links** + +- https://docs.bitnami.com/tutorials/resolve-helm2-helm3-post-migration-issues/ +- https://helm.sh/docs/topics/v2_v3_migration/ +- https://helm.sh/blog/migrate-from-helm-v2-to-helm-v3/ + +#### Breaking changes + +- The term `master` has been replaced with `primary` and `slave` with `readReplicas` throughout the chart. Role names have changed from `master` and `slave` to `primary` and `read`. + +To upgrade to `10.0.0`, it should be done reusing the PVCs used to hold the PostgreSQL data on your previous release. To do so, follow the instructions below (the following example assumes that the release name is `postgresql`): + +> NOTE: Please, create a backup of your database before running any of those actions. + +Obtain the credentials and the names of the PVCs used to hold the PostgreSQL data on your current release: + +```console +$ export POSTGRESQL_PASSWORD=$(kubectl get secret --namespace default postgresql -o jsonpath="{.data.postgresql-password}" | base64 --decode) +$ export POSTGRESQL_PVC=$(kubectl get pvc -l app.kubernetes.io/instance=postgresql,role=master -o jsonpath="{.items[0].metadata.name}") +``` + +Delete the PostgreSQL statefulset. Notice the option `--cascade=false`: + +```console +$ kubectl delete statefulsets.apps postgresql-postgresql --cascade=false +``` + +Now the upgrade works: + +```console +$ helm upgrade postgresql bitnami/postgresql --set postgresqlPassword=$POSTGRESQL_PASSWORD --set persistence.existingClaim=$POSTGRESQL_PVC +``` + +You will have to delete the existing PostgreSQL pod and the new statefulset is going to create a new one + +```console +$ kubectl delete pod postgresql-postgresql-0 +``` + +Finally, you should see the lines below in PostgreSQL container logs: + +```console +$ kubectl logs $(kubectl get pods -l app.kubernetes.io/instance=postgresql,app.kubernetes.io/name=postgresql,role=primary -o jsonpath="{.items[0].metadata.name}") +... +postgresql 08:05:12.59 INFO ==> Deploying PostgreSQL with persisted data... +... +``` + +### To 9.0.0 + +In this version the chart was adapted to follow the Helm label best practices, see [PR 3021](https://github.com/bitnami/charts/pull/3021). That means the backward compatibility is not guarantee when upgrading the chart to this major version. + +As a workaround, you can delete the existing statefulset (using the `--cascade=false` flag pods are not deleted) before upgrade the chart. For example, this can be a valid workflow: + +- Deploy an old version (8.X.X) + +```console +$ helm install postgresql bitnami/postgresql --version 8.10.14 +``` + +- Old version is up and running + +```console +$ helm ls +NAME NAMESPACE REVISION UPDATED STATUS CHART APP VERSION +postgresql default 1 2020-08-04 13:39:54.783480286 +0000 UTC deployed postgresql-8.10.14 11.8.0 + +$ kubectl get pods +NAME READY STATUS RESTARTS AGE +postgresql-postgresql-0 1/1 Running 0 76s +``` + +- The upgrade to the latest one (9.X.X) is going to fail + +```console +$ helm upgrade postgresql bitnami/postgresql +Error: UPGRADE FAILED: cannot patch "postgresql-postgresql" with kind StatefulSet: StatefulSet.apps "postgresql-postgresql" is invalid: spec: Forbidden: updates to statefulset spec for fields other than 'replicas', 'template', and 'updateStrategy' are forbidden +``` + +- Delete the statefulset + +```console +$ kubectl delete statefulsets.apps --cascade=false postgresql-postgresql +statefulset.apps "postgresql-postgresql" deleted +``` + +- Now the upgrade works + +```console +$ helm upgrade postgresql bitnami/postgresql +$ helm ls +NAME NAMESPACE REVISION UPDATED STATUS CHART APP VERSION +postgresql default 3 2020-08-04 13:42:08.020385884 +0000 UTC deployed postgresql-9.1.2 11.8.0 +``` + +- We can kill the existing pod and the new statefulset is going to create a new one: + +```console +$ kubectl delete pod postgresql-postgresql-0 +pod "postgresql-postgresql-0" deleted + +$ kubectl get pods +NAME READY STATUS RESTARTS AGE +postgresql-postgresql-0 1/1 Running 0 19s +``` + +Please, note that without the `--cascade=false` both objects (statefulset and pod) are going to be removed and both objects will be deployed again with the `helm upgrade` command + +### To 8.0.0 + +Prefixes the port names with their protocols to comply with Istio conventions. + +If you depend on the port names in your setup, make sure to update them to reflect this change. + +### To 7.1.0 + +Adds support for LDAP configuration. + +### To 7.0.0 + +Helm performs a lookup for the object based on its group (apps), version (v1), and kind (Deployment). Also known as its GroupVersionKind, or GVK. Changing the GVK is considered a compatibility breaker from Kubernetes' point of view, so you cannot "upgrade" those objects to the new GVK in-place. Earlier versions of Helm 3 did not perform the lookup correctly which has since been fixed to match the spec. + +In https://github.com/helm/charts/pull/17281 the `apiVersion` of the statefulset resources was updated to `apps/v1` in tune with the api's deprecated, resulting in compatibility breakage. + +This major version bump signifies this change. + +### To 6.5.7 + +In this version, the chart will use PostgreSQL with the Postgis extension included. The version used with Postgresql version 10, 11 and 12 is Postgis 2.5. It has been compiled with the following dependencies: + +- protobuf +- protobuf-c +- json-c +- geos +- proj + +### To 5.0.0 + +In this version, the **chart is using PostgreSQL 11 instead of PostgreSQL 10**. You can find the main difference and notable changes in the following links: [https://www.postgresql.org/about/news/1894/](https://www.postgresql.org/about/news/1894/) and [https://www.postgresql.org/about/featurematrix/](https://www.postgresql.org/about/featurematrix/). + +For major releases of PostgreSQL, the internal data storage format is subject to change, thus complicating upgrades, you can see some errors like the following one in the logs: + +```console +Welcome to the Bitnami postgresql container +Subscribe to project updates by watching https://github.com/bitnami/bitnami-docker-postgresql +Submit issues and feature requests at https://github.com/bitnami/bitnami-docker-postgresql/issues +Send us your feedback at containers@bitnami.com + +INFO ==> ** Starting PostgreSQL setup ** +NFO ==> Validating settings in POSTGRESQL_* env vars.. +INFO ==> Initializing PostgreSQL database... +INFO ==> postgresql.conf file not detected. Generating it... +INFO ==> pg_hba.conf file not detected. Generating it... +INFO ==> Deploying PostgreSQL with persisted data... +INFO ==> Configuring replication parameters +INFO ==> Loading custom scripts... +INFO ==> Enabling remote connections +INFO ==> Stopping PostgreSQL... +INFO ==> ** PostgreSQL setup finished! ** + +INFO ==> ** Starting PostgreSQL ** + [1] FATAL: database files are incompatible with server + [1] DETAIL: The data directory was initialized by PostgreSQL version 10, which is not compatible with this version 11.3. +``` + +In this case, you should migrate the data from the old chart to the new one following an approach similar to that described in [this section](https://www.postgresql.org/docs/current/upgrading.html#UPGRADING-VIA-PGDUMPALL) from the official documentation. Basically, create a database dump in the old chart, move and restore it in the new one. + +### To 4.0.0 + +This chart will use by default the Bitnami PostgreSQL container starting from version `10.7.0-r68`. This version moves the initialization logic from node.js to bash. This new version of the chart requires setting the `POSTGRES_PASSWORD` in the slaves as well, in order to properly configure the `pg_hba.conf` file. Users from previous versions of the chart are advised to upgrade immediately. + +IMPORTANT: If you do not want to upgrade the chart version then make sure you use the `10.7.0-r68` version of the container. Otherwise, you will get this error + +``` +The POSTGRESQL_PASSWORD environment variable is empty or not set. Set the environment variable ALLOW_EMPTY_PASSWORD=yes to allow the container to be started with blank passwords. This is recommended only for development +``` + +### To 3.0.0 + +This releases make it possible to specify different nodeSelector, affinity and tolerations for master and slave pods. +It also fixes an issue with `postgresql.master.fullname` helper template not obeying fullnameOverride. + +#### Breaking changes + +- `affinty` has been renamed to `master.affinity` and `slave.affinity`. +- `tolerations` has been renamed to `master.tolerations` and `slave.tolerations`. +- `nodeSelector` has been renamed to `master.nodeSelector` and `slave.nodeSelector`. + +### To 2.0.0 + +In order to upgrade from the `0.X.X` branch to `1.X.X`, you should follow the below steps: + +- Obtain the service name (`SERVICE_NAME`) and password (`OLD_PASSWORD`) of the existing postgresql chart. You can find the instructions to obtain the password in the NOTES.txt, the service name can be obtained by running + +```console +$ kubectl get svc +``` + +- Install (not upgrade) the new version + +```console +$ helm repo update +$ helm install my-release bitnami/postgresql +``` + +- Connect to the new pod (you can obtain the name by running `kubectl get pods`): + +```console +$ kubectl exec -it NAME bash +``` + +- Once logged in, create a dump file from the previous database using `pg_dump`, for that we should connect to the previous postgresql chart: + +```console +$ pg_dump -h SERVICE_NAME -U postgres DATABASE_NAME > /tmp/backup.sql +``` + +After run above command you should be prompted for a password, this password is the previous chart password (`OLD_PASSWORD`). +This operation could take some time depending on the database size. + +- Once you have the backup file, you can restore it with a command like the one below: + +```console +$ psql -U postgres DATABASE_NAME < /tmp/backup.sql +``` + +In this case, you are accessing to the local postgresql, so the password should be the new one (you can find it in NOTES.txt). + +If you want to restore the database and the database schema does not exist, it is necessary to first follow the steps described below. + +```console +$ psql -U postgres +postgres=# drop database DATABASE_NAME; +postgres=# create database DATABASE_NAME; +postgres=# create user USER_NAME; +postgres=# alter role USER_NAME with password 'BITNAMI_USER_PASSWORD'; +postgres=# grant all privileges on database DATABASE_NAME to USER_NAME; +postgres=# alter database DATABASE_NAME owner to USER_NAME; +``` diff --git a/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/charts/postgresql/charts/common/.helmignore b/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/charts/postgresql/charts/common/.helmignore new file mode 100644 index 0000000000..50af031725 --- /dev/null +++ b/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/charts/postgresql/charts/common/.helmignore @@ -0,0 +1,22 @@ +# 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/ diff --git a/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/charts/postgresql/charts/common/Chart.yaml b/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/charts/postgresql/charts/common/Chart.yaml new file mode 100644 index 0000000000..bcc3808d08 --- /dev/null +++ b/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/charts/postgresql/charts/common/Chart.yaml @@ -0,0 +1,23 @@ +annotations: + category: Infrastructure +apiVersion: v2 +appVersion: 1.4.2 +description: A Library Helm Chart for grouping common logic between bitnami charts. + This chart is not deployable by itself. +home: https://github.com/bitnami/charts/tree/master/bitnami/common +icon: https://bitnami.com/downloads/logos/bitnami-mark.png +keywords: +- common +- helper +- template +- function +- bitnami +maintainers: +- email: containers@bitnami.com + name: Bitnami +name: common +sources: +- https://github.com/bitnami/charts +- http://www.bitnami.com/ +type: library +version: 1.4.2 diff --git a/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/charts/postgresql/charts/common/README.md b/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/charts/postgresql/charts/common/README.md new file mode 100644 index 0000000000..7287cbb5fc --- /dev/null +++ b/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/charts/postgresql/charts/common/README.md @@ -0,0 +1,322 @@ +# Bitnami Common Library Chart + +A [Helm Library Chart](https://helm.sh/docs/topics/library_charts/#helm) for grouping common logic between bitnami charts. + +## TL;DR + +```yaml +dependencies: + - name: common + version: 0.x.x + repository: https://charts.bitnami.com/bitnami +``` + +```bash +$ helm dependency update +``` + +```yaml +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ include "common.names.fullname" . }} +data: + myvalue: "Hello World" +``` + +## Introduction + +This chart provides a common template helpers which can be used to develop new charts using [Helm](https://helm.sh) package manager. + +Bitnami charts can be used with [Kubeapps](https://kubeapps.com/) for deployment and management of Helm Charts in clusters. This Helm chart has been tested on top of [Bitnami Kubernetes Production Runtime](https://kubeprod.io/) (BKPR). Deploy BKPR to get automated TLS certificates, logging and monitoring for your applications. + +## Prerequisites + +- Kubernetes 1.12+ +- Helm 3.1.0 + +## Parameters + +The following table lists the helpers available in the library which are scoped in different sections. + +### Affinities + +| Helper identifier | Description | Expected Input | +|-------------------------------|------------------------------------------------------|------------------------------------------------| +| `common.affinities.node.soft` | Return a soft nodeAffinity definition | `dict "key" "FOO" "values" (list "BAR" "BAZ")` | +| `common.affinities.node.hard` | Return a hard nodeAffinity definition | `dict "key" "FOO" "values" (list "BAR" "BAZ")` | +| `common.affinities.pod.soft` | Return a soft podAffinity/podAntiAffinity definition | `dict "component" "FOO" "context" $` | +| `common.affinities.pod.hard` | Return a hard podAffinity/podAntiAffinity definition | `dict "component" "FOO" "context" $` | + +### Capabilities + +| Helper identifier | Description | Expected Input | +|----------------------------------------------|------------------------------------------------------------------------------------------------|-------------------| +| `common.capabilities.kubeVersion` | Return the target Kubernetes version (using client default if .Values.kubeVersion is not set). | `.` Chart context | +| `common.capabilities.deployment.apiVersion` | Return the appropriate apiVersion for deployment. | `.` Chart context | +| `common.capabilities.statefulset.apiVersion` | Return the appropriate apiVersion for statefulset. | `.` Chart context | +| `common.capabilities.ingress.apiVersion` | Return the appropriate apiVersion for ingress. | `.` Chart context | +| `common.capabilities.rbac.apiVersion` | Return the appropriate apiVersion for RBAC resources. | `.` Chart context | +| `common.capabilities.crd.apiVersion` | Return the appropriate apiVersion for CRDs. | `.` Chart context | +| `common.capabilities.supportsHelmVersion` | Returns true if the used Helm version is 3.3+ | `.` Chart context | + +### Errors + +| Helper identifier | Description | Expected Input | +|-----------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------| +| `common.errors.upgrade.passwords.empty` | It will ensure required passwords are given when we are upgrading a chart. If `validationErrors` is not empty it will throw an error and will stop the upgrade action. | `dict "validationErrors" (list $validationError00 $validationError01) "context" $` | + +### Images + +| Helper identifier | Description | Expected Input | +|-----------------------------|------------------------------------------------------|---------------------------------------------------------------------------------------------------------| +| `common.images.image` | Return the proper and full image name | `dict "imageRoot" .Values.path.to.the.image "global" $`, see [ImageRoot](#imageroot) for the structure. | +| `common.images.pullSecrets` | Return the proper Docker Image Registry Secret Names | `dict "images" (list .Values.path.to.the.image1, .Values.path.to.the.image2) "global" .Values.global` | + +### Ingress + +| Helper identifier | Description | Expected Input | +|--------------------------|----------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `common.ingress.backend` | Generate a proper Ingress backend entry depending on the API version | `dict "serviceName" "foo" "servicePort" "bar"`, see the [Ingress deprecation notice](https://kubernetes.io/blog/2019/07/18/api-deprecations-in-1-16/) for the syntax differences | + +### Labels + +| Helper identifier | Description | Expected Input | +|-----------------------------|------------------------------------------------------|-------------------| +| `common.labels.standard` | Return Kubernetes standard labels | `.` Chart context | +| `common.labels.matchLabels` | Return the proper Docker Image Registry Secret Names | `.` Chart context | + +### Names + +| Helper identifier | Description | Expected Inpput | +|-------------------------|------------------------------------------------------------|-------------------| +| `common.names.name` | Expand the name of the chart or use `.Values.nameOverride` | `.` Chart context | +| `common.names.fullname` | Create a default fully qualified app name. | `.` Chart context | +| `common.names.chart` | Chart name plus version | `.` Chart context | + +### Secrets + +| Helper identifier | Description | Expected Input | +|---------------------------|--------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `common.secrets.name` | Generate the name of the secret. | `dict "existingSecret" .Values.path.to.the.existingSecret "defaultNameSuffix" "mySuffix" "context" $` see [ExistingSecret](#existingsecret) for the structure. | +| `common.secrets.key` | Generate secret key. | `dict "existingSecret" .Values.path.to.the.existingSecret "key" "keyName"` see [ExistingSecret](#existingsecret) for the structure. | +| `common.passwords.manage` | Generate secret password or retrieve one if already created. | `dict "secret" "secret-name" "key" "keyName" "providedValues" (list "path.to.password1" "path.to.password2") "length" 10 "strong" false "chartName" "chartName" "context" $`, length, strong and chartNAme fields are optional. | +| `common.secrets.exists` | Returns whether a previous generated secret already exists. | `dict "secret" "secret-name" "context" $` | + +### Storage + +| Helper identifier | Description | Expected Input | +|-------------------------------|---------------------------------------|---------------------------------------------------------------------------------------------------------------------| +| `common.affinities.node.soft` | Return a soft nodeAffinity definition | `dict "persistence" .Values.path.to.the.persistence "global" $`, see [Persistence](#persistence) for the structure. | + +### TplValues + +| Helper identifier | Description | Expected Input | +|---------------------------|----------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------| +| `common.tplvalues.render` | Renders a value that contains template | `dict "value" .Values.path.to.the.Value "context" $`, value is the value should rendered as template, context frequently is the chart context `$` or `.` | + +### Utils + +| Helper identifier | Description | Expected Input | +|--------------------------------|------------------------------------------------------------------------------------------|------------------------------------------------------------------------| +| `common.utils.fieldToEnvVar` | Build environment variable name given a field. | `dict "field" "my-password"` | +| `common.utils.secret.getvalue` | Print instructions to get a secret value. | `dict "secret" "secret-name" "field" "secret-value-field" "context" $` | +| `common.utils.getValueFromKey` | Gets a value from `.Values` object given its key path | `dict "key" "path.to.key" "context" $` | +| `common.utils.getKeyFromList` | Returns first `.Values` key with a defined value or first of the list if all non-defined | `dict "keys" (list "path.to.key1" "path.to.key2") "context" $` | + +### Validations + +| Helper identifier | Description | Expected Input | +|--------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `common.validations.values.single.empty` | Validate a value must not be empty. | `dict "valueKey" "path.to.value" "secret" "secret.name" "field" "my-password" "subchart" "subchart" "context" $` secret, field and subchart are optional. In case they are given, the helper will generate a how to get instruction. See [ValidateValue](#validatevalue) | +| `common.validations.values.multiple.empty` | Validate a multiple values must not be empty. It returns a shared error for all the values. | `dict "required" (list $validateValueConf00 $validateValueConf01) "context" $`. See [ValidateValue](#validatevalue) | +| `common.validations.values.mariadb.passwords` | This helper will ensure required password for MariaDB are not empty. It returns a shared error for all the values. | `dict "secret" "mariadb-secret" "subchart" "true" "context" $` subchart field is optional and could be true or false it depends on where you will use mariadb chart and the helper. | +| `common.validations.values.postgresql.passwords` | This helper will ensure required password for PostgreSQL are not empty. It returns a shared error for all the values. | `dict "secret" "postgresql-secret" "subchart" "true" "context" $` subchart field is optional and could be true or false it depends on where you will use postgresql chart and the helper. | +| `common.validations.values.redis.passwords` | This helper will ensure required password for RedisTM are not empty. It returns a shared error for all the values. | `dict "secret" "redis-secret" "subchart" "true" "context" $` subchart field is optional and could be true or false it depends on where you will use redis chart and the helper. | +| `common.validations.values.cassandra.passwords` | This helper will ensure required password for Cassandra are not empty. It returns a shared error for all the values. | `dict "secret" "cassandra-secret" "subchart" "true" "context" $` subchart field is optional and could be true or false it depends on where you will use cassandra chart and the helper. | +| `common.validations.values.mongodb.passwords` | This helper will ensure required password for MongoDB® are not empty. It returns a shared error for all the values. | `dict "secret" "mongodb-secret" "subchart" "true" "context" $` subchart field is optional and could be true or false it depends on where you will use mongodb chart and the helper. | + +### Warnings + +| Helper identifier | Description | Expected Input | +|------------------------------|----------------------------------|------------------------------------------------------------| +| `common.warnings.rollingTag` | Warning about using rolling tag. | `ImageRoot` see [ImageRoot](#imageroot) for the structure. | + +## Special input schemas + +### ImageRoot + +```yaml +registry: + type: string + description: Docker registry where the image is located + example: docker.io + +repository: + type: string + description: Repository and image name + example: bitnami/nginx + +tag: + type: string + description: image tag + example: 1.16.1-debian-10-r63 + +pullPolicy: + type: string + description: Specify a imagePullPolicy. Defaults to 'Always' if image tag is 'latest', else set to 'IfNotPresent' + +pullSecrets: + type: array + items: + type: string + description: Optionally specify an array of imagePullSecrets. + +debug: + type: boolean + description: Set to true if you would like to see extra information on logs + example: false + +## An instance would be: +# registry: docker.io +# repository: bitnami/nginx +# tag: 1.16.1-debian-10-r63 +# pullPolicy: IfNotPresent +# debug: false +``` + +### Persistence + +```yaml +enabled: + type: boolean + description: Whether enable persistence. + example: true + +storageClass: + type: string + description: Ghost data Persistent Volume Storage Class, If set to "-", storageClassName: "" which disables dynamic provisioning. + example: "-" + +accessMode: + type: string + description: Access mode for the Persistent Volume Storage. + example: ReadWriteOnce + +size: + type: string + description: Size the Persistent Volume Storage. + example: 8Gi + +path: + type: string + description: Path to be persisted. + example: /bitnami + +## An instance would be: +# enabled: true +# storageClass: "-" +# accessMode: ReadWriteOnce +# size: 8Gi +# path: /bitnami +``` + +### ExistingSecret + +```yaml +name: + type: string + description: Name of the existing secret. + example: mySecret +keyMapping: + description: Mapping between the expected key name and the name of the key in the existing secret. + type: object + +## An instance would be: +# name: mySecret +# keyMapping: +# password: myPasswordKey +``` + +#### Example of use + +When we store sensitive data for a deployment in a secret, some times we want to give to users the possibility of using theirs existing secrets. + +```yaml +# templates/secret.yaml +--- +apiVersion: v1 +kind: Secret +metadata: + name: {{ include "common.names.fullname" . }} + labels: + app: {{ include "common.names.fullname" . }} +type: Opaque +data: + password: {{ .Values.password | b64enc | quote }} + +# templates/dpl.yaml +--- +... + env: + - name: PASSWORD + valueFrom: + secretKeyRef: + name: {{ include "common.secrets.name" (dict "existingSecret" .Values.existingSecret "context" $) }} + key: {{ include "common.secrets.key" (dict "existingSecret" .Values.existingSecret "key" "password") }} +... + +# values.yaml +--- +name: mySecret +keyMapping: + password: myPasswordKey +``` + +### ValidateValue + +#### NOTES.txt + +```console +{{- $validateValueConf00 := (dict "valueKey" "path.to.value00" "secret" "secretName" "field" "password-00") -}} +{{- $validateValueConf01 := (dict "valueKey" "path.to.value01" "secret" "secretName" "field" "password-01") -}} + +{{ include "common.validations.values.multiple.empty" (dict "required" (list $validateValueConf00 $validateValueConf01) "context" $) }} +``` + +If we force those values to be empty we will see some alerts + +```console +$ helm install test mychart --set path.to.value00="",path.to.value01="" + 'path.to.value00' must not be empty, please add '--set path.to.value00=$PASSWORD_00' to the command. To get the current value: + + export PASSWORD_00=$(kubectl get secret --namespace default secretName -o jsonpath="{.data.password-00}" | base64 --decode) + + 'path.to.value01' must not be empty, please add '--set path.to.value01=$PASSWORD_01' to the command. To get the current value: + + export PASSWORD_01=$(kubectl get secret --namespace default secretName -o jsonpath="{.data.password-01}" | base64 --decode) +``` + +## Upgrading + +### To 1.0.0 + +[On November 13, 2020, Helm v2 support was formally finished](https://github.com/helm/charts#status-of-the-project), this major version is the result of the required changes applied to the Helm Chart to be able to incorporate the different features added in Helm v3 and to be consistent with the Helm project itself regarding the Helm v2 EOL. + +**What changes were introduced in this major version?** + +- Previous versions of this Helm Chart use `apiVersion: v1` (installable by both Helm 2 and 3), this Helm Chart was updated to `apiVersion: v2` (installable by Helm 3 only). [Here](https://helm.sh/docs/topics/charts/#the-apiversion-field) you can find more information about the `apiVersion` field. +- Use `type: library`. [Here](https://v3.helm.sh/docs/faq/#library-chart-support) you can find more information. +- The different fields present in the *Chart.yaml* file has been ordered alphabetically in a homogeneous way for all the Bitnami Helm Charts + +**Considerations when upgrading to this version** + +- If you want to upgrade to this version from a previous one installed with Helm v3, you shouldn't face any issues +- If you want to upgrade to this version using Helm v2, this scenario is not supported as this version doesn't support Helm v2 anymore +- If you installed the previous version with Helm v2 and wants to upgrade to this version with Helm v3, please refer to the [official Helm documentation](https://helm.sh/docs/topics/v2_v3_migration/#migration-use-cases) about migrating from Helm v2 to v3 + +**Useful links** + +- https://docs.bitnami.com/tutorials/resolve-helm2-helm3-post-migration-issues/ +- https://helm.sh/docs/topics/v2_v3_migration/ +- https://helm.sh/blog/migrate-from-helm-v2-to-helm-v3/ diff --git a/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/charts/postgresql/charts/common/templates/_affinities.tpl b/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/charts/postgresql/charts/common/templates/_affinities.tpl new file mode 100644 index 0000000000..493a6dc7e4 --- /dev/null +++ b/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/charts/postgresql/charts/common/templates/_affinities.tpl @@ -0,0 +1,94 @@ +{{/* vim: set filetype=mustache: */}} + +{{/* +Return a soft nodeAffinity definition +{{ include "common.affinities.nodes.soft" (dict "key" "FOO" "values" (list "BAR" "BAZ")) -}} +*/}} +{{- define "common.affinities.nodes.soft" -}} +preferredDuringSchedulingIgnoredDuringExecution: + - preference: + matchExpressions: + - key: {{ .key }} + operator: In + values: + {{- range .values }} + - {{ . }} + {{- end }} + weight: 1 +{{- end -}} + +{{/* +Return a hard nodeAffinity definition +{{ include "common.affinities.nodes.hard" (dict "key" "FOO" "values" (list "BAR" "BAZ")) -}} +*/}} +{{- define "common.affinities.nodes.hard" -}} +requiredDuringSchedulingIgnoredDuringExecution: + nodeSelectorTerms: + - matchExpressions: + - key: {{ .key }} + operator: In + values: + {{- range .values }} + - {{ . }} + {{- end }} +{{- end -}} + +{{/* +Return a nodeAffinity definition +{{ include "common.affinities.nodes" (dict "type" "soft" "key" "FOO" "values" (list "BAR" "BAZ")) -}} +*/}} +{{- define "common.affinities.nodes" -}} + {{- if eq .type "soft" }} + {{- include "common.affinities.nodes.soft" . -}} + {{- else if eq .type "hard" }} + {{- include "common.affinities.nodes.hard" . -}} + {{- end -}} +{{- end -}} + +{{/* +Return a soft podAffinity/podAntiAffinity definition +{{ include "common.affinities.pods.soft" (dict "component" "FOO" "context" $) -}} +*/}} +{{- define "common.affinities.pods.soft" -}} +{{- $component := default "" .component -}} +preferredDuringSchedulingIgnoredDuringExecution: + - podAffinityTerm: + labelSelector: + matchLabels: {{- (include "common.labels.matchLabels" .context) | nindent 10 }} + {{- if not (empty $component) }} + {{ printf "app.kubernetes.io/component: %s" $component }} + {{- end }} + namespaces: + - {{ .context.Release.Namespace | quote }} + topologyKey: kubernetes.io/hostname + weight: 1 +{{- end -}} + +{{/* +Return a hard podAffinity/podAntiAffinity definition +{{ include "common.affinities.pods.hard" (dict "component" "FOO" "context" $) -}} +*/}} +{{- define "common.affinities.pods.hard" -}} +{{- $component := default "" .component -}} +requiredDuringSchedulingIgnoredDuringExecution: + - labelSelector: + matchLabels: {{- (include "common.labels.matchLabels" .context) | nindent 8 }} + {{- if not (empty $component) }} + {{ printf "app.kubernetes.io/component: %s" $component }} + {{- end }} + namespaces: + - {{ .context.Release.Namespace | quote }} + topologyKey: kubernetes.io/hostname +{{- end -}} + +{{/* +Return a podAffinity/podAntiAffinity definition +{{ include "common.affinities.pods" (dict "type" "soft" "key" "FOO" "values" (list "BAR" "BAZ")) -}} +*/}} +{{- define "common.affinities.pods" -}} + {{- if eq .type "soft" }} + {{- include "common.affinities.pods.soft" . -}} + {{- else if eq .type "hard" }} + {{- include "common.affinities.pods.hard" . -}} + {{- end -}} +{{- end -}} diff --git a/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/charts/postgresql/charts/common/templates/_capabilities.tpl b/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/charts/postgresql/charts/common/templates/_capabilities.tpl new file mode 100644 index 0000000000..4dde56a38d --- /dev/null +++ b/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/charts/postgresql/charts/common/templates/_capabilities.tpl @@ -0,0 +1,95 @@ +{{/* vim: set filetype=mustache: */}} + +{{/* +Return the target Kubernetes version +*/}} +{{- define "common.capabilities.kubeVersion" -}} +{{- if .Values.global }} + {{- if .Values.global.kubeVersion }} + {{- .Values.global.kubeVersion -}} + {{- else }} + {{- default .Capabilities.KubeVersion.Version .Values.kubeVersion -}} + {{- end -}} +{{- else }} +{{- default .Capabilities.KubeVersion.Version .Values.kubeVersion -}} +{{- end -}} +{{- end -}} + +{{/* +Return the appropriate apiVersion for deployment. +*/}} +{{- define "common.capabilities.deployment.apiVersion" -}} +{{- if semverCompare "<1.14-0" (include "common.capabilities.kubeVersion" .) -}} +{{- print "extensions/v1beta1" -}} +{{- else -}} +{{- print "apps/v1" -}} +{{- end -}} +{{- end -}} + +{{/* +Return the appropriate apiVersion for statefulset. +*/}} +{{- define "common.capabilities.statefulset.apiVersion" -}} +{{- if semverCompare "<1.14-0" (include "common.capabilities.kubeVersion" .) -}} +{{- print "apps/v1beta1" -}} +{{- else -}} +{{- print "apps/v1" -}} +{{- end -}} +{{- end -}} + +{{/* +Return the appropriate apiVersion for ingress. +*/}} +{{- define "common.capabilities.ingress.apiVersion" -}} +{{- if .Values.ingress -}} +{{- if .Values.ingress.apiVersion -}} +{{- .Values.ingress.apiVersion -}} +{{- else if semverCompare "<1.14-0" (include "common.capabilities.kubeVersion" .) -}} +{{- print "extensions/v1beta1" -}} +{{- else if semverCompare "<1.19-0" (include "common.capabilities.kubeVersion" .) -}} +{{- print "networking.k8s.io/v1beta1" -}} +{{- else -}} +{{- print "networking.k8s.io/v1" -}} +{{- end }} +{{- else if semverCompare "<1.14-0" (include "common.capabilities.kubeVersion" .) -}} +{{- print "extensions/v1beta1" -}} +{{- else if semverCompare "<1.19-0" (include "common.capabilities.kubeVersion" .) -}} +{{- print "networking.k8s.io/v1beta1" -}} +{{- else -}} +{{- print "networking.k8s.io/v1" -}} +{{- end -}} +{{- end -}} + +{{/* +Return the appropriate apiVersion for RBAC resources. +*/}} +{{- define "common.capabilities.rbac.apiVersion" -}} +{{- if semverCompare "<1.17-0" (include "common.capabilities.kubeVersion" .) -}} +{{- print "rbac.authorization.k8s.io/v1beta1" -}} +{{- else -}} +{{- print "rbac.authorization.k8s.io/v1" -}} +{{- end -}} +{{- end -}} + +{{/* +Return the appropriate apiVersion for CRDs. +*/}} +{{- define "common.capabilities.crd.apiVersion" -}} +{{- if semverCompare "<1.19-0" (include "common.capabilities.kubeVersion" .) -}} +{{- print "apiextensions.k8s.io/v1beta1" -}} +{{- else -}} +{{- print "apiextensions.k8s.io/v1" -}} +{{- end -}} +{{- end -}} + +{{/* +Returns true if the used Helm version is 3.3+. +A way to check the used Helm version was not introduced until version 3.3.0 with .Capabilities.HelmVersion, which contains an additional "{}}" structure. +This check is introduced as a regexMatch instead of {{ if .Capabilities.HelmVersion }} because checking for the key HelmVersion in <3.3 results in a "interface not found" error. +**To be removed when the catalog's minimun Helm version is 3.3** +*/}} +{{- define "common.capabilities.supportsHelmVersion" -}} +{{- if regexMatch "{(v[0-9])*[^}]*}}$" (.Capabilities | toString ) }} + {{- true -}} +{{- end -}} +{{- end -}} diff --git a/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/charts/postgresql/charts/common/templates/_errors.tpl b/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/charts/postgresql/charts/common/templates/_errors.tpl new file mode 100644 index 0000000000..a79cc2e322 --- /dev/null +++ b/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/charts/postgresql/charts/common/templates/_errors.tpl @@ -0,0 +1,23 @@ +{{/* vim: set filetype=mustache: */}} +{{/* +Through error when upgrading using empty passwords values that must not be empty. + +Usage: +{{- $validationError00 := include "common.validations.values.single.empty" (dict "valueKey" "path.to.password00" "secret" "secretName" "field" "password-00") -}} +{{- $validationError01 := include "common.validations.values.single.empty" (dict "valueKey" "path.to.password01" "secret" "secretName" "field" "password-01") -}} +{{ include "common.errors.upgrade.passwords.empty" (dict "validationErrors" (list $validationError00 $validationError01) "context" $) }} + +Required password params: + - validationErrors - String - Required. List of validation strings to be return, if it is empty it won't throw error. + - context - Context - Required. Parent context. +*/}} +{{- define "common.errors.upgrade.passwords.empty" -}} + {{- $validationErrors := join "" .validationErrors -}} + {{- if and $validationErrors .context.Release.IsUpgrade -}} + {{- $errorString := "\nPASSWORDS ERROR: You must provide your current passwords when upgrading the release." -}} + {{- $errorString = print $errorString "\n Note that even after reinstallation, old credentials may be needed as they may be kept in persistent volume claims." -}} + {{- $errorString = print $errorString "\n Further information can be obtained at https://docs.bitnami.com/general/how-to/troubleshoot-helm-chart-issues/#credential-errors-while-upgrading-chart-releases" -}} + {{- $errorString = print $errorString "\n%s" -}} + {{- printf $errorString $validationErrors | fail -}} + {{- end -}} +{{- end -}} diff --git a/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/charts/postgresql/charts/common/templates/_images.tpl b/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/charts/postgresql/charts/common/templates/_images.tpl new file mode 100644 index 0000000000..60f04fd6e2 --- /dev/null +++ b/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/charts/postgresql/charts/common/templates/_images.tpl @@ -0,0 +1,47 @@ +{{/* vim: set filetype=mustache: */}} +{{/* +Return the proper image name +{{ include "common.images.image" ( dict "imageRoot" .Values.path.to.the.image "global" $) }} +*/}} +{{- define "common.images.image" -}} +{{- $registryName := .imageRoot.registry -}} +{{- $repositoryName := .imageRoot.repository -}} +{{- $tag := .imageRoot.tag | toString -}} +{{- if .global }} + {{- if .global.imageRegistry }} + {{- $registryName = .global.imageRegistry -}} + {{- end -}} +{{- end -}} +{{- if $registryName }} +{{- printf "%s/%s:%s" $registryName $repositoryName $tag -}} +{{- else -}} +{{- printf "%s:%s" $repositoryName $tag -}} +{{- end -}} +{{- end -}} + +{{/* +Return the proper Docker Image Registry Secret Names +{{ include "common.images.pullSecrets" ( dict "images" (list .Values.path.to.the.image1, .Values.path.to.the.image2) "global" .Values.global) }} +*/}} +{{- define "common.images.pullSecrets" -}} + {{- $pullSecrets := list }} + + {{- if .global }} + {{- range .global.imagePullSecrets -}} + {{- $pullSecrets = append $pullSecrets . -}} + {{- end -}} + {{- end -}} + + {{- range .images -}} + {{- range .pullSecrets -}} + {{- $pullSecrets = append $pullSecrets . -}} + {{- end -}} + {{- end -}} + + {{- if (not (empty $pullSecrets)) }} +imagePullSecrets: + {{- range $pullSecrets }} + - name: {{ . }} + {{- end }} + {{- end }} +{{- end -}} diff --git a/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/charts/postgresql/charts/common/templates/_ingress.tpl b/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/charts/postgresql/charts/common/templates/_ingress.tpl new file mode 100644 index 0000000000..622ef50e3c --- /dev/null +++ b/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/charts/postgresql/charts/common/templates/_ingress.tpl @@ -0,0 +1,42 @@ +{{/* vim: set filetype=mustache: */}} + +{{/* +Generate backend entry that is compatible with all Kubernetes API versions. + +Usage: +{{ include "common.ingress.backend" (dict "serviceName" "backendName" "servicePort" "backendPort" "context" $) }} + +Params: + - serviceName - String. Name of an existing service backend + - servicePort - String/Int. Port name (or number) of the service. It will be translated to different yaml depending if it is a string or an integer. + - context - Dict - Required. The context for the template evaluation. +*/}} +{{- define "common.ingress.backend" -}} +{{- $apiVersion := (include "common.capabilities.ingress.apiVersion" .context) -}} +{{- if or (eq $apiVersion "extensions/v1beta1") (eq $apiVersion "networking.k8s.io/v1beta1") -}} +serviceName: {{ .serviceName }} +servicePort: {{ .servicePort }} +{{- else -}} +service: + name: {{ .serviceName }} + port: + {{- if typeIs "string" .servicePort }} + name: {{ .servicePort }} + {{- else if typeIs "int" .servicePort }} + number: {{ .servicePort }} + {{- end }} +{{- end -}} +{{- end -}} + +{{/* +Print "true" if the API pathType field is supported +Usage: +{{ include "common.ingress.supportsPathType" . }} +*/}} +{{- define "common.ingress.supportsPathType" -}} +{{- if (semverCompare "<1.18-0" (include "common.capabilities.kubeVersion" .)) -}} +{{- print "false" -}} +{{- else -}} +{{- print "true" -}} +{{- end -}} +{{- end -}} diff --git a/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/charts/postgresql/charts/common/templates/_labels.tpl b/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/charts/postgresql/charts/common/templates/_labels.tpl new file mode 100644 index 0000000000..252066c7e2 --- /dev/null +++ b/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/charts/postgresql/charts/common/templates/_labels.tpl @@ -0,0 +1,18 @@ +{{/* vim: set filetype=mustache: */}} +{{/* +Kubernetes standard labels +*/}} +{{- define "common.labels.standard" -}} +app.kubernetes.io/name: {{ include "common.names.name" . }} +helm.sh/chart: {{ include "common.names.chart" . }} +app.kubernetes.io/instance: {{ .Release.Name }} +app.kubernetes.io/managed-by: {{ .Release.Service }} +{{- end -}} + +{{/* +Labels to use on deploy.spec.selector.matchLabels and svc.spec.selector +*/}} +{{- define "common.labels.matchLabels" -}} +app.kubernetes.io/name: {{ include "common.names.name" . }} +app.kubernetes.io/instance: {{ .Release.Name }} +{{- end -}} diff --git a/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/charts/postgresql/charts/common/templates/_names.tpl b/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/charts/postgresql/charts/common/templates/_names.tpl new file mode 100644 index 0000000000..adf2a74f48 --- /dev/null +++ b/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/charts/postgresql/charts/common/templates/_names.tpl @@ -0,0 +1,32 @@ +{{/* vim: set filetype=mustache: */}} +{{/* +Expand the name of the chart. +*/}} +{{- define "common.names.name" -}} +{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}} +{{- end -}} + +{{/* +Create chart name and version as used by the chart label. +*/}} +{{- define "common.names.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}} +{{- end -}} + +{{/* +Create a default fully qualified app name. +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +If release name contains chart name it will be used as a full name. +*/}} +{{- define "common.names.fullname" -}} +{{- if .Values.fullnameOverride -}} +{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" -}} +{{- else -}} +{{- $name := default .Chart.Name .Values.nameOverride -}} +{{- if contains $name .Release.Name -}} +{{- .Release.Name | trunc 63 | trimSuffix "-" -}} +{{- else -}} +{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}} +{{- end -}} +{{- end -}} +{{- end -}} diff --git a/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/charts/postgresql/charts/common/templates/_secrets.tpl b/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/charts/postgresql/charts/common/templates/_secrets.tpl new file mode 100644 index 0000000000..60b84a7019 --- /dev/null +++ b/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/charts/postgresql/charts/common/templates/_secrets.tpl @@ -0,0 +1,129 @@ +{{/* vim: set filetype=mustache: */}} +{{/* +Generate secret name. + +Usage: +{{ include "common.secrets.name" (dict "existingSecret" .Values.path.to.the.existingSecret "defaultNameSuffix" "mySuffix" "context" $) }} + +Params: + - existingSecret - ExistingSecret/String - Optional. The path to the existing secrets in the values.yaml given by the user + to be used instead of the default one. Allows for it to be of type String (just the secret name) for backwards compatibility. + +info: https://github.com/bitnami/charts/tree/master/bitnami/common#existingsecret + - defaultNameSuffix - String - Optional. It is used only if we have several secrets in the same deployment. + - context - Dict - Required. The context for the template evaluation. +*/}} +{{- define "common.secrets.name" -}} +{{- $name := (include "common.names.fullname" .context) -}} + +{{- if .defaultNameSuffix -}} +{{- $name = printf "%s-%s" $name .defaultNameSuffix | trunc 63 | trimSuffix "-" -}} +{{- end -}} + +{{- with .existingSecret -}} +{{- if not (typeIs "string" .) -}} +{{- with .name -}} +{{- $name = . -}} +{{- end -}} +{{- else -}} +{{- $name = . -}} +{{- end -}} +{{- end -}} + +{{- printf "%s" $name -}} +{{- end -}} + +{{/* +Generate secret key. + +Usage: +{{ include "common.secrets.key" (dict "existingSecret" .Values.path.to.the.existingSecret "key" "keyName") }} + +Params: + - existingSecret - ExistingSecret/String - Optional. The path to the existing secrets in the values.yaml given by the user + to be used instead of the default one. Allows for it to be of type String (just the secret name) for backwards compatibility. + +info: https://github.com/bitnami/charts/tree/master/bitnami/common#existingsecret + - key - String - Required. Name of the key in the secret. +*/}} +{{- define "common.secrets.key" -}} +{{- $key := .key -}} + +{{- if .existingSecret -}} + {{- if not (typeIs "string" .existingSecret) -}} + {{- if .existingSecret.keyMapping -}} + {{- $key = index .existingSecret.keyMapping $.key -}} + {{- end -}} + {{- end }} +{{- end -}} + +{{- printf "%s" $key -}} +{{- end -}} + +{{/* +Generate secret password or retrieve one if already created. + +Usage: +{{ include "common.secrets.passwords.manage" (dict "secret" "secret-name" "key" "keyName" "providedValues" (list "path.to.password1" "path.to.password2") "length" 10 "strong" false "chartName" "chartName" "context" $) }} + +Params: + - secret - String - Required - Name of the 'Secret' resource where the password is stored. + - key - String - Required - Name of the key in the secret. + - providedValues - List - Required - The path to the validating value in the values.yaml, e.g: "mysql.password". Will pick first parameter with a defined value. + - length - int - Optional - Length of the generated random password. + - strong - Boolean - Optional - Whether to add symbols to the generated random password. + - chartName - String - Optional - Name of the chart used when said chart is deployed as a subchart. + - context - Context - Required - Parent context. +*/}} +{{- define "common.secrets.passwords.manage" -}} + +{{- $password := "" }} +{{- $subchart := "" }} +{{- $chartName := default "" .chartName }} +{{- $passwordLength := default 10 .length }} +{{- $providedPasswordKey := include "common.utils.getKeyFromList" (dict "keys" .providedValues "context" $.context) }} +{{- $providedPasswordValue := include "common.utils.getValueFromKey" (dict "key" $providedPasswordKey "context" $.context) }} +{{- $secret := (lookup "v1" "Secret" $.context.Release.Namespace .secret) }} +{{- if $secret }} + {{- if index $secret.data .key }} + {{- $password = index $secret.data .key }} + {{- end -}} +{{- else if $providedPasswordValue }} + {{- $password = $providedPasswordValue | toString | b64enc | quote }} +{{- else }} + + {{- if .context.Values.enabled }} + {{- $subchart = $chartName }} + {{- end -}} + + {{- $requiredPassword := dict "valueKey" $providedPasswordKey "secret" .secret "field" .key "subchart" $subchart "context" $.context -}} + {{- $requiredPasswordError := include "common.validations.values.single.empty" $requiredPassword -}} + {{- $passwordValidationErrors := list $requiredPasswordError -}} + {{- include "common.errors.upgrade.passwords.empty" (dict "validationErrors" $passwordValidationErrors "context" $.context) -}} + + {{- if .strong }} + {{- $subStr := list (lower (randAlpha 1)) (randNumeric 1) (upper (randAlpha 1)) | join "_" }} + {{- $password = randAscii $passwordLength }} + {{- $password = regexReplaceAllLiteral "\\W" $password "@" | substr 5 $passwordLength }} + {{- $password = printf "%s%s" $subStr $password | toString | shuffle | b64enc | quote }} + {{- else }} + {{- $password = randAlphaNum $passwordLength | b64enc | quote }} + {{- end }} +{{- end -}} +{{- printf "%s" $password -}} +{{- end -}} + +{{/* +Returns whether a previous generated secret already exists + +Usage: +{{ include "common.secrets.exists" (dict "secret" "secret-name" "context" $) }} + +Params: + - secret - String - Required - Name of the 'Secret' resource where the password is stored. + - context - Context - Required - Parent context. +*/}} +{{- define "common.secrets.exists" -}} +{{- $secret := (lookup "v1" "Secret" $.context.Release.Namespace .secret) }} +{{- if $secret }} + {{- true -}} +{{- end -}} +{{- end -}} diff --git a/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/charts/postgresql/charts/common/templates/_storage.tpl b/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/charts/postgresql/charts/common/templates/_storage.tpl new file mode 100644 index 0000000000..60e2a844f6 --- /dev/null +++ b/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/charts/postgresql/charts/common/templates/_storage.tpl @@ -0,0 +1,23 @@ +{{/* vim: set filetype=mustache: */}} +{{/* +Return the proper Storage Class +{{ include "common.storage.class" ( dict "persistence" .Values.path.to.the.persistence "global" $) }} +*/}} +{{- define "common.storage.class" -}} + +{{- $storageClass := .persistence.storageClass -}} +{{- if .global -}} + {{- if .global.storageClass -}} + {{- $storageClass = .global.storageClass -}} + {{- end -}} +{{- end -}} + +{{- if $storageClass -}} + {{- if (eq "-" $storageClass) -}} + {{- printf "storageClassName: \"\"" -}} + {{- else }} + {{- printf "storageClassName: %s" $storageClass -}} + {{- end -}} +{{- end -}} + +{{- end -}} diff --git a/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/charts/postgresql/charts/common/templates/_tplvalues.tpl b/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/charts/postgresql/charts/common/templates/_tplvalues.tpl new file mode 100644 index 0000000000..2db166851b --- /dev/null +++ b/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/charts/postgresql/charts/common/templates/_tplvalues.tpl @@ -0,0 +1,13 @@ +{{/* vim: set filetype=mustache: */}} +{{/* +Renders a value that contains template. +Usage: +{{ include "common.tplvalues.render" ( dict "value" .Values.path.to.the.Value "context" $) }} +*/}} +{{- define "common.tplvalues.render" -}} + {{- if typeIs "string" .value }} + {{- tpl .value .context }} + {{- else }} + {{- tpl (.value | toYaml) .context }} + {{- end }} +{{- end -}} diff --git a/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/charts/postgresql/charts/common/templates/_utils.tpl b/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/charts/postgresql/charts/common/templates/_utils.tpl new file mode 100644 index 0000000000..ea083a249f --- /dev/null +++ b/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/charts/postgresql/charts/common/templates/_utils.tpl @@ -0,0 +1,62 @@ +{{/* vim: set filetype=mustache: */}} +{{/* +Print instructions to get a secret value. +Usage: +{{ include "common.utils.secret.getvalue" (dict "secret" "secret-name" "field" "secret-value-field" "context" $) }} +*/}} +{{- define "common.utils.secret.getvalue" -}} +{{- $varname := include "common.utils.fieldToEnvVar" . -}} +export {{ $varname }}=$(kubectl get secret --namespace {{ .context.Release.Namespace | quote }} {{ .secret }} -o jsonpath="{.data.{{ .field }}}" | base64 --decode) +{{- end -}} + +{{/* +Build env var name given a field +Usage: +{{ include "common.utils.fieldToEnvVar" dict "field" "my-password" }} +*/}} +{{- define "common.utils.fieldToEnvVar" -}} + {{- $fieldNameSplit := splitList "-" .field -}} + {{- $upperCaseFieldNameSplit := list -}} + + {{- range $fieldNameSplit -}} + {{- $upperCaseFieldNameSplit = append $upperCaseFieldNameSplit ( upper . ) -}} + {{- end -}} + + {{ join "_" $upperCaseFieldNameSplit }} +{{- end -}} + +{{/* +Gets a value from .Values given +Usage: +{{ include "common.utils.getValueFromKey" (dict "key" "path.to.key" "context" $) }} +*/}} +{{- define "common.utils.getValueFromKey" -}} +{{- $splitKey := splitList "." .key -}} +{{- $value := "" -}} +{{- $latestObj := $.context.Values -}} +{{- range $splitKey -}} + {{- if not $latestObj -}} + {{- printf "please review the entire path of '%s' exists in values" $.key | fail -}} + {{- end -}} + {{- $value = ( index $latestObj . ) -}} + {{- $latestObj = $value -}} +{{- end -}} +{{- printf "%v" (default "" $value) -}} +{{- end -}} + +{{/* +Returns first .Values key with a defined value or first of the list if all non-defined +Usage: +{{ include "common.utils.getKeyFromList" (dict "keys" (list "path.to.key1" "path.to.key2") "context" $) }} +*/}} +{{- define "common.utils.getKeyFromList" -}} +{{- $key := first .keys -}} +{{- $reverseKeys := reverse .keys }} +{{- range $reverseKeys }} + {{- $value := include "common.utils.getValueFromKey" (dict "key" . "context" $.context ) }} + {{- if $value -}} + {{- $key = . }} + {{- end -}} +{{- end -}} +{{- printf "%s" $key -}} +{{- end -}} diff --git a/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/charts/postgresql/charts/common/templates/_warnings.tpl b/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/charts/postgresql/charts/common/templates/_warnings.tpl new file mode 100644 index 0000000000..ae10fa41ee --- /dev/null +++ b/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/charts/postgresql/charts/common/templates/_warnings.tpl @@ -0,0 +1,14 @@ +{{/* vim: set filetype=mustache: */}} +{{/* +Warning about using rolling tag. +Usage: +{{ include "common.warnings.rollingTag" .Values.path.to.the.imageRoot }} +*/}} +{{- define "common.warnings.rollingTag" -}} + +{{- if and (contains "bitnami/" .repository) (not (.tag | toString | regexFind "-r\\d+$|sha256:")) }} +WARNING: Rolling tag detected ({{ .repository }}:{{ .tag }}), please note that it is strongly recommended to avoid using rolling tags in a production environment. ++info https://docs.bitnami.com/containers/how-to/understand-rolling-tags-containers/ +{{- end }} + +{{- end -}} diff --git a/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/charts/postgresql/charts/common/templates/validations/_cassandra.tpl b/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/charts/postgresql/charts/common/templates/validations/_cassandra.tpl new file mode 100644 index 0000000000..8679ddffb1 --- /dev/null +++ b/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/charts/postgresql/charts/common/templates/validations/_cassandra.tpl @@ -0,0 +1,72 @@ +{{/* vim: set filetype=mustache: */}} +{{/* +Validate Cassandra required passwords are not empty. + +Usage: +{{ include "common.validations.values.cassandra.passwords" (dict "secret" "secretName" "subchart" false "context" $) }} +Params: + - secret - String - Required. Name of the secret where Cassandra values are stored, e.g: "cassandra-passwords-secret" + - subchart - Boolean - Optional. Whether Cassandra is used as subchart or not. Default: false +*/}} +{{- define "common.validations.values.cassandra.passwords" -}} + {{- $existingSecret := include "common.cassandra.values.existingSecret" . -}} + {{- $enabled := include "common.cassandra.values.enabled" . -}} + {{- $dbUserPrefix := include "common.cassandra.values.key.dbUser" . -}} + {{- $valueKeyPassword := printf "%s.password" $dbUserPrefix -}} + + {{- if and (not $existingSecret) (eq $enabled "true") -}} + {{- $requiredPasswords := list -}} + + {{- $requiredPassword := dict "valueKey" $valueKeyPassword "secret" .secret "field" "cassandra-password" -}} + {{- $requiredPasswords = append $requiredPasswords $requiredPassword -}} + + {{- include "common.validations.values.multiple.empty" (dict "required" $requiredPasswords "context" .context) -}} + + {{- end -}} +{{- end -}} + +{{/* +Auxiliary function to get the right value for existingSecret. + +Usage: +{{ include "common.cassandra.values.existingSecret" (dict "context" $) }} +Params: + - subchart - Boolean - Optional. Whether Cassandra is used as subchart or not. Default: false +*/}} +{{- define "common.cassandra.values.existingSecret" -}} + {{- if .subchart -}} + {{- .context.Values.cassandra.dbUser.existingSecret | quote -}} + {{- else -}} + {{- .context.Values.dbUser.existingSecret | quote -}} + {{- end -}} +{{- end -}} + +{{/* +Auxiliary function to get the right value for enabled cassandra. + +Usage: +{{ include "common.cassandra.values.enabled" (dict "context" $) }} +*/}} +{{- define "common.cassandra.values.enabled" -}} + {{- if .subchart -}} + {{- printf "%v" .context.Values.cassandra.enabled -}} + {{- else -}} + {{- printf "%v" (not .context.Values.enabled) -}} + {{- end -}} +{{- end -}} + +{{/* +Auxiliary function to get the right value for the key dbUser + +Usage: +{{ include "common.cassandra.values.key.dbUser" (dict "subchart" "true" "context" $) }} +Params: + - subchart - Boolean - Optional. Whether Cassandra is used as subchart or not. Default: false +*/}} +{{- define "common.cassandra.values.key.dbUser" -}} + {{- if .subchart -}} + cassandra.dbUser + {{- else -}} + dbUser + {{- end -}} +{{- end -}} diff --git a/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/charts/postgresql/charts/common/templates/validations/_mariadb.tpl b/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/charts/postgresql/charts/common/templates/validations/_mariadb.tpl new file mode 100644 index 0000000000..bb5ed7253d --- /dev/null +++ b/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/charts/postgresql/charts/common/templates/validations/_mariadb.tpl @@ -0,0 +1,103 @@ +{{/* vim: set filetype=mustache: */}} +{{/* +Validate MariaDB required passwords are not empty. + +Usage: +{{ include "common.validations.values.mariadb.passwords" (dict "secret" "secretName" "subchart" false "context" $) }} +Params: + - secret - String - Required. Name of the secret where MariaDB values are stored, e.g: "mysql-passwords-secret" + - subchart - Boolean - Optional. Whether MariaDB is used as subchart or not. Default: false +*/}} +{{- define "common.validations.values.mariadb.passwords" -}} + {{- $existingSecret := include "common.mariadb.values.auth.existingSecret" . -}} + {{- $enabled := include "common.mariadb.values.enabled" . -}} + {{- $architecture := include "common.mariadb.values.architecture" . -}} + {{- $authPrefix := include "common.mariadb.values.key.auth" . -}} + {{- $valueKeyRootPassword := printf "%s.rootPassword" $authPrefix -}} + {{- $valueKeyUsername := printf "%s.username" $authPrefix -}} + {{- $valueKeyPassword := printf "%s.password" $authPrefix -}} + {{- $valueKeyReplicationPassword := printf "%s.replicationPassword" $authPrefix -}} + + {{- if and (not $existingSecret) (eq $enabled "true") -}} + {{- $requiredPasswords := list -}} + + {{- $requiredRootPassword := dict "valueKey" $valueKeyRootPassword "secret" .secret "field" "mariadb-root-password" -}} + {{- $requiredPasswords = append $requiredPasswords $requiredRootPassword -}} + + {{- $valueUsername := include "common.utils.getValueFromKey" (dict "key" $valueKeyUsername "context" .context) }} + {{- if not (empty $valueUsername) -}} + {{- $requiredPassword := dict "valueKey" $valueKeyPassword "secret" .secret "field" "mariadb-password" -}} + {{- $requiredPasswords = append $requiredPasswords $requiredPassword -}} + {{- end -}} + + {{- if (eq $architecture "replication") -}} + {{- $requiredReplicationPassword := dict "valueKey" $valueKeyReplicationPassword "secret" .secret "field" "mariadb-replication-password" -}} + {{- $requiredPasswords = append $requiredPasswords $requiredReplicationPassword -}} + {{- end -}} + + {{- include "common.validations.values.multiple.empty" (dict "required" $requiredPasswords "context" .context) -}} + + {{- end -}} +{{- end -}} + +{{/* +Auxiliary function to get the right value for existingSecret. + +Usage: +{{ include "common.mariadb.values.auth.existingSecret" (dict "context" $) }} +Params: + - subchart - Boolean - Optional. Whether MariaDB is used as subchart or not. Default: false +*/}} +{{- define "common.mariadb.values.auth.existingSecret" -}} + {{- if .subchart -}} + {{- .context.Values.mariadb.auth.existingSecret | quote -}} + {{- else -}} + {{- .context.Values.auth.existingSecret | quote -}} + {{- end -}} +{{- end -}} + +{{/* +Auxiliary function to get the right value for enabled mariadb. + +Usage: +{{ include "common.mariadb.values.enabled" (dict "context" $) }} +*/}} +{{- define "common.mariadb.values.enabled" -}} + {{- if .subchart -}} + {{- printf "%v" .context.Values.mariadb.enabled -}} + {{- else -}} + {{- printf "%v" (not .context.Values.enabled) -}} + {{- end -}} +{{- end -}} + +{{/* +Auxiliary function to get the right value for architecture + +Usage: +{{ include "common.mariadb.values.architecture" (dict "subchart" "true" "context" $) }} +Params: + - subchart - Boolean - Optional. Whether MariaDB is used as subchart or not. Default: false +*/}} +{{- define "common.mariadb.values.architecture" -}} + {{- if .subchart -}} + {{- .context.Values.mariadb.architecture -}} + {{- else -}} + {{- .context.Values.architecture -}} + {{- end -}} +{{- end -}} + +{{/* +Auxiliary function to get the right value for the key auth + +Usage: +{{ include "common.mariadb.values.key.auth" (dict "subchart" "true" "context" $) }} +Params: + - subchart - Boolean - Optional. Whether MariaDB is used as subchart or not. Default: false +*/}} +{{- define "common.mariadb.values.key.auth" -}} + {{- if .subchart -}} + mariadb.auth + {{- else -}} + auth + {{- end -}} +{{- end -}} diff --git a/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/charts/postgresql/charts/common/templates/validations/_mongodb.tpl b/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/charts/postgresql/charts/common/templates/validations/_mongodb.tpl new file mode 100644 index 0000000000..7d5ecbccb4 --- /dev/null +++ b/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/charts/postgresql/charts/common/templates/validations/_mongodb.tpl @@ -0,0 +1,108 @@ +{{/* vim: set filetype=mustache: */}} +{{/* +Validate MongoDB(R) required passwords are not empty. + +Usage: +{{ include "common.validations.values.mongodb.passwords" (dict "secret" "secretName" "subchart" false "context" $) }} +Params: + - secret - String - Required. Name of the secret where MongoDB(R) values are stored, e.g: "mongodb-passwords-secret" + - subchart - Boolean - Optional. Whether MongoDB(R) is used as subchart or not. Default: false +*/}} +{{- define "common.validations.values.mongodb.passwords" -}} + {{- $existingSecret := include "common.mongodb.values.auth.existingSecret" . -}} + {{- $enabled := include "common.mongodb.values.enabled" . -}} + {{- $authPrefix := include "common.mongodb.values.key.auth" . -}} + {{- $architecture := include "common.mongodb.values.architecture" . -}} + {{- $valueKeyRootPassword := printf "%s.rootPassword" $authPrefix -}} + {{- $valueKeyUsername := printf "%s.username" $authPrefix -}} + {{- $valueKeyDatabase := printf "%s.database" $authPrefix -}} + {{- $valueKeyPassword := printf "%s.password" $authPrefix -}} + {{- $valueKeyReplicaSetKey := printf "%s.replicaSetKey" $authPrefix -}} + {{- $valueKeyAuthEnabled := printf "%s.enabled" $authPrefix -}} + + {{- $authEnabled := include "common.utils.getValueFromKey" (dict "key" $valueKeyAuthEnabled "context" .context) -}} + + {{- if and (not $existingSecret) (eq $enabled "true") (eq $authEnabled "true") -}} + {{- $requiredPasswords := list -}} + + {{- $requiredRootPassword := dict "valueKey" $valueKeyRootPassword "secret" .secret "field" "mongodb-root-password" -}} + {{- $requiredPasswords = append $requiredPasswords $requiredRootPassword -}} + + {{- $valueUsername := include "common.utils.getValueFromKey" (dict "key" $valueKeyUsername "context" .context) }} + {{- $valueDatabase := include "common.utils.getValueFromKey" (dict "key" $valueKeyDatabase "context" .context) }} + {{- if and $valueUsername $valueDatabase -}} + {{- $requiredPassword := dict "valueKey" $valueKeyPassword "secret" .secret "field" "mongodb-password" -}} + {{- $requiredPasswords = append $requiredPasswords $requiredPassword -}} + {{- end -}} + + {{- if (eq $architecture "replicaset") -}} + {{- $requiredReplicaSetKey := dict "valueKey" $valueKeyReplicaSetKey "secret" .secret "field" "mongodb-replica-set-key" -}} + {{- $requiredPasswords = append $requiredPasswords $requiredReplicaSetKey -}} + {{- end -}} + + {{- include "common.validations.values.multiple.empty" (dict "required" $requiredPasswords "context" .context) -}} + + {{- end -}} +{{- end -}} + +{{/* +Auxiliary function to get the right value for existingSecret. + +Usage: +{{ include "common.mongodb.values.auth.existingSecret" (dict "context" $) }} +Params: + - subchart - Boolean - Optional. Whether MongoDb is used as subchart or not. Default: false +*/}} +{{- define "common.mongodb.values.auth.existingSecret" -}} + {{- if .subchart -}} + {{- .context.Values.mongodb.auth.existingSecret | quote -}} + {{- else -}} + {{- .context.Values.auth.existingSecret | quote -}} + {{- end -}} +{{- end -}} + +{{/* +Auxiliary function to get the right value for enabled mongodb. + +Usage: +{{ include "common.mongodb.values.enabled" (dict "context" $) }} +*/}} +{{- define "common.mongodb.values.enabled" -}} + {{- if .subchart -}} + {{- printf "%v" .context.Values.mongodb.enabled -}} + {{- else -}} + {{- printf "%v" (not .context.Values.enabled) -}} + {{- end -}} +{{- end -}} + +{{/* +Auxiliary function to get the right value for the key auth + +Usage: +{{ include "common.mongodb.values.key.auth" (dict "subchart" "true" "context" $) }} +Params: + - subchart - Boolean - Optional. Whether MongoDB(R) is used as subchart or not. Default: false +*/}} +{{- define "common.mongodb.values.key.auth" -}} + {{- if .subchart -}} + mongodb.auth + {{- else -}} + auth + {{- end -}} +{{- end -}} + +{{/* +Auxiliary function to get the right value for architecture + +Usage: +{{ include "common.mongodb.values.architecture" (dict "subchart" "true" "context" $) }} +Params: + - subchart - Boolean - Optional. Whether MariaDB is used as subchart or not. Default: false +*/}} +{{- define "common.mongodb.values.architecture" -}} + {{- if .subchart -}} + {{- .context.Values.mongodb.architecture -}} + {{- else -}} + {{- .context.Values.architecture -}} + {{- end -}} +{{- end -}} diff --git a/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/charts/postgresql/charts/common/templates/validations/_postgresql.tpl b/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/charts/postgresql/charts/common/templates/validations/_postgresql.tpl new file mode 100644 index 0000000000..992bcd3908 --- /dev/null +++ b/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/charts/postgresql/charts/common/templates/validations/_postgresql.tpl @@ -0,0 +1,131 @@ +{{/* vim: set filetype=mustache: */}} +{{/* +Validate PostgreSQL required passwords are not empty. + +Usage: +{{ include "common.validations.values.postgresql.passwords" (dict "secret" "secretName" "subchart" false "context" $) }} +Params: + - secret - String - Required. Name of the secret where postgresql values are stored, e.g: "postgresql-passwords-secret" + - subchart - Boolean - Optional. Whether postgresql is used as subchart or not. Default: false +*/}} +{{- define "common.validations.values.postgresql.passwords" -}} + {{- $existingSecret := include "common.postgresql.values.existingSecret" . -}} + {{- $enabled := include "common.postgresql.values.enabled" . -}} + {{- $valueKeyPostgresqlPassword := include "common.postgresql.values.key.postgressPassword" . -}} + {{- $valueKeyPostgresqlReplicationEnabled := include "common.postgresql.values.key.replicationPassword" . -}} + + {{- if and (not $existingSecret) (eq $enabled "true") -}} + {{- $requiredPasswords := list -}} + + {{- $requiredPostgresqlPassword := dict "valueKey" $valueKeyPostgresqlPassword "secret" .secret "field" "postgresql-password" -}} + {{- $requiredPasswords = append $requiredPasswords $requiredPostgresqlPassword -}} + + {{- $enabledReplication := include "common.postgresql.values.enabled.replication" . -}} + {{- if (eq $enabledReplication "true") -}} + {{- $requiredPostgresqlReplicationPassword := dict "valueKey" $valueKeyPostgresqlReplicationEnabled "secret" .secret "field" "postgresql-replication-password" -}} + {{- $requiredPasswords = append $requiredPasswords $requiredPostgresqlReplicationPassword -}} + {{- end -}} + + {{- include "common.validations.values.multiple.empty" (dict "required" $requiredPasswords "context" .context) -}} + {{- end -}} +{{- end -}} + +{{/* +Auxiliary function to decide whether evaluate global values. + +Usage: +{{ include "common.postgresql.values.use.global" (dict "key" "key-of-global" "context" $) }} +Params: + - key - String - Required. Field to be evaluated within global, e.g: "existingSecret" +*/}} +{{- define "common.postgresql.values.use.global" -}} + {{- if .context.Values.global -}} + {{- if .context.Values.global.postgresql -}} + {{- index .context.Values.global.postgresql .key | quote -}} + {{- end -}} + {{- end -}} +{{- end -}} + +{{/* +Auxiliary function to get the right value for existingSecret. + +Usage: +{{ include "common.postgresql.values.existingSecret" (dict "context" $) }} +*/}} +{{- define "common.postgresql.values.existingSecret" -}} + {{- $globalValue := include "common.postgresql.values.use.global" (dict "key" "existingSecret" "context" .context) -}} + + {{- if .subchart -}} + {{- default (.context.Values.postgresql.existingSecret | quote) $globalValue -}} + {{- else -}} + {{- default (.context.Values.existingSecret | quote) $globalValue -}} + {{- end -}} +{{- end -}} + +{{/* +Auxiliary function to get the right value for enabled postgresql. + +Usage: +{{ include "common.postgresql.values.enabled" (dict "context" $) }} +*/}} +{{- define "common.postgresql.values.enabled" -}} + {{- if .subchart -}} + {{- printf "%v" .context.Values.postgresql.enabled -}} + {{- else -}} + {{- printf "%v" (not .context.Values.enabled) -}} + {{- end -}} +{{- end -}} + +{{/* +Auxiliary function to get the right value for the key postgressPassword. + +Usage: +{{ include "common.postgresql.values.key.postgressPassword" (dict "subchart" "true" "context" $) }} +Params: + - subchart - Boolean - Optional. Whether postgresql is used as subchart or not. Default: false +*/}} +{{- define "common.postgresql.values.key.postgressPassword" -}} + {{- $globalValue := include "common.postgresql.values.use.global" (dict "key" "postgresqlUsername" "context" .context) -}} + + {{- if not $globalValue -}} + {{- if .subchart -}} + postgresql.postgresqlPassword + {{- else -}} + postgresqlPassword + {{- end -}} + {{- else -}} + global.postgresql.postgresqlPassword + {{- end -}} +{{- end -}} + +{{/* +Auxiliary function to get the right value for enabled.replication. + +Usage: +{{ include "common.postgresql.values.enabled.replication" (dict "subchart" "true" "context" $) }} +Params: + - subchart - Boolean - Optional. Whether postgresql is used as subchart or not. Default: false +*/}} +{{- define "common.postgresql.values.enabled.replication" -}} + {{- if .subchart -}} + {{- printf "%v" .context.Values.postgresql.replication.enabled -}} + {{- else -}} + {{- printf "%v" .context.Values.replication.enabled -}} + {{- end -}} +{{- end -}} + +{{/* +Auxiliary function to get the right value for the key replication.password. + +Usage: +{{ include "common.postgresql.values.key.replicationPassword" (dict "subchart" "true" "context" $) }} +Params: + - subchart - Boolean - Optional. Whether postgresql is used as subchart or not. Default: false +*/}} +{{- define "common.postgresql.values.key.replicationPassword" -}} + {{- if .subchart -}} + postgresql.replication.password + {{- else -}} + replication.password + {{- end -}} +{{- end -}} diff --git a/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/charts/postgresql/charts/common/templates/validations/_redis.tpl b/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/charts/postgresql/charts/common/templates/validations/_redis.tpl new file mode 100644 index 0000000000..3e2a47c039 --- /dev/null +++ b/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/charts/postgresql/charts/common/templates/validations/_redis.tpl @@ -0,0 +1,72 @@ + +{{/* vim: set filetype=mustache: */}} +{{/* +Validate Redis(TM) required passwords are not empty. + +Usage: +{{ include "common.validations.values.redis.passwords" (dict "secret" "secretName" "subchart" false "context" $) }} +Params: + - secret - String - Required. Name of the secret where redis values are stored, e.g: "redis-passwords-secret" + - subchart - Boolean - Optional. Whether redis is used as subchart or not. Default: false +*/}} +{{- define "common.validations.values.redis.passwords" -}} + {{- $existingSecret := include "common.redis.values.existingSecret" . -}} + {{- $enabled := include "common.redis.values.enabled" . -}} + {{- $valueKeyPrefix := include "common.redis.values.keys.prefix" . -}} + {{- $valueKeyRedisPassword := printf "%s%s" $valueKeyPrefix "password" -}} + {{- $valueKeyRedisUsePassword := printf "%s%s" $valueKeyPrefix "usePassword" -}} + + {{- if and (not $existingSecret) (eq $enabled "true") -}} + {{- $requiredPasswords := list -}} + + {{- $usePassword := include "common.utils.getValueFromKey" (dict "key" $valueKeyRedisUsePassword "context" .context) -}} + {{- if eq $usePassword "true" -}} + {{- $requiredRedisPassword := dict "valueKey" $valueKeyRedisPassword "secret" .secret "field" "redis-password" -}} + {{- $requiredPasswords = append $requiredPasswords $requiredRedisPassword -}} + {{- end -}} + + {{- include "common.validations.values.multiple.empty" (dict "required" $requiredPasswords "context" .context) -}} + {{- end -}} +{{- end -}} + +{{/* +Redis Auxiliary function to get the right value for existingSecret. + +Usage: +{{ include "common.redis.values.existingSecret" (dict "context" $) }} +Params: + - subchart - Boolean - Optional. Whether Redis(TM) is used as subchart or not. Default: false +*/}} +{{- define "common.redis.values.existingSecret" -}} + {{- if .subchart -}} + {{- .context.Values.redis.existingSecret | quote -}} + {{- else -}} + {{- .context.Values.existingSecret | quote -}} + {{- end -}} +{{- end -}} + +{{/* +Auxiliary function to get the right value for enabled redis. + +Usage: +{{ include "common.redis.values.enabled" (dict "context" $) }} +*/}} +{{- define "common.redis.values.enabled" -}} + {{- if .subchart -}} + {{- printf "%v" .context.Values.redis.enabled -}} + {{- else -}} + {{- printf "%v" (not .context.Values.enabled) -}} + {{- end -}} +{{- end -}} + +{{/* +Auxiliary function to get the right prefix path for the values + +Usage: +{{ include "common.redis.values.key.prefix" (dict "subchart" "true" "context" $) }} +Params: + - subchart - Boolean - Optional. Whether redis is used as subchart or not. Default: false +*/}} +{{- define "common.redis.values.keys.prefix" -}} + {{- if .subchart -}}redis.{{- else -}}{{- end -}} +{{- end -}} diff --git a/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/charts/postgresql/charts/common/templates/validations/_validations.tpl b/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/charts/postgresql/charts/common/templates/validations/_validations.tpl new file mode 100644 index 0000000000..9a814cf40d --- /dev/null +++ b/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/charts/postgresql/charts/common/templates/validations/_validations.tpl @@ -0,0 +1,46 @@ +{{/* vim: set filetype=mustache: */}} +{{/* +Validate values must not be empty. + +Usage: +{{- $validateValueConf00 := (dict "valueKey" "path.to.value" "secret" "secretName" "field" "password-00") -}} +{{- $validateValueConf01 := (dict "valueKey" "path.to.value" "secret" "secretName" "field" "password-01") -}} +{{ include "common.validations.values.empty" (dict "required" (list $validateValueConf00 $validateValueConf01) "context" $) }} + +Validate value params: + - valueKey - String - Required. The path to the validating value in the values.yaml, e.g: "mysql.password" + - secret - String - Optional. Name of the secret where the validating value is generated/stored, e.g: "mysql-passwords-secret" + - field - String - Optional. Name of the field in the secret data, e.g: "mysql-password" +*/}} +{{- define "common.validations.values.multiple.empty" -}} + {{- range .required -}} + {{- include "common.validations.values.single.empty" (dict "valueKey" .valueKey "secret" .secret "field" .field "context" $.context) -}} + {{- end -}} +{{- end -}} + +{{/* +Validate a value must not be empty. + +Usage: +{{ include "common.validations.value.empty" (dict "valueKey" "mariadb.password" "secret" "secretName" "field" "my-password" "subchart" "subchart" "context" $) }} + +Validate value params: + - valueKey - String - Required. The path to the validating value in the values.yaml, e.g: "mysql.password" + - secret - String - Optional. Name of the secret where the validating value is generated/stored, e.g: "mysql-passwords-secret" + - field - String - Optional. Name of the field in the secret data, e.g: "mysql-password" + - subchart - String - Optional - Name of the subchart that the validated password is part of. +*/}} +{{- define "common.validations.values.single.empty" -}} + {{- $value := include "common.utils.getValueFromKey" (dict "key" .valueKey "context" .context) }} + {{- $subchart := ternary "" (printf "%s." .subchart) (empty .subchart) }} + + {{- if not $value -}} + {{- $varname := "my-value" -}} + {{- $getCurrentValue := "" -}} + {{- if and .secret .field -}} + {{- $varname = include "common.utils.fieldToEnvVar" . -}} + {{- $getCurrentValue = printf " To get the current value:\n\n %s\n" (include "common.utils.secret.getvalue" .) -}} + {{- end -}} + {{- printf "\n '%s' must not be empty, please add '--set %s%s=$%s' to the command.%s" .valueKey $subchart .valueKey $varname $getCurrentValue -}} + {{- end -}} +{{- end -}} diff --git a/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/charts/postgresql/charts/common/values.yaml b/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/charts/postgresql/charts/common/values.yaml new file mode 100644 index 0000000000..9ecdc93f58 --- /dev/null +++ b/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/charts/postgresql/charts/common/values.yaml @@ -0,0 +1,3 @@ +## bitnami/common +## It is required by CI/CD tools and processes. +exampleValue: common-chart diff --git a/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/charts/postgresql/ci/commonAnnotations.yaml b/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/charts/postgresql/ci/commonAnnotations.yaml new file mode 100644 index 0000000000..97e18a4cc0 --- /dev/null +++ b/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/charts/postgresql/ci/commonAnnotations.yaml @@ -0,0 +1,3 @@ +commonAnnotations: + helm.sh/hook: "\"pre-install, pre-upgrade\"" + helm.sh/hook-weight: "-1" diff --git a/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/charts/postgresql/ci/default-values.yaml b/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/charts/postgresql/ci/default-values.yaml new file mode 100644 index 0000000000..fc2ba605ad --- /dev/null +++ b/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/charts/postgresql/ci/default-values.yaml @@ -0,0 +1 @@ +# Leave this file empty to ensure that CI runs builds against the default configuration in values.yaml. diff --git a/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/charts/postgresql/ci/shmvolume-disabled-values.yaml b/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/charts/postgresql/ci/shmvolume-disabled-values.yaml new file mode 100644 index 0000000000..347d3b40a8 --- /dev/null +++ b/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/charts/postgresql/ci/shmvolume-disabled-values.yaml @@ -0,0 +1,2 @@ +shmVolume: + enabled: false diff --git a/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/charts/postgresql/files/README.md b/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/charts/postgresql/files/README.md new file mode 100644 index 0000000000..1813a2feaa --- /dev/null +++ b/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/charts/postgresql/files/README.md @@ -0,0 +1 @@ +Copy here your postgresql.conf and/or pg_hba.conf files to use it as a config map. diff --git a/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/charts/postgresql/files/conf.d/README.md b/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/charts/postgresql/files/conf.d/README.md new file mode 100644 index 0000000000..184c1875d5 --- /dev/null +++ b/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/charts/postgresql/files/conf.d/README.md @@ -0,0 +1,4 @@ +If you don't want to provide the whole configuration file and only specify certain parameters, you can copy here your extended `.conf` files. +These files will be injected as a config maps and add/overwrite the default configuration using the `include_dir` directive that allows settings to be loaded from files other than the default `postgresql.conf`. + +More info in the [bitnami-docker-postgresql README](https://github.com/bitnami/bitnami-docker-postgresql#configuration-file). diff --git a/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/charts/postgresql/files/docker-entrypoint-initdb.d/README.md b/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/charts/postgresql/files/docker-entrypoint-initdb.d/README.md new file mode 100644 index 0000000000..cba38091e0 --- /dev/null +++ b/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/charts/postgresql/files/docker-entrypoint-initdb.d/README.md @@ -0,0 +1,3 @@ +You can copy here your custom `.sh`, `.sql` or `.sql.gz` file so they are executed during the first boot of the image. + +More info in the [bitnami-docker-postgresql](https://github.com/bitnami/bitnami-docker-postgresql#initializing-a-new-instance) repository. \ No newline at end of file diff --git a/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/charts/postgresql/templates/NOTES.txt b/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/charts/postgresql/templates/NOTES.txt new file mode 100644 index 0000000000..4e98958c13 --- /dev/null +++ b/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/charts/postgresql/templates/NOTES.txt @@ -0,0 +1,59 @@ +** Please be patient while the chart is being deployed ** + +PostgreSQL can be accessed via port {{ template "postgresql.port" . }} on the following DNS name from within your cluster: + + {{ template "common.names.fullname" . }}.{{ .Release.Namespace }}.svc.cluster.local - Read/Write connection +{{- if .Values.replication.enabled }} + {{ template "common.names.fullname" . }}-read.{{ .Release.Namespace }}.svc.cluster.local - Read only connection +{{- end }} + +{{- if not (eq (include "postgresql.username" .) "postgres") }} + +To get the password for "postgres" run: + + export POSTGRES_ADMIN_PASSWORD=$(kubectl get secret --namespace {{ .Release.Namespace }} {{ template "postgresql.secretName" . }} -o jsonpath="{.data.postgresql-postgres-password}" | base64 --decode) +{{- end }} + +To get the password for "{{ template "postgresql.username" . }}" run: + + export POSTGRES_PASSWORD=$(kubectl get secret --namespace {{ .Release.Namespace }} {{ template "postgresql.secretName" . }} -o jsonpath="{.data.postgresql-password}" | base64 --decode) + +To connect to your database run the following command: + + kubectl run {{ template "common.names.fullname" . }}-client --rm --tty -i --restart='Never' --namespace {{ .Release.Namespace }} --image {{ template "postgresql.image" . }} --env="PGPASSWORD=$POSTGRES_PASSWORD" {{- if and (.Values.networkPolicy.enabled) (not .Values.networkPolicy.allowExternal) }} + --labels="{{ template "common.names.fullname" . }}-client=true" {{- end }} --command -- psql --host {{ template "common.names.fullname" . }} -U {{ .Values.postgresqlUsername }} -d {{- if .Values.postgresqlDatabase }} {{ .Values.postgresqlDatabase }}{{- else }} postgres{{- end }} -p {{ template "postgresql.port" . }} + +{{ if and (.Values.networkPolicy.enabled) (not .Values.networkPolicy.allowExternal) }} +Note: Since NetworkPolicy is enabled, only pods with label {{ template "common.names.fullname" . }}-client=true" will be able to connect to this PostgreSQL cluster. +{{- end }} + +To connect to your database from outside the cluster execute the following commands: + +{{- if contains "NodePort" .Values.service.type }} + + export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}") + export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ template "common.names.fullname" . }}) + {{ if (include "postgresql.password" . ) }}PGPASSWORD="$POSTGRES_PASSWORD" {{ end }}psql --host $NODE_IP --port $NODE_PORT -U {{ .Values.postgresqlUsername }} -d {{- if .Values.postgresqlDatabase }} {{ .Values.postgresqlDatabase }}{{- else }} postgres{{- end }} + +{{- else if contains "LoadBalancer" .Values.service.type }} + + NOTE: It may take a few minutes for the LoadBalancer IP to be available. + Watch the status with: 'kubectl get svc --namespace {{ .Release.Namespace }} -w {{ template "common.names.fullname" . }}' + + export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ template "common.names.fullname" . }} --template "{{"{{ range (index .status.loadBalancer.ingress 0) }}{{.}}{{ end }}"}}") + {{ if (include "postgresql.password" . ) }}PGPASSWORD="$POSTGRES_PASSWORD" {{ end }}psql --host $SERVICE_IP --port {{ template "postgresql.port" . }} -U {{ .Values.postgresqlUsername }} -d {{- if .Values.postgresqlDatabase }} {{ .Values.postgresqlDatabase }}{{- else }} postgres{{- end }} + +{{- else if contains "ClusterIP" .Values.service.type }} + + kubectl port-forward --namespace {{ .Release.Namespace }} svc/{{ template "common.names.fullname" . }} {{ template "postgresql.port" . }}:{{ template "postgresql.port" . }} & + {{ if (include "postgresql.password" . ) }}PGPASSWORD="$POSTGRES_PASSWORD" {{ end }}psql --host 127.0.0.1 -U {{ .Values.postgresqlUsername }} -d {{- if .Values.postgresqlDatabase }} {{ .Values.postgresqlDatabase }}{{- else }} postgres{{- end }} -p {{ template "postgresql.port" . }} + +{{- end }} + +{{- include "postgresql.validateValues" . -}} + +{{- include "common.warnings.rollingTag" .Values.image -}} + +{{- $passwordValidationErrors := include "common.validations.values.postgresql.passwords" (dict "secret" (include "common.names.fullname" .) "context" $) -}} + +{{- include "common.errors.upgrade.passwords.empty" (dict "validationErrors" (list $passwordValidationErrors) "context" $) -}} diff --git a/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/charts/postgresql/templates/_helpers.tpl b/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/charts/postgresql/templates/_helpers.tpl new file mode 100644 index 0000000000..1f98efe789 --- /dev/null +++ b/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/charts/postgresql/templates/_helpers.tpl @@ -0,0 +1,337 @@ +{{/* vim: set filetype=mustache: */}} + +{{/* +Expand the name of the chart. +*/}} +{{- define "postgresql.name" -}} +{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}} +{{- end -}} + +{{/* +Create a default fully qualified app name. +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +*/}} +{{- define "postgresql.primary.fullname" -}} +{{- $name := default .Chart.Name .Values.nameOverride -}} +{{- $fullname := default (printf "%s-%s" .Release.Name $name) .Values.fullnameOverride -}} +{{- if .Values.replication.enabled -}} +{{- printf "%s-%s" $fullname "primary" | trunc 63 | trimSuffix "-" -}} +{{- else -}} +{{- printf "%s" $fullname | trunc 63 | trimSuffix "-" -}} +{{- end -}} +{{- end -}} + +{{/* +Return the proper PostgreSQL image name +*/}} +{{- define "postgresql.image" -}} +{{ include "common.images.image" (dict "imageRoot" .Values.image "global" .Values.global) }} +{{- end -}} + +{{/* +Return the proper PostgreSQL metrics image name +*/}} +{{- define "postgresql.metrics.image" -}} +{{ include "common.images.image" (dict "imageRoot" .Values.metrics.image "global" .Values.global) }} +{{- end -}} + +{{/* +Return the proper image name (for the init container volume-permissions image) +*/}} +{{- define "postgresql.volumePermissions.image" -}} +{{ include "common.images.image" (dict "imageRoot" .Values.volumePermissions.image "global" .Values.global) }} +{{- end -}} + +{{/* +Return the proper Docker Image Registry Secret Names +*/}} +{{- define "postgresql.imagePullSecrets" -}} +{{ include "common.images.pullSecrets" (dict "images" (list .Values.image .Values.metrics.image .Values.volumePermissions.image) "global" .Values.global) }} +{{- end -}} + +{{/* +Return PostgreSQL postgres user password +*/}} +{{- define "postgresql.postgres.password" -}} +{{- if .Values.global.postgresql.postgresqlPostgresPassword }} + {{- .Values.global.postgresql.postgresqlPostgresPassword -}} +{{- else if .Values.postgresqlPostgresPassword -}} + {{- .Values.postgresqlPostgresPassword -}} +{{- else -}} + {{- randAlphaNum 10 -}} +{{- end -}} +{{- end -}} + +{{/* +Return PostgreSQL password +*/}} +{{- define "postgresql.password" -}} +{{- if .Values.global.postgresql.postgresqlPassword }} + {{- .Values.global.postgresql.postgresqlPassword -}} +{{- else if .Values.postgresqlPassword -}} + {{- .Values.postgresqlPassword -}} +{{- else -}} + {{- randAlphaNum 10 -}} +{{- end -}} +{{- end -}} + +{{/* +Return PostgreSQL replication password +*/}} +{{- define "postgresql.replication.password" -}} +{{- if .Values.global.postgresql.replicationPassword }} + {{- .Values.global.postgresql.replicationPassword -}} +{{- else if .Values.replication.password -}} + {{- .Values.replication.password -}} +{{- else -}} + {{- randAlphaNum 10 -}} +{{- end -}} +{{- end -}} + +{{/* +Return PostgreSQL username +*/}} +{{- define "postgresql.username" -}} +{{- if .Values.global.postgresql.postgresqlUsername }} + {{- .Values.global.postgresql.postgresqlUsername -}} +{{- else -}} + {{- .Values.postgresqlUsername -}} +{{- end -}} +{{- end -}} + +{{/* +Return PostgreSQL replication username +*/}} +{{- define "postgresql.replication.username" -}} +{{- if .Values.global.postgresql.replicationUser }} + {{- .Values.global.postgresql.replicationUser -}} +{{- else -}} + {{- .Values.replication.user -}} +{{- end -}} +{{- end -}} + +{{/* +Return PostgreSQL port +*/}} +{{- define "postgresql.port" -}} +{{- if .Values.global.postgresql.servicePort }} + {{- .Values.global.postgresql.servicePort -}} +{{- else -}} + {{- .Values.service.port -}} +{{- end -}} +{{- end -}} + +{{/* +Return PostgreSQL created database +*/}} +{{- define "postgresql.database" -}} +{{- if .Values.global.postgresql.postgresqlDatabase }} + {{- .Values.global.postgresql.postgresqlDatabase -}} +{{- else if .Values.postgresqlDatabase -}} + {{- .Values.postgresqlDatabase -}} +{{- end -}} +{{- end -}} + +{{/* +Get the password secret. +*/}} +{{- define "postgresql.secretName" -}} +{{- if .Values.global.postgresql.existingSecret }} + {{- printf "%s" (tpl .Values.global.postgresql.existingSecret $) -}} +{{- else if .Values.existingSecret -}} + {{- printf "%s" (tpl .Values.existingSecret $) -}} +{{- else -}} + {{- printf "%s" (include "common.names.fullname" .) -}} +{{- end -}} +{{- end -}} + +{{/* +Return true if we should use an existingSecret. +*/}} +{{- define "postgresql.useExistingSecret" -}} +{{- if or .Values.global.postgresql.existingSecret .Values.existingSecret -}} + {{- true -}} +{{- end -}} +{{- end -}} + +{{/* +Return true if a secret object should be created +*/}} +{{- define "postgresql.createSecret" -}} +{{- if not (include "postgresql.useExistingSecret" .) -}} + {{- true -}} +{{- end -}} +{{- end -}} + +{{/* +Get the configuration ConfigMap name. +*/}} +{{- define "postgresql.configurationCM" -}} +{{- if .Values.configurationConfigMap -}} +{{- printf "%s" (tpl .Values.configurationConfigMap $) -}} +{{- else -}} +{{- printf "%s-configuration" (include "common.names.fullname" .) -}} +{{- end -}} +{{- end -}} + +{{/* +Get the extended configuration ConfigMap name. +*/}} +{{- define "postgresql.extendedConfigurationCM" -}} +{{- if .Values.extendedConfConfigMap -}} +{{- printf "%s" (tpl .Values.extendedConfConfigMap $) -}} +{{- else -}} +{{- printf "%s-extended-configuration" (include "common.names.fullname" .) -}} +{{- end -}} +{{- end -}} + +{{/* +Return true if a configmap should be mounted with PostgreSQL configuration +*/}} +{{- define "postgresql.mountConfigurationCM" -}} +{{- if or (.Files.Glob "files/postgresql.conf") (.Files.Glob "files/pg_hba.conf") .Values.postgresqlConfiguration .Values.pgHbaConfiguration .Values.configurationConfigMap }} + {{- true -}} +{{- end -}} +{{- end -}} + +{{/* +Get the initialization scripts ConfigMap name. +*/}} +{{- define "postgresql.initdbScriptsCM" -}} +{{- if .Values.initdbScriptsConfigMap -}} +{{- printf "%s" (tpl .Values.initdbScriptsConfigMap $) -}} +{{- else -}} +{{- printf "%s-init-scripts" (include "common.names.fullname" .) -}} +{{- end -}} +{{- end -}} + +{{/* +Get the initialization scripts Secret name. +*/}} +{{- define "postgresql.initdbScriptsSecret" -}} +{{- printf "%s" (tpl .Values.initdbScriptsSecret $) -}} +{{- end -}} + +{{/* +Get the metrics ConfigMap name. +*/}} +{{- define "postgresql.metricsCM" -}} +{{- printf "%s-metrics" (include "common.names.fullname" .) -}} +{{- end -}} + +{{/* +Get the readiness probe command +*/}} +{{- define "postgresql.readinessProbeCommand" -}} +- | +{{- if (include "postgresql.database" .) }} + exec pg_isready -U {{ include "postgresql.username" . | quote }} -d "dbname={{ include "postgresql.database" . }} {{- if and .Values.tls.enabled .Values.tls.certCAFilename }} sslcert={{ include "postgresql.tlsCert" . }} sslkey={{ include "postgresql.tlsCertKey" . }}{{- end }}" -h 127.0.0.1 -p {{ template "postgresql.port" . }} +{{- else }} + exec pg_isready -U {{ include "postgresql.username" . | quote }} {{- if and .Values.tls.enabled .Values.tls.certCAFilename }} -d "sslcert={{ include "postgresql.tlsCert" . }} sslkey={{ include "postgresql.tlsCertKey" . }}"{{- end }} -h 127.0.0.1 -p {{ template "postgresql.port" . }} +{{- end }} +{{- if contains "bitnami/" .Values.image.repository }} + [ -f /opt/bitnami/postgresql/tmp/.initialized ] || [ -f /bitnami/postgresql/.initialized ] +{{- end -}} +{{- end -}} + +{{/* +Compile all warnings into a single message, and call fail. +*/}} +{{- define "postgresql.validateValues" -}} +{{- $messages := list -}} +{{- $messages := append $messages (include "postgresql.validateValues.ldapConfigurationMethod" .) -}} +{{- $messages := append $messages (include "postgresql.validateValues.psp" .) -}} +{{- $messages := append $messages (include "postgresql.validateValues.tls" .) -}} +{{- $messages := without $messages "" -}} +{{- $message := join "\n" $messages -}} + +{{- if $message -}} +{{- printf "\nVALUES VALIDATION:\n%s" $message | fail -}} +{{- end -}} +{{- end -}} + +{{/* +Validate values of Postgresql - If ldap.url is used then you don't need the other settings for ldap +*/}} +{{- define "postgresql.validateValues.ldapConfigurationMethod" -}} +{{- if and .Values.ldap.enabled (and (not (empty .Values.ldap.url)) (not (empty .Values.ldap.server))) }} +postgresql: ldap.url, ldap.server + You cannot set both `ldap.url` and `ldap.server` at the same time. + Please provide a unique way to configure LDAP. + More info at https://www.postgresql.org/docs/current/auth-ldap.html +{{- end -}} +{{- end -}} + +{{/* +Validate values of Postgresql - If PSP is enabled RBAC should be enabled too +*/}} +{{- define "postgresql.validateValues.psp" -}} +{{- if and .Values.psp.create (not .Values.rbac.create) }} +postgresql: psp.create, rbac.create + RBAC should be enabled if PSP is enabled in order for PSP to work. + More info at https://kubernetes.io/docs/concepts/policy/pod-security-policy/#authorizing-policies +{{- end -}} +{{- end -}} + +{{/* +Return the appropriate apiVersion for podsecuritypolicy. +*/}} +{{- define "podsecuritypolicy.apiVersion" -}} +{{- if semverCompare "<1.10-0" .Capabilities.KubeVersion.GitVersion -}} +{{- print "extensions/v1beta1" -}} +{{- else -}} +{{- print "policy/v1beta1" -}} +{{- end -}} +{{- end -}} + +{{/* +Return the appropriate apiVersion for networkpolicy. +*/}} +{{- define "postgresql.networkPolicy.apiVersion" -}} +{{- if semverCompare ">=1.4-0, <1.7-0" .Capabilities.KubeVersion.GitVersion -}} +"extensions/v1beta1" +{{- else if semverCompare "^1.7-0" .Capabilities.KubeVersion.GitVersion -}} +"networking.k8s.io/v1" +{{- end -}} +{{- end -}} + +{{/* +Validate values of Postgresql TLS - When TLS is enabled, so must be VolumePermissions +*/}} +{{- define "postgresql.validateValues.tls" -}} +{{- if and .Values.tls.enabled (not .Values.volumePermissions.enabled) }} +postgresql: tls.enabled, volumePermissions.enabled + When TLS is enabled you must enable volumePermissions as well to ensure certificates files have + the right permissions. +{{- end -}} +{{- end -}} + +{{/* +Return the path to the cert file. +*/}} +{{- define "postgresql.tlsCert" -}} +{{- required "Certificate filename is required when TLS in enabled" .Values.tls.certFilename | printf "/opt/bitnami/postgresql/certs/%s" -}} +{{- end -}} + +{{/* +Return the path to the cert key file. +*/}} +{{- define "postgresql.tlsCertKey" -}} +{{- required "Certificate Key filename is required when TLS in enabled" .Values.tls.certKeyFilename | printf "/opt/bitnami/postgresql/certs/%s" -}} +{{- end -}} + +{{/* +Return the path to the CA cert file. +*/}} +{{- define "postgresql.tlsCACert" -}} +{{- printf "/opt/bitnami/postgresql/certs/%s" .Values.tls.certCAFilename -}} +{{- end -}} + +{{/* +Return the path to the CRL file. +*/}} +{{- define "postgresql.tlsCRL" -}} +{{- if .Values.tls.crlFilename -}} +{{- printf "/opt/bitnami/postgresql/certs/%s" .Values.tls.crlFilename -}} +{{- end -}} +{{- end -}} diff --git a/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/charts/postgresql/templates/configmap.yaml b/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/charts/postgresql/templates/configmap.yaml new file mode 100644 index 0000000000..3a5ea18ae9 --- /dev/null +++ b/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/charts/postgresql/templates/configmap.yaml @@ -0,0 +1,31 @@ +{{ if and (or (.Files.Glob "files/postgresql.conf") (.Files.Glob "files/pg_hba.conf") .Values.postgresqlConfiguration .Values.pgHbaConfiguration) (not .Values.configurationConfigMap) }} +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ template "common.names.fullname" . }}-configuration + labels: + {{- include "common.labels.standard" . | nindent 4 }} + {{- if .Values.commonAnnotations }} + annotations: {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} + {{- end }} + namespace: {{ .Release.Namespace }} +data: +{{- if (.Files.Glob "files/postgresql.conf") }} +{{ (.Files.Glob "files/postgresql.conf").AsConfig | indent 2 }} +{{- else if .Values.postgresqlConfiguration }} + postgresql.conf: | +{{- range $key, $value := default dict .Values.postgresqlConfiguration }} + {{- if kindIs "string" $value }} + {{ $key | snakecase }} = '{{ $value }}' + {{- else }} + {{ $key | snakecase }} = {{ $value }} + {{- end }} +{{- end }} +{{- end }} +{{- if (.Files.Glob "files/pg_hba.conf") }} +{{ (.Files.Glob "files/pg_hba.conf").AsConfig | indent 2 }} +{{- else if .Values.pgHbaConfiguration }} + pg_hba.conf: | +{{ .Values.pgHbaConfiguration | indent 4 }} +{{- end }} +{{ end }} diff --git a/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/charts/postgresql/templates/extended-config-configmap.yaml b/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/charts/postgresql/templates/extended-config-configmap.yaml new file mode 100644 index 0000000000..b0dad253b5 --- /dev/null +++ b/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/charts/postgresql/templates/extended-config-configmap.yaml @@ -0,0 +1,26 @@ +{{- if and (or (.Files.Glob "files/conf.d/*.conf") .Values.postgresqlExtendedConf) (not .Values.extendedConfConfigMap)}} +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ template "common.names.fullname" . }}-extended-configuration + labels: + {{- include "common.labels.standard" . | nindent 4 }} + {{- if .Values.commonAnnotations }} + annotations: {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} + {{- end }} + namespace: {{ .Release.Namespace }} +data: +{{- with .Files.Glob "files/conf.d/*.conf" }} +{{ .AsConfig | indent 2 }} +{{- end }} +{{ with .Values.postgresqlExtendedConf }} + override.conf: | +{{- range $key, $value := . }} + {{- if kindIs "string" $value }} + {{ $key | snakecase }} = '{{ $value }}' + {{- else }} + {{ $key | snakecase }} = {{ $value }} + {{- end }} +{{- end }} +{{- end }} +{{- end }} diff --git a/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/charts/postgresql/templates/extra-list.yaml b/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/charts/postgresql/templates/extra-list.yaml new file mode 100644 index 0000000000..9ac65f9e16 --- /dev/null +++ b/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/charts/postgresql/templates/extra-list.yaml @@ -0,0 +1,4 @@ +{{- range .Values.extraDeploy }} +--- +{{ include "common.tplvalues.render" (dict "value" . "context" $) }} +{{- end }} diff --git a/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/charts/postgresql/templates/initialization-configmap.yaml b/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/charts/postgresql/templates/initialization-configmap.yaml new file mode 100644 index 0000000000..7796c67a93 --- /dev/null +++ b/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/charts/postgresql/templates/initialization-configmap.yaml @@ -0,0 +1,25 @@ +{{- if and (or (.Files.Glob "files/docker-entrypoint-initdb.d/*.{sh,sql,sql.gz}") .Values.initdbScripts) (not .Values.initdbScriptsConfigMap) }} +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ template "common.names.fullname" . }}-init-scripts + labels: + {{- include "common.labels.standard" . | nindent 4 }} + {{- if .Values.commonAnnotations }} + annotations: {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} + {{- end }} + namespace: {{ .Release.Namespace }} +{{- with .Files.Glob "files/docker-entrypoint-initdb.d/*.sql.gz" }} +binaryData: +{{- range $path, $bytes := . }} + {{ base $path }}: {{ $.Files.Get $path | b64enc | quote }} +{{- end }} +{{- end }} +data: +{{- with .Files.Glob "files/docker-entrypoint-initdb.d/*.{sh,sql}" }} +{{ .AsConfig | indent 2 }} +{{- end }} +{{- with .Values.initdbScripts }} +{{ toYaml . | indent 2 }} +{{- end }} +{{- end }} diff --git a/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/charts/postgresql/templates/metrics-configmap.yaml b/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/charts/postgresql/templates/metrics-configmap.yaml new file mode 100644 index 0000000000..fa539582bb --- /dev/null +++ b/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/charts/postgresql/templates/metrics-configmap.yaml @@ -0,0 +1,14 @@ +{{- if and .Values.metrics.enabled .Values.metrics.customMetrics }} +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ template "postgresql.metricsCM" . }} + labels: + {{- include "common.labels.standard" . | nindent 4 }} + {{- if .Values.commonAnnotations }} + annotations: {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} + {{- end }} + namespace: {{ .Release.Namespace }} +data: + custom-metrics.yaml: {{ toYaml .Values.metrics.customMetrics | quote }} +{{- end }} diff --git a/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/charts/postgresql/templates/metrics-svc.yaml b/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/charts/postgresql/templates/metrics-svc.yaml new file mode 100644 index 0000000000..af8b67e2ff --- /dev/null +++ b/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/charts/postgresql/templates/metrics-svc.yaml @@ -0,0 +1,26 @@ +{{- if .Values.metrics.enabled }} +apiVersion: v1 +kind: Service +metadata: + name: {{ template "common.names.fullname" . }}-metrics + labels: + {{- include "common.labels.standard" . | nindent 4 }} + annotations: + {{- if .Values.commonAnnotations }} + {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} + {{- end }} + {{- toYaml .Values.metrics.service.annotations | nindent 4 }} + namespace: {{ .Release.Namespace }} +spec: + type: {{ .Values.metrics.service.type }} + {{- if and (eq .Values.metrics.service.type "LoadBalancer") .Values.metrics.service.loadBalancerIP }} + loadBalancerIP: {{ .Values.metrics.service.loadBalancerIP }} + {{- end }} + ports: + - name: http-metrics + port: 9187 + targetPort: http-metrics + selector: + {{- include "common.labels.matchLabels" . | nindent 4 }} + role: primary +{{- end }} diff --git a/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/charts/postgresql/templates/networkpolicy.yaml b/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/charts/postgresql/templates/networkpolicy.yaml new file mode 100644 index 0000000000..4f2740ea0c --- /dev/null +++ b/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/charts/postgresql/templates/networkpolicy.yaml @@ -0,0 +1,39 @@ +{{- if .Values.networkPolicy.enabled }} +kind: NetworkPolicy +apiVersion: {{ template "postgresql.networkPolicy.apiVersion" . }} +metadata: + name: {{ template "common.names.fullname" . }} + labels: + {{- include "common.labels.standard" . | nindent 4 }} + {{- if .Values.commonAnnotations }} + annotations: {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} + {{- end }} + namespace: {{ .Release.Namespace }} +spec: + podSelector: + matchLabels: + {{- include "common.labels.matchLabels" . | nindent 6 }} + ingress: + # Allow inbound connections + - ports: + - port: {{ template "postgresql.port" . }} + {{- if not .Values.networkPolicy.allowExternal }} + from: + - podSelector: + matchLabels: + {{ template "common.names.fullname" . }}-client: "true" + {{- if .Values.networkPolicy.explicitNamespacesSelector }} + namespaceSelector: +{{ toYaml .Values.networkPolicy.explicitNamespacesSelector | indent 12 }} + {{- end }} + - podSelector: + matchLabels: + {{- include "common.labels.matchLabels" . | nindent 14 }} + role: read + {{- end }} + {{- if .Values.metrics.enabled }} + # Allow prometheus scrapes + - ports: + - port: 9187 + {{- end }} +{{- end }} diff --git a/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/charts/postgresql/templates/podsecuritypolicy.yaml b/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/charts/postgresql/templates/podsecuritypolicy.yaml new file mode 100644 index 0000000000..0c49694fad --- /dev/null +++ b/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/charts/postgresql/templates/podsecuritypolicy.yaml @@ -0,0 +1,38 @@ +{{- if .Values.psp.create }} +apiVersion: {{ include "podsecuritypolicy.apiVersion" . }} +kind: PodSecurityPolicy +metadata: + name: {{ template "common.names.fullname" . }} + labels: + {{- include "common.labels.standard" . | nindent 4 }} + {{- if .Values.commonAnnotations }} + annotations: {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} + {{- end }} + namespace: {{ .Release.Namespace }} +spec: + privileged: false + volumes: + - 'configMap' + - 'secret' + - 'persistentVolumeClaim' + - 'emptyDir' + - 'projected' + hostNetwork: false + hostIPC: false + hostPID: false + runAsUser: + rule: 'RunAsAny' + seLinux: + rule: 'RunAsAny' + supplementalGroups: + rule: 'MustRunAs' + ranges: + - min: 1 + max: 65535 + fsGroup: + rule: 'MustRunAs' + ranges: + - min: 1 + max: 65535 + readOnlyRootFilesystem: false +{{- end }} diff --git a/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/charts/postgresql/templates/prometheusrule.yaml b/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/charts/postgresql/templates/prometheusrule.yaml new file mode 100644 index 0000000000..d0f408c78f --- /dev/null +++ b/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/charts/postgresql/templates/prometheusrule.yaml @@ -0,0 +1,23 @@ +{{- if and .Values.metrics.enabled .Values.metrics.prometheusRule.enabled }} +apiVersion: monitoring.coreos.com/v1 +kind: PrometheusRule +metadata: + name: {{ template "common.names.fullname" . }} +{{- with .Values.metrics.prometheusRule.namespace }} + namespace: {{ . }} +{{- end }} + labels: + {{- include "common.labels.standard" . | nindent 4 }} + {{- with .Values.metrics.prometheusRule.additionalLabels }} + {{- toYaml . | nindent 4 }} + {{- end }} + {{- if .Values.commonAnnotations }} + annotations: {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} + {{- end }} +spec: +{{- with .Values.metrics.prometheusRule.rules }} + groups: + - name: {{ template "postgresql.name" $ }} + rules: {{ tpl (toYaml .) $ | nindent 8 }} +{{- end }} +{{- end }} diff --git a/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/charts/postgresql/templates/role.yaml b/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/charts/postgresql/templates/role.yaml new file mode 100644 index 0000000000..017a5716b1 --- /dev/null +++ b/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/charts/postgresql/templates/role.yaml @@ -0,0 +1,20 @@ +{{- if .Values.rbac.create }} +kind: Role +apiVersion: {{ include "common.capabilities.rbac.apiVersion" . }} +metadata: + name: {{ template "common.names.fullname" . }} + labels: + {{- include "common.labels.standard" . | nindent 4 }} + {{- if .Values.commonAnnotations }} + annotations: {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} + {{- end }} + namespace: {{ .Release.Namespace }} +rules: + {{- if .Values.psp.create }} + - apiGroups: ["extensions"] + resources: ["podsecuritypolicies"] + verbs: ["use"] + resourceNames: + - {{ template "common.names.fullname" . }} + {{- end }} +{{- end }} diff --git a/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/charts/postgresql/templates/rolebinding.yaml b/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/charts/postgresql/templates/rolebinding.yaml new file mode 100644 index 0000000000..189775a15a --- /dev/null +++ b/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/charts/postgresql/templates/rolebinding.yaml @@ -0,0 +1,20 @@ +{{- if .Values.rbac.create }} +kind: RoleBinding +apiVersion: {{ include "common.capabilities.rbac.apiVersion" . }} +metadata: + name: {{ template "common.names.fullname" . }} + labels: + {{- include "common.labels.standard" . | nindent 4 }} + {{- if .Values.commonAnnotations }} + annotations: {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} + {{- end }} + namespace: {{ .Release.Namespace }} +roleRef: + kind: Role + name: {{ template "common.names.fullname" . }} + apiGroup: rbac.authorization.k8s.io +subjects: + - kind: ServiceAccount + name: {{ default (include "common.names.fullname" . ) .Values.serviceAccount.name }} + namespace: {{ .Release.Namespace }} +{{- end }} diff --git a/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/charts/postgresql/templates/secrets.yaml b/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/charts/postgresql/templates/secrets.yaml new file mode 100644 index 0000000000..d492cd593b --- /dev/null +++ b/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/charts/postgresql/templates/secrets.yaml @@ -0,0 +1,24 @@ +{{- if (include "postgresql.createSecret" .) }} +apiVersion: v1 +kind: Secret +metadata: + name: {{ template "common.names.fullname" . }} + labels: + {{- include "common.labels.standard" . | nindent 4 }} + {{- if .Values.commonAnnotations }} + annotations: {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} + {{- end }} + namespace: {{ .Release.Namespace }} +type: Opaque +data: + {{- if not (eq (include "postgresql.username" .) "postgres") }} + postgresql-postgres-password: {{ include "postgresql.postgres.password" . | b64enc | quote }} + {{- end }} + postgresql-password: {{ include "postgresql.password" . | b64enc | quote }} + {{- if .Values.replication.enabled }} + postgresql-replication-password: {{ include "postgresql.replication.password" . | b64enc | quote }} + {{- end }} + {{- if (and .Values.ldap.enabled .Values.ldap.bind_password)}} + postgresql-ldap-password: {{ .Values.ldap.bind_password | b64enc | quote }} + {{- end }} +{{- end -}} diff --git a/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/charts/postgresql/templates/serviceaccount.yaml b/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/charts/postgresql/templates/serviceaccount.yaml new file mode 100644 index 0000000000..03f0f50e7d --- /dev/null +++ b/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/charts/postgresql/templates/serviceaccount.yaml @@ -0,0 +1,12 @@ +{{- if and (.Values.serviceAccount.enabled) (not .Values.serviceAccount.name) }} +apiVersion: v1 +kind: ServiceAccount +metadata: + labels: + {{- include "common.labels.standard" . | nindent 4 }} + name: {{ template "common.names.fullname" . }} + {{- if .Values.commonAnnotations }} + annotations: {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} + {{- end }} + namespace: {{ .Release.Namespace }} +{{- end }} diff --git a/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/charts/postgresql/templates/servicemonitor.yaml b/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/charts/postgresql/templates/servicemonitor.yaml new file mode 100644 index 0000000000..587ce85b87 --- /dev/null +++ b/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/charts/postgresql/templates/servicemonitor.yaml @@ -0,0 +1,33 @@ +{{- if and .Values.metrics.enabled .Values.metrics.serviceMonitor.enabled }} +apiVersion: monitoring.coreos.com/v1 +kind: ServiceMonitor +metadata: + name: {{ include "common.names.fullname" . }} + {{- if .Values.metrics.serviceMonitor.namespace }} + namespace: {{ .Values.metrics.serviceMonitor.namespace }} + {{- end }} + labels: + {{- include "common.labels.standard" . | nindent 4 }} + {{- if .Values.metrics.serviceMonitor.additionalLabels }} + {{- toYaml .Values.metrics.serviceMonitor.additionalLabels | nindent 4 }} + {{- end }} + {{- if .Values.commonAnnotations }} + annotations: {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} + {{- end }} + +spec: + endpoints: + - port: http-metrics + {{- if .Values.metrics.serviceMonitor.interval }} + interval: {{ .Values.metrics.serviceMonitor.interval }} + {{- end }} + {{- if .Values.metrics.serviceMonitor.scrapeTimeout }} + scrapeTimeout: {{ .Values.metrics.serviceMonitor.scrapeTimeout }} + {{- end }} + namespaceSelector: + matchNames: + - {{ .Release.Namespace }} + selector: + matchLabels: + {{- include "common.labels.matchLabels" . | nindent 6 }} +{{- end }} diff --git a/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/charts/postgresql/templates/statefulset-readreplicas.yaml b/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/charts/postgresql/templates/statefulset-readreplicas.yaml new file mode 100644 index 0000000000..b038299bf6 --- /dev/null +++ b/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/charts/postgresql/templates/statefulset-readreplicas.yaml @@ -0,0 +1,411 @@ +{{- if .Values.replication.enabled }} +{{- $readReplicasResources := coalesce .Values.readReplicas.resources .Values.resources -}} +apiVersion: {{ include "common.capabilities.statefulset.apiVersion" . }} +kind: StatefulSet +metadata: + name: "{{ template "common.names.fullname" . }}-read" + labels: {{- include "common.labels.standard" . | nindent 4 }} + app.kubernetes.io/component: read +{{- with .Values.readReplicas.labels }} +{{ toYaml . | indent 4 }} +{{- end }} + annotations: + {{- if .Values.commonAnnotations }} + {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} + {{- end }} + {{- with .Values.readReplicas.annotations }} + {{- toYaml . | nindent 4 }} + {{- end }} + namespace: {{ .Release.Namespace }} +spec: + serviceName: {{ template "common.names.fullname" . }}-headless + replicas: {{ .Values.replication.readReplicas }} + selector: + matchLabels: + {{- include "common.labels.matchLabels" . | nindent 6 }} + role: read + template: + metadata: + name: {{ template "common.names.fullname" . }} + labels: + {{- include "common.labels.standard" . | nindent 8 }} + app.kubernetes.io/component: read + role: read +{{- with .Values.readReplicas.podLabels }} +{{ toYaml . | indent 8 }} +{{- end }} +{{- with .Values.readReplicas.podAnnotations }} + annotations: +{{ toYaml . | indent 8 }} +{{- end }} + spec: + {{- if .Values.schedulerName }} + schedulerName: "{{ .Values.schedulerName }}" + {{- end }} +{{- include "postgresql.imagePullSecrets" . | indent 6 }} + {{- if .Values.readReplicas.affinity }} + affinity: {{- include "common.tplvalues.render" (dict "value" .Values.readReplicas.affinity "context" $) | nindent 8 }} + {{- else }} + affinity: + podAffinity: {{- include "common.affinities.pods" (dict "type" .Values.readReplicas.podAffinityPreset "component" "read" "context" $) | nindent 10 }} + podAntiAffinity: {{- include "common.affinities.pods" (dict "type" .Values.readReplicas.podAntiAffinityPreset "component" "read" "context" $) | nindent 10 }} + nodeAffinity: {{- include "common.affinities.nodes" (dict "type" .Values.readReplicas.nodeAffinityPreset.type "key" .Values.readReplicas.nodeAffinityPreset.key "values" .Values.readReplicas.nodeAffinityPreset.values) | nindent 10 }} + {{- end }} + {{- if .Values.readReplicas.nodeSelector }} + nodeSelector: {{- include "common.tplvalues.render" (dict "value" .Values.readReplicas.nodeSelector "context" $) | nindent 8 }} + {{- end }} + {{- if .Values.readReplicas.tolerations }} + tolerations: {{- include "common.tplvalues.render" (dict "value" .Values.readReplicas.tolerations "context" $) | nindent 8 }} + {{- end }} + {{- if .Values.terminationGracePeriodSeconds }} + terminationGracePeriodSeconds: {{ .Values.terminationGracePeriodSeconds }} + {{- end }} + {{- if .Values.securityContext.enabled }} + securityContext: {{- omit .Values.securityContext "enabled" | toYaml | nindent 8 }} + {{- end }} + {{- if .Values.serviceAccount.enabled }} + serviceAccountName: {{ default (include "common.names.fullname" . ) .Values.serviceAccount.name}} + {{- end }} + {{- if or .Values.readReplicas.extraInitContainers (and .Values.volumePermissions.enabled (or .Values.persistence.enabled (and .Values.shmVolume.enabled .Values.shmVolume.chmod.enabled))) }} + initContainers: + {{- if and .Values.volumePermissions.enabled (or .Values.persistence.enabled (and .Values.shmVolume.enabled .Values.shmVolume.chmod.enabled) .Values.tls.enabled) }} + - name: init-chmod-data + image: {{ template "postgresql.volumePermissions.image" . }} + imagePullPolicy: {{ .Values.volumePermissions.image.pullPolicy | quote }} + {{- if .Values.resources }} + resources: {{- toYaml .Values.resources | nindent 12 }} + {{- end }} + command: + - /bin/sh + - -cx + - | + {{- if .Values.persistence.enabled }} + {{- if eq ( toString ( .Values.volumePermissions.securityContext.runAsUser )) "auto" }} + chown `id -u`:`id -G | cut -d " " -f2` {{ .Values.persistence.mountPath }} + {{- else }} + chown {{ .Values.containerSecurityContext.runAsUser }}:{{ .Values.securityContext.fsGroup }} {{ .Values.persistence.mountPath }} + {{- end }} + mkdir -p {{ .Values.persistence.mountPath }}/data {{- if (include "postgresql.mountConfigurationCM" .) }} {{ .Values.persistence.mountPath }}/conf {{- end }} + chmod 700 {{ .Values.persistence.mountPath }}/data {{- if (include "postgresql.mountConfigurationCM" .) }} {{ .Values.persistence.mountPath }}/conf {{- end }} + find {{ .Values.persistence.mountPath }} -mindepth 1 -maxdepth 1 {{- if not (include "postgresql.mountConfigurationCM" .) }} -not -name "conf" {{- end }} -not -name ".snapshot" -not -name "lost+found" | \ + {{- if eq ( toString ( .Values.volumePermissions.securityContext.runAsUser )) "auto" }} + xargs chown -R `id -u`:`id -G | cut -d " " -f2` + {{- else }} + xargs chown -R {{ .Values.containerSecurityContext.runAsUser }}:{{ .Values.securityContext.fsGroup }} + {{- end }} + {{- end }} + {{- if and .Values.shmVolume.enabled .Values.shmVolume.chmod.enabled }} + chmod -R 777 /dev/shm + {{- end }} + {{- if .Values.tls.enabled }} + cp /tmp/certs/* /opt/bitnami/postgresql/certs/ + {{- if eq ( toString ( .Values.volumePermissions.securityContext.runAsUser )) "auto" }} + chown -R `id -u`:`id -G | cut -d " " -f2` /opt/bitnami/postgresql/certs/ + {{- else }} + chown -R {{ .Values.containerSecurityContext.runAsUser }}:{{ .Values.securityContext.fsGroup }} /opt/bitnami/postgresql/certs/ + {{- end }} + chmod 600 {{ template "postgresql.tlsCertKey" . }} + {{- end }} + {{- if eq ( toString ( .Values.volumePermissions.securityContext.runAsUser )) "auto" }} + securityContext: {{- omit .Values.volumePermissions.securityContext "runAsUser" | toYaml | nindent 12 }} + {{- else }} + securityContext: {{- .Values.volumePermissions.securityContext | toYaml | nindent 12 }} + {{- end }} + volumeMounts: + {{ if .Values.persistence.enabled }} + - name: data + mountPath: {{ .Values.persistence.mountPath }} + subPath: {{ .Values.persistence.subPath }} + {{- end }} + {{- if .Values.shmVolume.enabled }} + - name: dshm + mountPath: /dev/shm + {{- end }} + {{- if .Values.tls.enabled }} + - name: raw-certificates + mountPath: /tmp/certs + - name: postgresql-certificates + mountPath: /opt/bitnami/postgresql/certs + {{- end }} + {{- end }} + {{- if .Values.readReplicas.extraInitContainers }} + {{- include "common.tplvalues.render" ( dict "value" .Values.readReplicas.extraInitContainers "context" $ ) | nindent 8 }} + {{- end }} + {{- end }} + {{- if .Values.readReplicas.priorityClassName }} + priorityClassName: {{ .Values.readReplicas.priorityClassName }} + {{- end }} + containers: + - name: {{ template "common.names.fullname" . }} + image: {{ template "postgresql.image" . }} + imagePullPolicy: "{{ .Values.image.pullPolicy }}" + {{- if $readReplicasResources }} + resources: {{- toYaml $readReplicasResources | nindent 12 }} + {{- end }} + {{- if .Values.containerSecurityContext.enabled }} + securityContext: {{- omit .Values.containerSecurityContext "enabled" | toYaml | nindent 12 }} + {{- end }} + env: + - name: BITNAMI_DEBUG + value: {{ ternary "true" "false" .Values.image.debug | quote }} + - name: POSTGRESQL_VOLUME_DIR + value: "{{ .Values.persistence.mountPath }}" + - name: POSTGRESQL_PORT_NUMBER + value: "{{ template "postgresql.port" . }}" + {{- if .Values.persistence.mountPath }} + - name: PGDATA + value: {{ .Values.postgresqlDataDir | quote }} + {{- end }} + - name: POSTGRES_REPLICATION_MODE + value: "slave" + - name: POSTGRES_REPLICATION_USER + value: {{ include "postgresql.replication.username" . | quote }} + {{- if .Values.usePasswordFile }} + - name: POSTGRES_REPLICATION_PASSWORD_FILE + value: "/opt/bitnami/postgresql/secrets/postgresql-replication-password" + {{- else }} + - name: POSTGRES_REPLICATION_PASSWORD + valueFrom: + secretKeyRef: + name: {{ template "postgresql.secretName" . }} + key: postgresql-replication-password + {{- end }} + - name: POSTGRES_CLUSTER_APP_NAME + value: {{ .Values.replication.applicationName }} + - name: POSTGRES_MASTER_HOST + value: {{ template "common.names.fullname" . }} + - name: POSTGRES_MASTER_PORT_NUMBER + value: {{ include "postgresql.port" . | quote }} + {{- if not (eq (include "postgresql.username" .) "postgres") }} + {{- if .Values.usePasswordFile }} + - name: POSTGRES_POSTGRES_PASSWORD_FILE + value: "/opt/bitnami/postgresql/secrets/postgresql-postgres-password" + {{- else }} + - name: POSTGRES_POSTGRES_PASSWORD + valueFrom: + secretKeyRef: + name: {{ template "postgresql.secretName" . }} + key: postgresql-postgres-password + {{- end }} + {{- end }} + {{- if .Values.usePasswordFile }} + - name: POSTGRES_PASSWORD_FILE + value: "/opt/bitnami/postgresql/secrets/postgresql-password" + {{- else }} + - name: POSTGRES_PASSWORD + valueFrom: + secretKeyRef: + name: {{ template "postgresql.secretName" . }} + key: postgresql-password + {{- end }} + - name: POSTGRESQL_ENABLE_TLS + value: {{ ternary "yes" "no" .Values.tls.enabled | quote }} + {{- if .Values.tls.enabled }} + - name: POSTGRESQL_TLS_PREFER_SERVER_CIPHERS + value: {{ ternary "yes" "no" .Values.tls.preferServerCiphers | quote }} + - name: POSTGRESQL_TLS_CERT_FILE + value: {{ template "postgresql.tlsCert" . }} + - name: POSTGRESQL_TLS_KEY_FILE + value: {{ template "postgresql.tlsCertKey" . }} + {{- if .Values.tls.certCAFilename }} + - name: POSTGRESQL_TLS_CA_FILE + value: {{ template "postgresql.tlsCACert" . }} + {{- end }} + {{- if .Values.tls.crlFilename }} + - name: POSTGRESQL_TLS_CRL_FILE + value: {{ template "postgresql.tlsCRL" . }} + {{- end }} + {{- end }} + - name: POSTGRESQL_LOG_HOSTNAME + value: {{ .Values.audit.logHostname | quote }} + - name: POSTGRESQL_LOG_CONNECTIONS + value: {{ .Values.audit.logConnections | quote }} + - name: POSTGRESQL_LOG_DISCONNECTIONS + value: {{ .Values.audit.logDisconnections | quote }} + {{- if .Values.audit.logLinePrefix }} + - name: POSTGRESQL_LOG_LINE_PREFIX + value: {{ .Values.audit.logLinePrefix | quote }} + {{- end }} + {{- if .Values.audit.logTimezone }} + - name: POSTGRESQL_LOG_TIMEZONE + value: {{ .Values.audit.logTimezone | quote }} + {{- end }} + {{- if .Values.audit.pgAuditLog }} + - name: POSTGRESQL_PGAUDIT_LOG + value: {{ .Values.audit.pgAuditLog | quote }} + {{- end }} + - name: POSTGRESQL_PGAUDIT_LOG_CATALOG + value: {{ .Values.audit.pgAuditLogCatalog | quote }} + - name: POSTGRESQL_CLIENT_MIN_MESSAGES + value: {{ .Values.audit.clientMinMessages | quote }} + - name: POSTGRESQL_SHARED_PRELOAD_LIBRARIES + value: {{ .Values.postgresqlSharedPreloadLibraries | quote }} + {{- if .Values.postgresqlMaxConnections }} + - name: POSTGRESQL_MAX_CONNECTIONS + value: {{ .Values.postgresqlMaxConnections | quote }} + {{- end }} + {{- if .Values.postgresqlPostgresConnectionLimit }} + - name: POSTGRESQL_POSTGRES_CONNECTION_LIMIT + value: {{ .Values.postgresqlPostgresConnectionLimit | quote }} + {{- end }} + {{- if .Values.postgresqlDbUserConnectionLimit }} + - name: POSTGRESQL_USERNAME_CONNECTION_LIMIT + value: {{ .Values.postgresqlDbUserConnectionLimit | quote }} + {{- end }} + {{- if .Values.postgresqlTcpKeepalivesInterval }} + - name: POSTGRESQL_TCP_KEEPALIVES_INTERVAL + value: {{ .Values.postgresqlTcpKeepalivesInterval | quote }} + {{- end }} + {{- if .Values.postgresqlTcpKeepalivesIdle }} + - name: POSTGRESQL_TCP_KEEPALIVES_IDLE + value: {{ .Values.postgresqlTcpKeepalivesIdle | quote }} + {{- end }} + {{- if .Values.postgresqlStatementTimeout }} + - name: POSTGRESQL_STATEMENT_TIMEOUT + value: {{ .Values.postgresqlStatementTimeout | quote }} + {{- end }} + {{- if .Values.postgresqlTcpKeepalivesCount }} + - name: POSTGRESQL_TCP_KEEPALIVES_COUNT + value: {{ .Values.postgresqlTcpKeepalivesCount | quote }} + {{- end }} + {{- if .Values.postgresqlPghbaRemoveFilters }} + - name: POSTGRESQL_PGHBA_REMOVE_FILTERS + value: {{ .Values.postgresqlPghbaRemoveFilters | quote }} + {{- end }} + ports: + - name: tcp-postgresql + containerPort: {{ template "postgresql.port" . }} + {{- if .Values.livenessProbe.enabled }} + livenessProbe: + exec: + command: + - /bin/sh + - -c + {{- if (include "postgresql.database" .) }} + - exec pg_isready -U {{ include "postgresql.username" . | quote }} -d "dbname={{ include "postgresql.database" . }} {{- if and .Values.tls.enabled .Values.tls.certCAFilename }} sslcert={{ include "postgresql.tlsCert" . }} sslkey={{ include "postgresql.tlsCertKey" . }}{{- end }}" -h 127.0.0.1 -p {{ template "postgresql.port" . }} + {{- else }} + - exec pg_isready -U {{ include "postgresql.username" . | quote }} {{- if and .Values.tls.enabled .Values.tls.certCAFilename }} -d "sslcert={{ include "postgresql.tlsCert" . }} sslkey={{ include "postgresql.tlsCertKey" . }}"{{- end }} -h 127.0.0.1 -p {{ template "postgresql.port" . }} + {{- end }} + initialDelaySeconds: {{ .Values.livenessProbe.initialDelaySeconds }} + periodSeconds: {{ .Values.livenessProbe.periodSeconds }} + timeoutSeconds: {{ .Values.livenessProbe.timeoutSeconds }} + successThreshold: {{ .Values.livenessProbe.successThreshold }} + failureThreshold: {{ .Values.livenessProbe.failureThreshold }} + {{- else if .Values.customLivenessProbe }} + livenessProbe: {{- include "common.tplvalues.render" (dict "value" .Values.customLivenessProbe "context" $) | nindent 12 }} + {{- end }} + {{- if .Values.readinessProbe.enabled }} + readinessProbe: + exec: + command: + - /bin/sh + - -c + - -e + {{- include "postgresql.readinessProbeCommand" . | nindent 16 }} + initialDelaySeconds: {{ .Values.readinessProbe.initialDelaySeconds }} + periodSeconds: {{ .Values.readinessProbe.periodSeconds }} + timeoutSeconds: {{ .Values.readinessProbe.timeoutSeconds }} + successThreshold: {{ .Values.readinessProbe.successThreshold }} + failureThreshold: {{ .Values.readinessProbe.failureThreshold }} + {{- else if .Values.customReadinessProbe }} + readinessProbe: {{- include "common.tplvalues.render" (dict "value" .Values.customReadinessProbe "context" $) | nindent 12 }} + {{- end }} + volumeMounts: + {{- if .Values.usePasswordFile }} + - name: postgresql-password + mountPath: /opt/bitnami/postgresql/secrets/ + {{- end }} + {{- if .Values.shmVolume.enabled }} + - name: dshm + mountPath: /dev/shm + {{- end }} + {{- if .Values.persistence.enabled }} + - name: data + mountPath: {{ .Values.persistence.mountPath }} + subPath: {{ .Values.persistence.subPath }} + {{ end }} + {{- if or (.Files.Glob "files/conf.d/*.conf") .Values.postgresqlExtendedConf .Values.extendedConfConfigMap }} + - name: postgresql-extended-config + mountPath: /bitnami/postgresql/conf/conf.d/ + {{- end }} + {{- if or (.Files.Glob "files/postgresql.conf") (.Files.Glob "files/pg_hba.conf") .Values.postgresqlConfiguration .Values.pgHbaConfiguration .Values.configurationConfigMap }} + - name: postgresql-config + mountPath: /bitnami/postgresql/conf + {{- end }} + {{- if .Values.tls.enabled }} + - name: postgresql-certificates + mountPath: /opt/bitnami/postgresql/certs + readOnly: true + {{- end }} + {{- if .Values.readReplicas.extraVolumeMounts }} + {{- toYaml .Values.readReplicas.extraVolumeMounts | nindent 12 }} + {{- end }} +{{- if .Values.readReplicas.sidecars }} +{{- include "common.tplvalues.render" ( dict "value" .Values.readReplicas.sidecars "context" $ ) | nindent 8 }} +{{- end }} + volumes: + {{- if .Values.usePasswordFile }} + - name: postgresql-password + secret: + secretName: {{ template "postgresql.secretName" . }} + {{- end }} + {{- if or (.Files.Glob "files/postgresql.conf") (.Files.Glob "files/pg_hba.conf") .Values.postgresqlConfiguration .Values.pgHbaConfiguration .Values.configurationConfigMap}} + - name: postgresql-config + configMap: + name: {{ template "postgresql.configurationCM" . }} + {{- end }} + {{- if or (.Files.Glob "files/conf.d/*.conf") .Values.postgresqlExtendedConf .Values.extendedConfConfigMap }} + - name: postgresql-extended-config + configMap: + name: {{ template "postgresql.extendedConfigurationCM" . }} + {{- end }} + {{- if .Values.tls.enabled }} + - name: raw-certificates + secret: + secretName: {{ required "A secret containing TLS certificates is required when TLS is enabled" .Values.tls.certificatesSecret }} + - name: postgresql-certificates + emptyDir: {} + {{- end }} + {{- if .Values.shmVolume.enabled }} + - name: dshm + emptyDir: + medium: Memory + sizeLimit: 1Gi + {{- end }} + {{- if or (not .Values.persistence.enabled) (not .Values.readReplicas.persistence.enabled) }} + - name: data + emptyDir: {} + {{- end }} + {{- if .Values.readReplicas.extraVolumes }} + {{- toYaml .Values.readReplicas.extraVolumes | nindent 8 }} + {{- end }} + updateStrategy: + type: {{ .Values.updateStrategy.type }} + {{- if (eq "Recreate" .Values.updateStrategy.type) }} + rollingUpdate: null + {{- end }} +{{- if and .Values.persistence.enabled .Values.readReplicas.persistence.enabled }} + volumeClaimTemplates: + - metadata: + name: data + {{- with .Values.persistence.annotations }} + annotations: + {{- range $key, $value := . }} + {{ $key }}: {{ $value }} + {{- end }} + {{- end }} + spec: + accessModes: + {{- range .Values.persistence.accessModes }} + - {{ . | quote }} + {{- end }} + resources: + requests: + storage: {{ .Values.persistence.size | quote }} + {{ include "common.storage.class" (dict "persistence" .Values.persistence "global" .Values.global) }} + + {{- if .Values.persistence.selector }} + selector: {{- include "common.tplvalues.render" (dict "value" .Values.persistence.selector "context" $) | nindent 10 }} + {{- end -}} +{{- end }} +{{- end }} diff --git a/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/charts/postgresql/templates/statefulset.yaml b/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/charts/postgresql/templates/statefulset.yaml new file mode 100644 index 0000000000..f8163fd99f --- /dev/null +++ b/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/charts/postgresql/templates/statefulset.yaml @@ -0,0 +1,609 @@ +apiVersion: {{ include "common.capabilities.statefulset.apiVersion" . }} +kind: StatefulSet +metadata: + name: {{ template "postgresql.primary.fullname" . }} + labels: {{- include "common.labels.standard" . | nindent 4 }} + app.kubernetes.io/component: primary + {{- with .Values.primary.labels }} + {{- toYaml . | nindent 4 }} + {{- end }} + annotations: + {{- if .Values.commonAnnotations }} + {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} + {{- end }} + {{- with .Values.primary.annotations }} + {{- toYaml . | nindent 4 }} + {{- end }} + namespace: {{ .Release.Namespace }} +spec: + serviceName: {{ template "common.names.fullname" . }}-headless + replicas: 1 + updateStrategy: + type: {{ .Values.updateStrategy.type }} + {{- if (eq "Recreate" .Values.updateStrategy.type) }} + rollingUpdate: null + {{- end }} + selector: + matchLabels: + {{- include "common.labels.matchLabels" . | nindent 6 }} + role: primary + template: + metadata: + name: {{ template "common.names.fullname" . }} + labels: + {{- include "common.labels.standard" . | nindent 8 }} + role: primary + app.kubernetes.io/component: primary + {{- with .Values.primary.podLabels }} + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.primary.podAnnotations }} + annotations: {{- toYaml . | nindent 8 }} + {{- end }} + spec: + {{- if .Values.schedulerName }} + schedulerName: "{{ .Values.schedulerName }}" + {{- end }} +{{- include "postgresql.imagePullSecrets" . | indent 6 }} + {{- if .Values.primary.affinity }} + affinity: {{- include "common.tplvalues.render" (dict "value" .Values.primary.affinity "context" $) | nindent 8 }} + {{- else }} + affinity: + podAffinity: {{- include "common.affinities.pods" (dict "type" .Values.primary.podAffinityPreset "component" "primary" "context" $) | nindent 10 }} + podAntiAffinity: {{- include "common.affinities.pods" (dict "type" .Values.primary.podAntiAffinityPreset "component" "primary" "context" $) | nindent 10 }} + nodeAffinity: {{- include "common.affinities.nodes" (dict "type" .Values.primary.nodeAffinityPreset.type "key" .Values.primary.nodeAffinityPreset.key "values" .Values.primary.nodeAffinityPreset.values) | nindent 10 }} + {{- end }} + {{- if .Values.primary.nodeSelector }} + nodeSelector: {{- include "common.tplvalues.render" (dict "value" .Values.primary.nodeSelector "context" $) | nindent 8 }} + {{- end }} + {{- if .Values.primary.tolerations }} + tolerations: {{- include "common.tplvalues.render" (dict "value" .Values.primary.tolerations "context" $) | nindent 8 }} + {{- end }} + {{- if .Values.terminationGracePeriodSeconds }} + terminationGracePeriodSeconds: {{ .Values.terminationGracePeriodSeconds }} + {{- end }} + {{- if .Values.securityContext.enabled }} + securityContext: {{- omit .Values.securityContext "enabled" | toYaml | nindent 8 }} + {{- end }} + {{- if .Values.serviceAccount.enabled }} + serviceAccountName: {{ default (include "common.names.fullname" . ) .Values.serviceAccount.name }} + {{- end }} + {{- if or .Values.primary.extraInitContainers (and .Values.volumePermissions.enabled (or .Values.persistence.enabled (and .Values.shmVolume.enabled .Values.shmVolume.chmod.enabled))) }} + initContainers: + {{- if and .Values.volumePermissions.enabled (or .Values.persistence.enabled (and .Values.shmVolume.enabled .Values.shmVolume.chmod.enabled) .Values.tls.enabled) }} + - name: init-chmod-data + image: {{ template "postgresql.volumePermissions.image" . }} + imagePullPolicy: {{ .Values.volumePermissions.image.pullPolicy | quote }} + {{- if .Values.resources }} + resources: {{- toYaml .Values.resources | nindent 12 }} + {{- end }} + command: + - /bin/sh + - -cx + - | + {{- if .Values.persistence.enabled }} + {{- if eq ( toString ( .Values.volumePermissions.securityContext.runAsUser )) "auto" }} + chown `id -u`:`id -G | cut -d " " -f2` {{ .Values.persistence.mountPath }} + {{- else }} + chown {{ .Values.containerSecurityContext.runAsUser }}:{{ .Values.securityContext.fsGroup }} {{ .Values.persistence.mountPath }} + {{- end }} + mkdir -p {{ .Values.persistence.mountPath }}/data {{- if (include "postgresql.mountConfigurationCM" .) }} {{ .Values.persistence.mountPath }}/conf {{- end }} + chmod 700 {{ .Values.persistence.mountPath }}/data {{- if (include "postgresql.mountConfigurationCM" .) }} {{ .Values.persistence.mountPath }}/conf {{- end }} + find {{ .Values.persistence.mountPath }} -mindepth 1 -maxdepth 1 {{- if not (include "postgresql.mountConfigurationCM" .) }} -not -name "conf" {{- end }} -not -name ".snapshot" -not -name "lost+found" | \ + {{- if eq ( toString ( .Values.volumePermissions.securityContext.runAsUser )) "auto" }} + xargs chown -R `id -u`:`id -G | cut -d " " -f2` + {{- else }} + xargs chown -R {{ .Values.containerSecurityContext.runAsUser }}:{{ .Values.securityContext.fsGroup }} + {{- end }} + {{- end }} + {{- if and .Values.shmVolume.enabled .Values.shmVolume.chmod.enabled }} + chmod -R 777 /dev/shm + {{- end }} + {{- if .Values.tls.enabled }} + cp /tmp/certs/* /opt/bitnami/postgresql/certs/ + {{- if eq ( toString ( .Values.volumePermissions.securityContext.runAsUser )) "auto" }} + chown -R `id -u`:`id -G | cut -d " " -f2` /opt/bitnami/postgresql/certs/ + {{- else }} + chown -R {{ .Values.containerSecurityContext.runAsUser }}:{{ .Values.securityContext.fsGroup }} /opt/bitnami/postgresql/certs/ + {{- end }} + chmod 600 {{ template "postgresql.tlsCertKey" . }} + {{- end }} + {{- if eq ( toString ( .Values.volumePermissions.securityContext.runAsUser )) "auto" }} + securityContext: {{- omit .Values.volumePermissions.securityContext "runAsUser" | toYaml | nindent 12 }} + {{- else }} + securityContext: {{- .Values.volumePermissions.securityContext | toYaml | nindent 12 }} + {{- end }} + volumeMounts: + {{- if .Values.persistence.enabled }} + - name: data + mountPath: {{ .Values.persistence.mountPath }} + subPath: {{ .Values.persistence.subPath }} + {{- end }} + {{- if .Values.shmVolume.enabled }} + - name: dshm + mountPath: /dev/shm + {{- end }} + {{- if .Values.tls.enabled }} + - name: raw-certificates + mountPath: /tmp/certs + - name: postgresql-certificates + mountPath: /opt/bitnami/postgresql/certs + {{- end }} + {{- end }} + {{- if .Values.primary.extraInitContainers }} + {{- include "common.tplvalues.render" ( dict "value" .Values.primary.extraInitContainers "context" $ ) | nindent 8 }} + {{- end }} + {{- end }} + {{- if .Values.primary.priorityClassName }} + priorityClassName: {{ .Values.primary.priorityClassName }} + {{- end }} + containers: + - name: {{ template "common.names.fullname" . }} + image: {{ template "postgresql.image" . }} + imagePullPolicy: "{{ .Values.image.pullPolicy }}" + {{- if .Values.resources }} + resources: {{- toYaml .Values.resources | nindent 12 }} + {{- end }} + {{- if .Values.containerSecurityContext.enabled }} + securityContext: {{- omit .Values.containerSecurityContext "enabled" | toYaml | nindent 12 }} + {{- end }} + env: + - name: BITNAMI_DEBUG + value: {{ ternary "true" "false" .Values.image.debug | quote }} + - name: POSTGRESQL_PORT_NUMBER + value: "{{ template "postgresql.port" . }}" + - name: POSTGRESQL_VOLUME_DIR + value: "{{ .Values.persistence.mountPath }}" + {{- if .Values.postgresqlInitdbArgs }} + - name: POSTGRES_INITDB_ARGS + value: {{ .Values.postgresqlInitdbArgs | quote }} + {{- end }} + {{- if .Values.postgresqlInitdbWalDir }} + - name: POSTGRES_INITDB_WALDIR + value: {{ .Values.postgresqlInitdbWalDir | quote }} + {{- end }} + {{- if .Values.initdbUser }} + - name: POSTGRESQL_INITSCRIPTS_USERNAME + value: {{ .Values.initdbUser }} + {{- end }} + {{- if .Values.initdbPassword }} + - name: POSTGRESQL_INITSCRIPTS_PASSWORD + value: {{ .Values.initdbPassword }} + {{- end }} + {{- if .Values.persistence.mountPath }} + - name: PGDATA + value: {{ .Values.postgresqlDataDir | quote }} + {{- end }} + {{- if .Values.primaryAsStandBy.enabled }} + - name: POSTGRES_MASTER_HOST + value: {{ .Values.primaryAsStandBy.primaryHost }} + - name: POSTGRES_MASTER_PORT_NUMBER + value: {{ .Values.primaryAsStandBy.primaryPort | quote }} + {{- end }} + {{- if or .Values.replication.enabled .Values.primaryAsStandBy.enabled }} + - name: POSTGRES_REPLICATION_MODE + {{- if .Values.primaryAsStandBy.enabled }} + value: "slave" + {{- else }} + value: "master" + {{- end }} + - name: POSTGRES_REPLICATION_USER + value: {{ include "postgresql.replication.username" . | quote }} + {{- if .Values.usePasswordFile }} + - name: POSTGRES_REPLICATION_PASSWORD_FILE + value: "/opt/bitnami/postgresql/secrets/postgresql-replication-password" + {{- else }} + - name: POSTGRES_REPLICATION_PASSWORD + valueFrom: + secretKeyRef: + name: {{ template "postgresql.secretName" . }} + key: postgresql-replication-password + {{- end }} + {{- if not (eq .Values.replication.synchronousCommit "off")}} + - name: POSTGRES_SYNCHRONOUS_COMMIT_MODE + value: {{ .Values.replication.synchronousCommit | quote }} + - name: POSTGRES_NUM_SYNCHRONOUS_REPLICAS + value: {{ .Values.replication.numSynchronousReplicas | quote }} + {{- end }} + - name: POSTGRES_CLUSTER_APP_NAME + value: {{ .Values.replication.applicationName }} + {{- end }} + {{- if not (eq (include "postgresql.username" .) "postgres") }} + {{- if .Values.usePasswordFile }} + - name: POSTGRES_POSTGRES_PASSWORD_FILE + value: "/opt/bitnami/postgresql/secrets/postgresql-postgres-password" + {{- else }} + - name: POSTGRES_POSTGRES_PASSWORD + valueFrom: + secretKeyRef: + name: {{ template "postgresql.secretName" . }} + key: postgresql-postgres-password + {{- end }} + {{- end }} + - name: POSTGRES_USER + value: {{ include "postgresql.username" . | quote }} + {{- if .Values.usePasswordFile }} + - name: POSTGRES_PASSWORD_FILE + value: "/opt/bitnami/postgresql/secrets/postgresql-password" + {{- else }} + - name: POSTGRES_PASSWORD + valueFrom: + secretKeyRef: + name: {{ template "postgresql.secretName" . }} + key: postgresql-password + {{- end }} + {{- if (include "postgresql.database" .) }} + - name: POSTGRES_DB + value: {{ (include "postgresql.database" .) | quote }} + {{- end }} + {{- if .Values.extraEnv }} + {{- include "common.tplvalues.render" (dict "value" .Values.extraEnv "context" $) | nindent 12 }} + {{- end }} + - name: POSTGRESQL_ENABLE_LDAP + value: {{ ternary "yes" "no" .Values.ldap.enabled | quote }} + {{- if .Values.ldap.enabled }} + - name: POSTGRESQL_LDAP_SERVER + value: {{ .Values.ldap.server }} + - name: POSTGRESQL_LDAP_PORT + value: {{ .Values.ldap.port | quote }} + - name: POSTGRESQL_LDAP_SCHEME + value: {{ .Values.ldap.scheme }} + {{- if .Values.ldap.tls }} + - name: POSTGRESQL_LDAP_TLS + value: "1" + {{- end }} + - name: POSTGRESQL_LDAP_PREFIX + value: {{ .Values.ldap.prefix | quote }} + - name: POSTGRESQL_LDAP_SUFFIX + value: {{ .Values.ldap.suffix | quote }} + - name: POSTGRESQL_LDAP_BASE_DN + value: {{ .Values.ldap.baseDN }} + - name: POSTGRESQL_LDAP_BIND_DN + value: {{ .Values.ldap.bindDN }} + {{- if (not (empty .Values.ldap.bind_password)) }} + - name: POSTGRESQL_LDAP_BIND_PASSWORD + valueFrom: + secretKeyRef: + name: {{ template "postgresql.secretName" . }} + key: postgresql-ldap-password + {{- end}} + - name: POSTGRESQL_LDAP_SEARCH_ATTR + value: {{ .Values.ldap.search_attr }} + - name: POSTGRESQL_LDAP_SEARCH_FILTER + value: {{ .Values.ldap.search_filter }} + - name: POSTGRESQL_LDAP_URL + value: {{ .Values.ldap.url }} + {{- end}} + - name: POSTGRESQL_ENABLE_TLS + value: {{ ternary "yes" "no" .Values.tls.enabled | quote }} + {{- if .Values.tls.enabled }} + - name: POSTGRESQL_TLS_PREFER_SERVER_CIPHERS + value: {{ ternary "yes" "no" .Values.tls.preferServerCiphers | quote }} + - name: POSTGRESQL_TLS_CERT_FILE + value: {{ template "postgresql.tlsCert" . }} + - name: POSTGRESQL_TLS_KEY_FILE + value: {{ template "postgresql.tlsCertKey" . }} + {{- if .Values.tls.certCAFilename }} + - name: POSTGRESQL_TLS_CA_FILE + value: {{ template "postgresql.tlsCACert" . }} + {{- end }} + {{- if .Values.tls.crlFilename }} + - name: POSTGRESQL_TLS_CRL_FILE + value: {{ template "postgresql.tlsCRL" . }} + {{- end }} + {{- end }} + - name: POSTGRESQL_LOG_HOSTNAME + value: {{ .Values.audit.logHostname | quote }} + - name: POSTGRESQL_LOG_CONNECTIONS + value: {{ .Values.audit.logConnections | quote }} + - name: POSTGRESQL_LOG_DISCONNECTIONS + value: {{ .Values.audit.logDisconnections | quote }} + {{- if .Values.audit.logLinePrefix }} + - name: POSTGRESQL_LOG_LINE_PREFIX + value: {{ .Values.audit.logLinePrefix | quote }} + {{- end }} + {{- if .Values.audit.logTimezone }} + - name: POSTGRESQL_LOG_TIMEZONE + value: {{ .Values.audit.logTimezone | quote }} + {{- end }} + {{- if .Values.audit.pgAuditLog }} + - name: POSTGRESQL_PGAUDIT_LOG + value: {{ .Values.audit.pgAuditLog | quote }} + {{- end }} + - name: POSTGRESQL_PGAUDIT_LOG_CATALOG + value: {{ .Values.audit.pgAuditLogCatalog | quote }} + - name: POSTGRESQL_CLIENT_MIN_MESSAGES + value: {{ .Values.audit.clientMinMessages | quote }} + - name: POSTGRESQL_SHARED_PRELOAD_LIBRARIES + value: {{ .Values.postgresqlSharedPreloadLibraries | quote }} + {{- if .Values.postgresqlMaxConnections }} + - name: POSTGRESQL_MAX_CONNECTIONS + value: {{ .Values.postgresqlMaxConnections | quote }} + {{- end }} + {{- if .Values.postgresqlPostgresConnectionLimit }} + - name: POSTGRESQL_POSTGRES_CONNECTION_LIMIT + value: {{ .Values.postgresqlPostgresConnectionLimit | quote }} + {{- end }} + {{- if .Values.postgresqlDbUserConnectionLimit }} + - name: POSTGRESQL_USERNAME_CONNECTION_LIMIT + value: {{ .Values.postgresqlDbUserConnectionLimit | quote }} + {{- end }} + {{- if .Values.postgresqlTcpKeepalivesInterval }} + - name: POSTGRESQL_TCP_KEEPALIVES_INTERVAL + value: {{ .Values.postgresqlTcpKeepalivesInterval | quote }} + {{- end }} + {{- if .Values.postgresqlTcpKeepalivesIdle }} + - name: POSTGRESQL_TCP_KEEPALIVES_IDLE + value: {{ .Values.postgresqlTcpKeepalivesIdle | quote }} + {{- end }} + {{- if .Values.postgresqlStatementTimeout }} + - name: POSTGRESQL_STATEMENT_TIMEOUT + value: {{ .Values.postgresqlStatementTimeout | quote }} + {{- end }} + {{- if .Values.postgresqlTcpKeepalivesCount }} + - name: POSTGRESQL_TCP_KEEPALIVES_COUNT + value: {{ .Values.postgresqlTcpKeepalivesCount | quote }} + {{- end }} + {{- if .Values.postgresqlPghbaRemoveFilters }} + - name: POSTGRESQL_PGHBA_REMOVE_FILTERS + value: {{ .Values.postgresqlPghbaRemoveFilters | quote }} + {{- end }} + {{- if .Values.extraEnvVarsCM }} + envFrom: + - configMapRef: + name: {{ tpl .Values.extraEnvVarsCM . }} + {{- end }} + ports: + - name: tcp-postgresql + containerPort: {{ template "postgresql.port" . }} + {{- if .Values.startupProbe.enabled }} + startupProbe: + exec: + command: + - /bin/sh + - -c + {{- if (include "postgresql.database" .) }} + - exec pg_isready -U {{ include "postgresql.username" . | quote }} -d "dbname={{ include "postgresql.database" . }} {{- if and .Values.tls.enabled .Values.tls.certCAFilename }} sslcert={{ include "postgresql.tlsCert" . }} sslkey={{ include "postgresql.tlsCertKey" . }}{{- end }}" -h 127.0.0.1 -p {{ template "postgresql.port" . }} + {{- else }} + - exec pg_isready -U {{ include "postgresql.username" . | quote }} {{- if and .Values.tls.enabled .Values.tls.certCAFilename }} -d "sslcert={{ include "postgresql.tlsCert" . }} sslkey={{ include "postgresql.tlsCertKey" . }}"{{- end }} -h 127.0.0.1 -p {{ template "postgresql.port" . }} + {{- end }} + initialDelaySeconds: {{ .Values.startupProbe.initialDelaySeconds }} + periodSeconds: {{ .Values.startupProbe.periodSeconds }} + timeoutSeconds: {{ .Values.startupProbe.timeoutSeconds }} + successThreshold: {{ .Values.startupProbe.successThreshold }} + failureThreshold: {{ .Values.startupProbe.failureThreshold }} + {{- else if .Values.customStartupProbe }} + startupProbe: {{- include "common.tplvalues.render" (dict "value" .Values.customStartupProbe "context" $) | nindent 12 }} + {{- end }} + {{- if .Values.livenessProbe.enabled }} + livenessProbe: + exec: + command: + - /bin/sh + - -c + {{- if (include "postgresql.database" .) }} + - exec pg_isready -U {{ include "postgresql.username" . | quote }} -d "dbname={{ include "postgresql.database" . }} {{- if and .Values.tls.enabled .Values.tls.certCAFilename }} sslcert={{ include "postgresql.tlsCert" . }} sslkey={{ include "postgresql.tlsCertKey" . }}{{- end }}" -h 127.0.0.1 -p {{ template "postgresql.port" . }} + {{- else }} + - exec pg_isready -U {{ include "postgresql.username" . | quote }} {{- if and .Values.tls.enabled .Values.tls.certCAFilename }} -d "sslcert={{ include "postgresql.tlsCert" . }} sslkey={{ include "postgresql.tlsCertKey" . }}"{{- end }} -h 127.0.0.1 -p {{ template "postgresql.port" . }} + {{- end }} + initialDelaySeconds: {{ .Values.livenessProbe.initialDelaySeconds }} + periodSeconds: {{ .Values.livenessProbe.periodSeconds }} + timeoutSeconds: {{ .Values.livenessProbe.timeoutSeconds }} + successThreshold: {{ .Values.livenessProbe.successThreshold }} + failureThreshold: {{ .Values.livenessProbe.failureThreshold }} + {{- else if .Values.customLivenessProbe }} + livenessProbe: {{- include "common.tplvalues.render" (dict "value" .Values.customLivenessProbe "context" $) | nindent 12 }} + {{- end }} + {{- if .Values.readinessProbe.enabled }} + readinessProbe: + exec: + command: + - /bin/sh + - -c + - -e + {{- include "postgresql.readinessProbeCommand" . | nindent 16 }} + initialDelaySeconds: {{ .Values.readinessProbe.initialDelaySeconds }} + periodSeconds: {{ .Values.readinessProbe.periodSeconds }} + timeoutSeconds: {{ .Values.readinessProbe.timeoutSeconds }} + successThreshold: {{ .Values.readinessProbe.successThreshold }} + failureThreshold: {{ .Values.readinessProbe.failureThreshold }} + {{- else if .Values.customReadinessProbe }} + readinessProbe: {{- include "common.tplvalues.render" (dict "value" .Values.customReadinessProbe "context" $) | nindent 12 }} + {{- end }} + volumeMounts: + {{- if or (.Files.Glob "files/docker-entrypoint-initdb.d/*.{sh,sql,sql.gz}") .Values.initdbScriptsConfigMap .Values.initdbScripts }} + - name: custom-init-scripts + mountPath: /docker-entrypoint-initdb.d/ + {{- end }} + {{- if .Values.initdbScriptsSecret }} + - name: custom-init-scripts-secret + mountPath: /docker-entrypoint-initdb.d/secret + {{- end }} + {{- if or (.Files.Glob "files/conf.d/*.conf") .Values.postgresqlExtendedConf .Values.extendedConfConfigMap }} + - name: postgresql-extended-config + mountPath: /bitnami/postgresql/conf/conf.d/ + {{- end }} + {{- if .Values.usePasswordFile }} + - name: postgresql-password + mountPath: /opt/bitnami/postgresql/secrets/ + {{- end }} + {{- if .Values.tls.enabled }} + - name: postgresql-certificates + mountPath: /opt/bitnami/postgresql/certs + readOnly: true + {{- end }} + {{- if .Values.shmVolume.enabled }} + - name: dshm + mountPath: /dev/shm + {{- end }} + {{- if .Values.persistence.enabled }} + - name: data + mountPath: {{ .Values.persistence.mountPath }} + subPath: {{ .Values.persistence.subPath }} + {{- end }} + {{- if or (.Files.Glob "files/postgresql.conf") (.Files.Glob "files/pg_hba.conf") .Values.postgresqlConfiguration .Values.pgHbaConfiguration .Values.configurationConfigMap }} + - name: postgresql-config + mountPath: /bitnami/postgresql/conf + {{- end }} + {{- if .Values.primary.extraVolumeMounts }} + {{- toYaml .Values.primary.extraVolumeMounts | nindent 12 }} + {{- end }} +{{- if .Values.primary.sidecars }} +{{- include "common.tplvalues.render" ( dict "value" .Values.primary.sidecars "context" $ ) | nindent 8 }} +{{- end }} +{{- if .Values.metrics.enabled }} + - name: metrics + image: {{ template "postgresql.metrics.image" . }} + imagePullPolicy: {{ .Values.metrics.image.pullPolicy | quote }} + {{- if .Values.metrics.securityContext.enabled }} + securityContext: {{- omit .Values.metrics.securityContext "enabled" | toYaml | nindent 12 }} + {{- end }} + env: + {{- $database := required "In order to enable metrics you need to specify a database (.Values.postgresqlDatabase or .Values.global.postgresql.postgresqlDatabase)" (include "postgresql.database" .) }} + {{- $sslmode := ternary "require" "disable" .Values.tls.enabled }} + {{- if and .Values.tls.enabled .Values.tls.certCAFilename }} + - name: DATA_SOURCE_NAME + value: {{ printf "host=127.0.0.1 port=%d user=%s sslmode=%s sslcert=%s sslkey=%s" (int (include "postgresql.port" .)) (include "postgresql.username" .) $sslmode (include "postgresql.tlsCert" .) (include "postgresql.tlsCertKey" .) }} + {{- else }} + - name: DATA_SOURCE_URI + value: {{ printf "127.0.0.1:%d/%s?sslmode=%s" (int (include "postgresql.port" .)) $database $sslmode }} + {{- end }} + {{- if .Values.usePasswordFile }} + - name: DATA_SOURCE_PASS_FILE + value: "/opt/bitnami/postgresql/secrets/postgresql-password" + {{- else }} + - name: DATA_SOURCE_PASS + valueFrom: + secretKeyRef: + name: {{ template "postgresql.secretName" . }} + key: postgresql-password + {{- end }} + - name: DATA_SOURCE_USER + value: {{ template "postgresql.username" . }} + {{- if .Values.metrics.extraEnvVars }} + {{- include "common.tplvalues.render" (dict "value" .Values.metrics.extraEnvVars "context" $) | nindent 12 }} + {{- end }} + {{- if .Values.livenessProbe.enabled }} + livenessProbe: + httpGet: + path: / + port: http-metrics + initialDelaySeconds: {{ .Values.metrics.livenessProbe.initialDelaySeconds }} + periodSeconds: {{ .Values.metrics.livenessProbe.periodSeconds }} + timeoutSeconds: {{ .Values.metrics.livenessProbe.timeoutSeconds }} + successThreshold: {{ .Values.metrics.livenessProbe.successThreshold }} + failureThreshold: {{ .Values.metrics.livenessProbe.failureThreshold }} + {{- end }} + {{- if .Values.readinessProbe.enabled }} + readinessProbe: + httpGet: + path: / + port: http-metrics + initialDelaySeconds: {{ .Values.metrics.readinessProbe.initialDelaySeconds }} + periodSeconds: {{ .Values.metrics.readinessProbe.periodSeconds }} + timeoutSeconds: {{ .Values.metrics.readinessProbe.timeoutSeconds }} + successThreshold: {{ .Values.metrics.readinessProbe.successThreshold }} + failureThreshold: {{ .Values.metrics.readinessProbe.failureThreshold }} + {{- end }} + volumeMounts: + {{- if .Values.usePasswordFile }} + - name: postgresql-password + mountPath: /opt/bitnami/postgresql/secrets/ + {{- end }} + {{- if .Values.tls.enabled }} + - name: postgresql-certificates + mountPath: /opt/bitnami/postgresql/certs + readOnly: true + {{- end }} + {{- if .Values.metrics.customMetrics }} + - name: custom-metrics + mountPath: /conf + readOnly: true + args: ["--extend.query-path", "/conf/custom-metrics.yaml"] + {{- end }} + ports: + - name: http-metrics + containerPort: 9187 + {{- if .Values.metrics.resources }} + resources: {{- toYaml .Values.metrics.resources | nindent 12 }} + {{- end }} +{{- end }} + volumes: + {{- if or (.Files.Glob "files/postgresql.conf") (.Files.Glob "files/pg_hba.conf") .Values.postgresqlConfiguration .Values.pgHbaConfiguration .Values.configurationConfigMap}} + - name: postgresql-config + configMap: + name: {{ template "postgresql.configurationCM" . }} + {{- end }} + {{- if or (.Files.Glob "files/conf.d/*.conf") .Values.postgresqlExtendedConf .Values.extendedConfConfigMap }} + - name: postgresql-extended-config + configMap: + name: {{ template "postgresql.extendedConfigurationCM" . }} + {{- end }} + {{- if .Values.usePasswordFile }} + - name: postgresql-password + secret: + secretName: {{ template "postgresql.secretName" . }} + {{- end }} + {{- if or (.Files.Glob "files/docker-entrypoint-initdb.d/*.{sh,sql,sql.gz}") .Values.initdbScriptsConfigMap .Values.initdbScripts }} + - name: custom-init-scripts + configMap: + name: {{ template "postgresql.initdbScriptsCM" . }} + {{- end }} + {{- if .Values.initdbScriptsSecret }} + - name: custom-init-scripts-secret + secret: + secretName: {{ template "postgresql.initdbScriptsSecret" . }} + {{- end }} + {{- if .Values.tls.enabled }} + - name: raw-certificates + secret: + secretName: {{ required "A secret containing TLS certificates is required when TLS is enabled" .Values.tls.certificatesSecret }} + - name: postgresql-certificates + emptyDir: {} + {{- end }} + {{- if .Values.primary.extraVolumes }} + {{- toYaml .Values.primary.extraVolumes | nindent 8 }} + {{- end }} + {{- if and .Values.metrics.enabled .Values.metrics.customMetrics }} + - name: custom-metrics + configMap: + name: {{ template "postgresql.metricsCM" . }} + {{- end }} + {{- if .Values.shmVolume.enabled }} + - name: dshm + emptyDir: + medium: Memory + sizeLimit: 1Gi + {{- end }} +{{- if and .Values.persistence.enabled .Values.persistence.existingClaim }} + - name: data + persistentVolumeClaim: +{{- with .Values.persistence.existingClaim }} + claimName: {{ tpl . $ }} +{{- end }} +{{- else if not .Values.persistence.enabled }} + - name: data + emptyDir: {} +{{- else if and .Values.persistence.enabled (not .Values.persistence.existingClaim) }} + volumeClaimTemplates: + - metadata: + name: data + {{- with .Values.persistence.annotations }} + annotations: + {{- range $key, $value := . }} + {{ $key }}: {{ $value }} + {{- end }} + {{- end }} + spec: + accessModes: + {{- range .Values.persistence.accessModes }} + - {{ . | quote }} + {{- end }} + resources: + requests: + storage: {{ .Values.persistence.size | quote }} + {{ include "common.storage.class" (dict "persistence" .Values.persistence "global" .Values.global) }} + {{- if .Values.persistence.selector }} + selector: {{- include "common.tplvalues.render" (dict "value" .Values.persistence.selector "context" $) | nindent 10 }} + {{- end -}} +{{- end }} diff --git a/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/charts/postgresql/templates/svc-headless.yaml b/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/charts/postgresql/templates/svc-headless.yaml new file mode 100644 index 0000000000..6f5f3b9ee4 --- /dev/null +++ b/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/charts/postgresql/templates/svc-headless.yaml @@ -0,0 +1,28 @@ +apiVersion: v1 +kind: Service +metadata: + name: {{ template "common.names.fullname" . }}-headless + labels: + {{- include "common.labels.standard" . | nindent 4 }} + {{- if .Values.commonAnnotations }} + annotations: {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} + {{- end }} + # Use this annotation in addition to the actual publishNotReadyAddresses + # field below because the annotation will stop being respected soon but the + # field is broken in some versions of Kubernetes: + # https://github.com/kubernetes/kubernetes/issues/58662 + service.alpha.kubernetes.io/tolerate-unready-endpoints: "true" + namespace: {{ .Release.Namespace }} +spec: + type: ClusterIP + clusterIP: None + # We want all pods in the StatefulSet to have their addresses published for + # the sake of the other Postgresql pods even before they're ready, since they + # have to be able to talk to each other in order to become ready. + publishNotReadyAddresses: true + ports: + - name: tcp-postgresql + port: {{ template "postgresql.port" . }} + targetPort: tcp-postgresql + selector: + {{- include "common.labels.matchLabels" . | nindent 4 }} diff --git a/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/charts/postgresql/templates/svc-read.yaml b/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/charts/postgresql/templates/svc-read.yaml new file mode 100644 index 0000000000..56195ea1e6 --- /dev/null +++ b/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/charts/postgresql/templates/svc-read.yaml @@ -0,0 +1,43 @@ +{{- if .Values.replication.enabled }} +{{- $serviceAnnotations := coalesce .Values.readReplicas.service.annotations .Values.service.annotations -}} +{{- $serviceType := coalesce .Values.readReplicas.service.type .Values.service.type -}} +{{- $serviceLoadBalancerIP := coalesce .Values.readReplicas.service.loadBalancerIP .Values.service.loadBalancerIP -}} +{{- $serviceLoadBalancerSourceRanges := coalesce .Values.readReplicas.service.loadBalancerSourceRanges .Values.service.loadBalancerSourceRanges -}} +{{- $serviceClusterIP := coalesce .Values.readReplicas.service.clusterIP .Values.service.clusterIP -}} +{{- $serviceNodePort := coalesce .Values.readReplicas.service.nodePort .Values.service.nodePort -}} +apiVersion: v1 +kind: Service +metadata: + name: {{ template "common.names.fullname" . }}-read + labels: + {{- include "common.labels.standard" . | nindent 4 }} + annotations: + {{- if .Values.commonAnnotations }} + {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} + {{- end }} + {{- if $serviceAnnotations }} + {{- include "common.tplvalues.render" (dict "value" $serviceAnnotations "context" $) | nindent 4 }} + {{- end }} + namespace: {{ .Release.Namespace }} +spec: + type: {{ $serviceType }} + {{- if and $serviceLoadBalancerIP (eq $serviceType "LoadBalancer") }} + loadBalancerIP: {{ $serviceLoadBalancerIP }} + {{- end }} + {{- if and (eq $serviceType "LoadBalancer") $serviceLoadBalancerSourceRanges }} + loadBalancerSourceRanges: {{- include "common.tplvalues.render" (dict "value" $serviceLoadBalancerSourceRanges "context" $) | nindent 4 }} + {{- end }} + {{- if and (eq $serviceType "ClusterIP") $serviceClusterIP }} + clusterIP: {{ $serviceClusterIP }} + {{- end }} + ports: + - name: tcp-postgresql + port: {{ template "postgresql.port" . }} + targetPort: tcp-postgresql + {{- if $serviceNodePort }} + nodePort: {{ $serviceNodePort }} + {{- end }} + selector: + {{- include "common.labels.matchLabels" . | nindent 4 }} + role: read +{{- end }} diff --git a/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/charts/postgresql/templates/svc.yaml b/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/charts/postgresql/templates/svc.yaml new file mode 100644 index 0000000000..a29431b6a4 --- /dev/null +++ b/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/charts/postgresql/templates/svc.yaml @@ -0,0 +1,41 @@ +{{- $serviceAnnotations := coalesce .Values.primary.service.annotations .Values.service.annotations -}} +{{- $serviceType := coalesce .Values.primary.service.type .Values.service.type -}} +{{- $serviceLoadBalancerIP := coalesce .Values.primary.service.loadBalancerIP .Values.service.loadBalancerIP -}} +{{- $serviceLoadBalancerSourceRanges := coalesce .Values.primary.service.loadBalancerSourceRanges .Values.service.loadBalancerSourceRanges -}} +{{- $serviceClusterIP := coalesce .Values.primary.service.clusterIP .Values.service.clusterIP -}} +{{- $serviceNodePort := coalesce .Values.primary.service.nodePort .Values.service.nodePort -}} +apiVersion: v1 +kind: Service +metadata: + name: {{ template "common.names.fullname" . }} + labels: + {{- include "common.labels.standard" . | nindent 4 }} + annotations: + {{- if .Values.commonAnnotations }} + {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} + {{- end }} + {{- if $serviceAnnotations }} + {{- include "common.tplvalues.render" (dict "value" $serviceAnnotations "context" $) | nindent 4 }} + {{- end }} + namespace: {{ .Release.Namespace }} +spec: + type: {{ $serviceType }} + {{- if and $serviceLoadBalancerIP (eq $serviceType "LoadBalancer") }} + loadBalancerIP: {{ $serviceLoadBalancerIP }} + {{- end }} + {{- if and (eq $serviceType "LoadBalancer") $serviceLoadBalancerSourceRanges }} + loadBalancerSourceRanges: {{- include "common.tplvalues.render" (dict "value" $serviceLoadBalancerSourceRanges "context" $) | nindent 4 }} + {{- end }} + {{- if and (eq $serviceType "ClusterIP") $serviceClusterIP }} + clusterIP: {{ $serviceClusterIP }} + {{- end }} + ports: + - name: tcp-postgresql + port: {{ template "postgresql.port" . }} + targetPort: tcp-postgresql + {{- if $serviceNodePort }} + nodePort: {{ $serviceNodePort }} + {{- end }} + selector: + {{- include "common.labels.matchLabels" . | nindent 4 }} + role: primary diff --git a/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/charts/postgresql/values.schema.json b/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/charts/postgresql/values.schema.json new file mode 100644 index 0000000000..66a2a9dd06 --- /dev/null +++ b/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/charts/postgresql/values.schema.json @@ -0,0 +1,103 @@ +{ + "$schema": "http://json-schema.org/schema#", + "type": "object", + "properties": { + "postgresqlUsername": { + "type": "string", + "title": "Admin user", + "form": true + }, + "postgresqlPassword": { + "type": "string", + "title": "Password", + "form": true + }, + "persistence": { + "type": "object", + "properties": { + "size": { + "type": "string", + "title": "Persistent Volume Size", + "form": true, + "render": "slider", + "sliderMin": 1, + "sliderMax": 100, + "sliderUnit": "Gi" + } + } + }, + "resources": { + "type": "object", + "title": "Required Resources", + "description": "Configure resource requests", + "form": true, + "properties": { + "requests": { + "type": "object", + "properties": { + "memory": { + "type": "string", + "form": true, + "render": "slider", + "title": "Memory Request", + "sliderMin": 10, + "sliderMax": 2048, + "sliderUnit": "Mi" + }, + "cpu": { + "type": "string", + "form": true, + "render": "slider", + "title": "CPU Request", + "sliderMin": 10, + "sliderMax": 2000, + "sliderUnit": "m" + } + } + } + } + }, + "replication": { + "type": "object", + "form": true, + "title": "Replication Details", + "properties": { + "enabled": { + "type": "boolean", + "title": "Enable Replication", + "form": true + }, + "readReplicas": { + "type": "integer", + "title": "read Replicas", + "form": true, + "hidden": { + "value": false, + "path": "replication/enabled" + } + } + } + }, + "volumePermissions": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean", + "form": true, + "title": "Enable Init Containers", + "description": "Change the owner of the persist volume mountpoint to RunAsUser:fsGroup" + } + } + }, + "metrics": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean", + "title": "Configure metrics exporter", + "form": true + } + } + } + } +} diff --git a/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/charts/postgresql/values.yaml b/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/charts/postgresql/values.yaml new file mode 100644 index 0000000000..82ce092344 --- /dev/null +++ b/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/charts/postgresql/values.yaml @@ -0,0 +1,824 @@ +## Global Docker image parameters +## Please, note that this will override the image parameters, including dependencies, configured to use the global value +## Current available global Docker image parameters: imageRegistry and imagePullSecrets +## +global: + postgresql: {} +# imageRegistry: myRegistryName +# imagePullSecrets: +# - myRegistryKeySecretName +# storageClass: myStorageClass + +## Bitnami PostgreSQL image version +## ref: https://hub.docker.com/r/bitnami/postgresql/tags/ +## +image: + registry: docker.io + repository: bitnami/postgresql + tag: 11.11.0-debian-10-r71 + ## Specify a imagePullPolicy + ## Defaults to 'Always' if image tag is 'latest', else set to 'IfNotPresent' + ## ref: http://kubernetes.io/docs/user-guide/images/#pre-pulling-images + ## + pullPolicy: IfNotPresent + ## Optionally specify an array of imagePullSecrets. + ## Secrets must be manually created in the namespace. + ## ref: https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry/ + ## + # pullSecrets: + # - myRegistryKeySecretName + + ## Set to true if you would like to see extra information on logs + ## It turns BASH and/or NAMI debugging in the image + ## + debug: false + +## String to partially override common.names.fullname template (will maintain the release name) +## +# nameOverride: + +## String to fully override common.names.fullname template +## +# fullnameOverride: + +## +## Init containers parameters: +## volumePermissions: Change the owner of the persist volume mountpoint to RunAsUser:fsGroup +## +volumePermissions: + enabled: false + image: + registry: docker.io + repository: bitnami/bitnami-shell + tag: "10" + ## Specify a imagePullPolicy + ## Defaults to 'Always' if image tag is 'latest', else set to 'IfNotPresent' + ## ref: http://kubernetes.io/docs/user-guide/images/#pre-pulling-images + ## + pullPolicy: Always + ## Optionally specify an array of imagePullSecrets. + ## Secrets must be manually created in the namespace. + ## ref: https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry/ + ## + # pullSecrets: + # - myRegistryKeySecretName + ## Init container Security Context + ## Note: the chown of the data folder is done to securityContext.runAsUser + ## and not the below volumePermissions.securityContext.runAsUser + ## When runAsUser is set to special value "auto", init container will try to chwon the + ## data folder to autodetermined user&group, using commands: `id -u`:`id -G | cut -d" " -f2` + ## "auto" is especially useful for OpenShift which has scc with dynamic userids (and 0 is not allowed). + ## You may want to use this volumePermissions.securityContext.runAsUser="auto" in combination with + ## pod securityContext.enabled=false and shmVolume.chmod.enabled=false + ## + securityContext: + runAsUser: 0 + +## Use an alternate scheduler, e.g. "stork". +## ref: https://kubernetes.io/docs/tasks/administer-cluster/configure-multiple-schedulers/ +## +# schedulerName: + +## Pod Security Context +## ref: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/ +## +securityContext: + enabled: true + fsGroup: 1001 + +## Container Security Context +## ref: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/ +## +containerSecurityContext: + enabled: true + runAsUser: 1001 + +## Pod Service Account +## ref: https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/ +## +serviceAccount: + enabled: false + ## Name of an already existing service account. Setting this value disables the automatic service account creation. + # name: + +## Pod Security Policy +## ref: https://kubernetes.io/docs/concepts/policy/pod-security-policy/ +## +psp: + create: false + +## Creates role for ServiceAccount +## Required for PSP +## +rbac: + create: false + +replication: + enabled: false + user: repl_user + password: repl_password + readReplicas: 1 + ## Set synchronous commit mode: on, off, remote_apply, remote_write and local + ## ref: https://www.postgresql.org/docs/9.6/runtime-config-wal.html#GUC-WAL-LEVEL + synchronousCommit: 'off' + ## From the number of `readReplicas` defined above, set the number of those that will have synchronous replication + ## NOTE: It cannot be > readReplicas + numSynchronousReplicas: 0 + ## Replication Cluster application name. Useful for defining multiple replication policies + ## + applicationName: my_application + +## PostgreSQL admin password (used when `postgresqlUsername` is not `postgres`) +## ref: https://github.com/bitnami/bitnami-docker-postgresql/blob/master/README.md#creating-a-database-user-on-first-run (see note!) +# postgresqlPostgresPassword: + +## PostgreSQL user (has superuser privileges if username is `postgres`) +## ref: https://github.com/bitnami/bitnami-docker-postgresql/blob/master/README.md#setting-the-root-password-on-first-run +## +postgresqlUsername: postgres + +## PostgreSQL password +## ref: https://github.com/bitnami/bitnami-docker-postgresql/blob/master/README.md#setting-the-root-password-on-first-run +## +# postgresqlPassword: + +## PostgreSQL password using existing secret +## existingSecret: secret +## + +## Mount PostgreSQL secret as a file instead of passing environment variable +# usePasswordFile: false + +## Create a database +## ref: https://github.com/bitnami/bitnami-docker-postgresql/blob/master/README.md#creating-a-database-on-first-run +## +# postgresqlDatabase: + +## PostgreSQL data dir +## ref: https://github.com/bitnami/bitnami-docker-postgresql/blob/master/README.md +## +postgresqlDataDir: /bitnami/postgresql/data + +## An array to add extra environment variables +## For example: +## extraEnv: +## - name: FOO +## value: "bar" +## +# extraEnv: +extraEnv: [] + +## Name of a ConfigMap containing extra env vars +## +# extraEnvVarsCM: + +## Specify extra initdb args +## ref: https://github.com/bitnami/bitnami-docker-postgresql/blob/master/README.md +## +# postgresqlInitdbArgs: + +## Specify a custom location for the PostgreSQL transaction log +## ref: https://github.com/bitnami/bitnami-docker-postgresql/blob/master/README.md +## +# postgresqlInitdbWalDir: + +## PostgreSQL configuration +## Specify runtime configuration parameters as a dict, using camelCase, e.g. +## {"sharedBuffers": "500MB"} +## Alternatively, you can put your postgresql.conf under the files/ directory +## ref: https://www.postgresql.org/docs/current/static/runtime-config.html +## +# postgresqlConfiguration: + +## PostgreSQL extended configuration +## As above, but _appended_ to the main configuration +## Alternatively, you can put your *.conf under the files/conf.d/ directory +## https://github.com/bitnami/bitnami-docker-postgresql#allow-settings-to-be-loaded-from-files-other-than-the-default-postgresqlconf +## +# postgresqlExtendedConf: + +## Configure current cluster's primary server to be the standby server in other cluster. +## This will allow cross cluster replication and provide cross cluster high availability. +## You will need to configure pgHbaConfiguration if you want to enable this feature with local cluster replication enabled. +## +primaryAsStandBy: + enabled: false + # primaryHost: + # primaryPort: + +## PostgreSQL client authentication configuration +## Specify content for pg_hba.conf +## Default: do not create pg_hba.conf +## Alternatively, you can put your pg_hba.conf under the files/ directory +# pgHbaConfiguration: |- +# local all all trust +# host all all localhost trust +# host mydatabase mysuser 192.168.0.0/24 md5 + +## ConfigMap with PostgreSQL configuration +## NOTE: This will override postgresqlConfiguration and pgHbaConfiguration +# configurationConfigMap: + +## ConfigMap with PostgreSQL extended configuration +# extendedConfConfigMap: + +## initdb scripts +## Specify dictionary of scripts to be run at first boot +## Alternatively, you can put your scripts under the files/docker-entrypoint-initdb.d directory +## +# initdbScripts: +# my_init_script.sh: | +# #!/bin/sh +# echo "Do something." + +## ConfigMap with scripts to be run at first boot +## NOTE: This will override initdbScripts +# initdbScriptsConfigMap: + +## Secret with scripts to be run at first boot (in case it contains sensitive information) +## NOTE: This can work along initdbScripts or initdbScriptsConfigMap +# initdbScriptsSecret: + +## Specify the PostgreSQL username and password to execute the initdb scripts +# initdbUser: +# initdbPassword: + +## Audit settings +## https://github.com/bitnami/bitnami-docker-postgresql#auditing +## +audit: + ## Log client hostnames + ## + logHostname: false + ## Log connections to the server + ## + logConnections: false + ## Log disconnections + ## + logDisconnections: false + ## Operation to audit using pgAudit (default if not set) + ## + pgAuditLog: "" + ## Log catalog using pgAudit + ## + pgAuditLogCatalog: "off" + ## Log level for clients + ## + clientMinMessages: error + ## Template for log line prefix (default if not set) + ## + logLinePrefix: "" + ## Log timezone + ## + logTimezone: "" + +## Shared preload libraries +## +postgresqlSharedPreloadLibraries: "pgaudit" + +## Maximum total connections +## +postgresqlMaxConnections: + +## Maximum connections for the postgres user +## +postgresqlPostgresConnectionLimit: + +## Maximum connections for the created user +## +postgresqlDbUserConnectionLimit: + +## TCP keepalives interval +## +postgresqlTcpKeepalivesInterval: + +## TCP keepalives idle +## +postgresqlTcpKeepalivesIdle: + +## TCP keepalives count +## +postgresqlTcpKeepalivesCount: + +## Statement timeout +## +postgresqlStatementTimeout: + +## Remove pg_hba.conf lines with the following comma-separated patterns +## (cannot be used with custom pg_hba.conf) +## +postgresqlPghbaRemoveFilters: + +## Optional duration in seconds the pod needs to terminate gracefully. +## ref: https://kubernetes.io/docs/concepts/workloads/pods/pod/#termination-of-pods +## +# terminationGracePeriodSeconds: 30 + +## LDAP configuration +## +ldap: + enabled: false + url: '' + server: '' + port: '' + prefix: '' + suffix: '' + baseDN: '' + bindDN: '' + bind_password: + search_attr: '' + search_filter: '' + scheme: '' + tls: {} + +## PostgreSQL service configuration +## +service: + ## PosgresSQL service type + ## + type: ClusterIP + # clusterIP: None + port: 5432 + + ## Specify the nodePort value for the LoadBalancer and NodePort service types. + ## ref: https://kubernetes.io/docs/concepts/services-networking/service/#type-nodeport + ## + # nodePort: + + ## Provide any additional annotations which may be required. Evaluated as a template. + ## + annotations: {} + ## Set the LoadBalancer service type to internal only. + ## ref: https://kubernetes.io/docs/concepts/services-networking/service/#internal-load-balancer + ## + # loadBalancerIP: + ## Load Balancer sources. Evaluated as a template. + ## https://kubernetes.io/docs/tasks/access-application-cluster/configure-cloud-provider-firewall/#restrict-access-for-loadbalancer-service + ## + # loadBalancerSourceRanges: + # - 10.10.10.0/24 + +## Start primary and read(s) pod(s) without limitations on shm memory. +## By default docker and containerd (and possibly other container runtimes) +## limit `/dev/shm` to `64M` (see e.g. the +## [docker issue](https://github.com/docker-library/postgres/issues/416) and the +## [containerd issue](https://github.com/containerd/containerd/issues/3654), +## which could be not enough if PostgreSQL uses parallel workers heavily. +## +shmVolume: + ## Set `shmVolume.enabled` to `true` to mount a new tmpfs volume to remove + ## this limitation. + ## + enabled: true + ## Set to `true` to `chmod 777 /dev/shm` on a initContainer. + ## This option is ignored if `volumePermissions.enabled` is `false` + ## + chmod: + enabled: true + +## PostgreSQL data Persistent Volume Storage Class +## If defined, storageClassName: +## If set to "-", storageClassName: "", which disables dynamic provisioning +## If undefined (the default) or set to null, no storageClassName spec is +## set, choosing the default provisioner. (gp2 on AWS, standard on +## GKE, AWS & OpenStack) +## +persistence: + enabled: true + ## A manually managed Persistent Volume and Claim + ## If defined, PVC must be created manually before volume will be bound + ## The value is evaluated as a template, so, for example, the name can depend on .Release or .Chart + ## + # existingClaim: + + ## The path the volume will be mounted at, useful when using different + ## PostgreSQL images. + ## + mountPath: /bitnami/postgresql + + ## The subdirectory of the volume to mount to, useful in dev environments + ## and one PV for multiple services. + ## + subPath: '' + + # storageClass: "-" + accessModes: + - ReadWriteOnce + size: 8Gi + annotations: {} + ## selector can be used to match an existing PersistentVolume + ## selector: + ## matchLabels: + ## app: my-app + selector: {} + +## updateStrategy for PostgreSQL StatefulSet and its reads StatefulSets +## ref: https://kubernetes.io/docs/concepts/workloads/controllers/statefulset/#update-strategies +## +updateStrategy: + type: RollingUpdate + +## +## PostgreSQL Primary parameters +## +primary: + ## PostgreSQL Primary pod affinity preset + ## ref: https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#inter-pod-affinity-and-anti-affinity + ## Allowed values: soft, hard + ## + podAffinityPreset: "" + + ## PostgreSQL Primary pod anti-affinity preset + ## ref: https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#inter-pod-affinity-and-anti-affinity + ## Allowed values: soft, hard + ## + podAntiAffinityPreset: soft + + ## PostgreSQL Primary node affinity preset + ## ref: https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#node-affinity + ## Allowed values: soft, hard + ## + nodeAffinityPreset: + ## Node affinity type + ## Allowed values: soft, hard + type: "" + ## Node label key to match + ## E.g. + ## key: "kubernetes.io/e2e-az-name" + ## + key: "" + ## Node label values to match + ## E.g. + ## values: + ## - e2e-az1 + ## - e2e-az2 + ## + values: [] + + ## Affinity for PostgreSQL primary pods assignment + ## ref: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#affinity-and-anti-affinity + ## Note: primary.podAffinityPreset, primary.podAntiAffinityPreset, and primary.nodeAffinityPreset will be ignored when it's set + ## + affinity: {} + + ## Node labels for PostgreSQL primary pods assignment + ## ref: https://kubernetes.io/docs/user-guide/node-selection/ + ## + nodeSelector: {} + + ## Tolerations for PostgreSQL primary pods assignment + ## ref: https://kubernetes.io/docs/concepts/configuration/taint-and-toleration/ + ## + tolerations: [] + + labels: {} + annotations: {} + podLabels: {} + podAnnotations: {} + priorityClassName: '' + ## Extra init containers + ## Example + ## + ## extraInitContainers: + ## - name: do-something + ## image: busybox + ## command: ['do', 'something'] + ## + extraInitContainers: [] + + ## Additional PostgreSQL primary Volume mounts + ## + extraVolumeMounts: [] + ## Additional PostgreSQL primary Volumes + ## + extraVolumes: [] + ## Add sidecars to the pod + ## + ## For example: + ## sidecars: + ## - name: your-image-name + ## image: your-image + ## imagePullPolicy: Always + ## ports: + ## - name: portname + ## containerPort: 1234 + ## + sidecars: [] + + ## Override the service configuration for primary + ## + service: {} + # type: + # nodePort: + # clusterIP: + +## +## PostgreSQL read only replica parameters +## +readReplicas: + ## PostgreSQL read only pod affinity preset + ## ref: https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#inter-pod-affinity-and-anti-affinity + ## Allowed values: soft, hard + ## + podAffinityPreset: "" + + ## PostgreSQL read only pod anti-affinity preset + ## ref: https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#inter-pod-affinity-and-anti-affinity + ## Allowed values: soft, hard + ## + podAntiAffinityPreset: soft + + ## PostgreSQL read only node affinity preset + ## ref: https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#node-affinity + ## Allowed values: soft, hard + ## + nodeAffinityPreset: + ## Node affinity type + ## Allowed values: soft, hard + type: "" + ## Node label key to match + ## E.g. + ## key: "kubernetes.io/e2e-az-name" + ## + key: "" + ## Node label values to match + ## E.g. + ## values: + ## - e2e-az1 + ## - e2e-az2 + ## + values: [] + + ## Affinity for PostgreSQL read only pods assignment + ## ref: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#affinity-and-anti-affinity + ## Note: readReplicas.podAffinityPreset, readReplicas.podAntiAffinityPreset, and readReplicas.nodeAffinityPreset will be ignored when it's set + ## + affinity: {} + + ## Node labels for PostgreSQL read only pods assignment + ## ref: https://kubernetes.io/docs/user-guide/node-selection/ + ## + nodeSelector: {} + + ## Tolerations for PostgreSQL read only pods assignment + ## ref: https://kubernetes.io/docs/concepts/configuration/taint-and-toleration/ + ## + tolerations: [] + labels: {} + annotations: {} + podLabels: {} + podAnnotations: {} + priorityClassName: '' + + ## Extra init containers + ## Example + ## + ## extraInitContainers: + ## - name: do-something + ## image: busybox + ## command: ['do', 'something'] + ## + extraInitContainers: [] + + ## Additional PostgreSQL read replicas Volume mounts + ## + extraVolumeMounts: [] + + ## Additional PostgreSQL read replicas Volumes + ## + extraVolumes: [] + + ## Add sidecars to the pod + ## + ## For example: + ## sidecars: + ## - name: your-image-name + ## image: your-image + ## imagePullPolicy: Always + ## ports: + ## - name: portname + ## containerPort: 1234 + ## + sidecars: [] + + ## Override the service configuration for read + ## + service: {} + # type: + # nodePort: + # clusterIP: + + ## Whether to enable PostgreSQL read replicas data Persistent + ## + persistence: + enabled: true + + # Override the resource configuration for read replicas + resources: {} + # requests: + # memory: 256Mi + # cpu: 250m + +## Configure resource requests and limits +## ref: http://kubernetes.io/docs/user-guide/compute-resources/ +## +resources: + requests: + memory: 256Mi + cpu: 250m + +## Add annotations to all the deployed resources +## +commonAnnotations: {} + +networkPolicy: + ## Enable creation of NetworkPolicy resources. Only Ingress traffic is filtered for now. + ## + enabled: false + + ## The Policy model to apply. When set to false, only pods with the correct + ## client label will have network access to the port PostgreSQL is listening + ## on. When true, PostgreSQL will accept connections from any source + ## (with the correct destination port). + ## + allowExternal: true + + ## if explicitNamespacesSelector is missing or set to {}, only client Pods that are in the networkPolicy's namespace + ## and that match other criteria, the ones that have the good label, can reach the DB. + ## But sometimes, we want the DB to be accessible to clients from other namespaces, in this case, we can use this + ## LabelSelector to select these namespaces, note that the networkPolicy's namespace should also be explicitly added. + ## + ## Example: + ## explicitNamespacesSelector: + ## matchLabels: + ## role: frontend + ## matchExpressions: + ## - {key: role, operator: In, values: [frontend]} + ## + explicitNamespacesSelector: {} + +## Configure extra options for startup, liveness and readiness probes +## ref: https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/#configure-probes +## +startupProbe: + enabled: false + initialDelaySeconds: 30 + periodSeconds: 15 + timeoutSeconds: 5 + failureThreshold: 10 + successThreshold: 1 + +livenessProbe: + enabled: true + initialDelaySeconds: 30 + periodSeconds: 10 + timeoutSeconds: 5 + failureThreshold: 6 + successThreshold: 1 + +readinessProbe: + enabled: true + initialDelaySeconds: 5 + periodSeconds: 10 + timeoutSeconds: 5 + failureThreshold: 6 + successThreshold: 1 + +## Custom Startup probe +## +customStartupProbe: {} + +## Custom Liveness probe +## +customLivenessProbe: {} + +## Custom Rediness probe +## +customReadinessProbe: {} + +## +## TLS configuration +## +tls: + # Enable TLS traffic + enabled: false + # + # Whether to use the server's TLS cipher preferences rather than the client's. + preferServerCiphers: true + # + # Name of the Secret that contains the certificates + certificatesSecret: '' + # + # Certificate filename + certFilename: '' + # + # Certificate Key filename + certKeyFilename: '' + # + # CA Certificate filename + # If provided, PostgreSQL will authenticate TLS/SSL clients by requesting them a certificate + # ref: https://www.postgresql.org/docs/9.6/auth-methods.html + certCAFilename: + # + # File containing a Certificate Revocation List + crlFilename: + +## Configure metrics exporter +## +metrics: + enabled: false + # resources: {} + service: + type: ClusterIP + annotations: + prometheus.io/scrape: 'true' + prometheus.io/port: '9187' + loadBalancerIP: + serviceMonitor: + enabled: false + additionalLabels: {} + # namespace: monitoring + # interval: 30s + # scrapeTimeout: 10s + ## Custom PrometheusRule to be defined + ## The value is evaluated as a template, so, for example, the value can depend on .Release or .Chart + ## ref: https://github.com/coreos/prometheus-operator#customresourcedefinitions + ## + prometheusRule: + enabled: false + additionalLabels: {} + namespace: '' + ## These are just examples rules, please adapt them to your needs. + ## Make sure to constraint the rules to the current postgresql service. + ## rules: + ## - alert: HugeReplicationLag + ## expr: pg_replication_lag{service="{{ template "common.names.fullname" . }}-metrics"} / 3600 > 1 + ## for: 1m + ## labels: + ## severity: critical + ## annotations: + ## description: replication for {{ template "common.names.fullname" . }} PostgreSQL is lagging by {{ "{{ $value }}" }} hour(s). + ## summary: PostgreSQL replication is lagging by {{ "{{ $value }}" }} hour(s). + ## + rules: [] + + image: + registry: docker.io + repository: bitnami/postgres-exporter + tag: 0.9.0-debian-10-r43 + pullPolicy: IfNotPresent + ## Optionally specify an array of imagePullSecrets. + ## Secrets must be manually created in the namespace. + ## ref: https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry/ + ## + # pullSecrets: + # - myRegistryKeySecretName + ## Define additional custom metrics + ## ref: https://github.com/wrouesnel/postgres_exporter#adding-new-metrics-via-a-config-file + # customMetrics: + # pg_database: + # query: "SELECT d.datname AS name, CASE WHEN pg_catalog.has_database_privilege(d.datname, 'CONNECT') THEN pg_catalog.pg_database_size(d.datname) ELSE 0 END AS size_bytes FROM pg_catalog.pg_database d where datname not in ('template0', 'template1', 'postgres')" + # metrics: + # - name: + # usage: "LABEL" + # description: "Name of the database" + # - size_bytes: + # usage: "GAUGE" + # description: "Size of the database in bytes" + # + ## An array to add extra env vars to configure postgres-exporter + ## see: https://github.com/wrouesnel/postgres_exporter#environment-variables + ## For example: + # extraEnvVars: + # - name: PG_EXPORTER_DISABLE_DEFAULT_METRICS + # value: "true" + extraEnvVars: {} + + ## Pod Security Context + ## ref: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/ + ## + securityContext: + enabled: false + runAsUser: 1001 + ## ref: https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-probes/#configure-probes) + ## Configure extra options for liveness and readiness probes + ## + livenessProbe: + enabled: true + initialDelaySeconds: 5 + periodSeconds: 10 + timeoutSeconds: 5 + failureThreshold: 6 + successThreshold: 1 + + readinessProbe: + enabled: true + initialDelaySeconds: 5 + periodSeconds: 10 + timeoutSeconds: 5 + failureThreshold: 6 + successThreshold: 1 + +## Array with extra yaml to deploy with the chart. Evaluated as a template +## +extraDeploy: [] diff --git a/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/ci/access-tls-values.yaml b/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/ci/access-tls-values.yaml new file mode 100644 index 0000000000..1a8c4698d2 --- /dev/null +++ b/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/ci/access-tls-values.yaml @@ -0,0 +1,24 @@ +databaseUpgradeReady: true +# To Fix ct tool --reuse-values - PASSWORDS ERROR: you must provide your current passwords when upgrade the release +postgresql: + postgresqlPassword: password + persistence: + enabled: false +artifactory: + persistence: + enabled: false + resources: + requests: + memory: "4Gi" + cpu: "2" + limits: + memory: "6Gi" + cpu: "4" + javaOpts: + xms: "4g" + xmx: "4g" +access: + accessConfig: + security: + tls: true + resetAccessCAKeys: true diff --git a/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/ci/default-values.yaml b/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/ci/default-values.yaml new file mode 100644 index 0000000000..fc34693998 --- /dev/null +++ b/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/ci/default-values.yaml @@ -0,0 +1,21 @@ +# Leave this file empty to ensure that CI runs builds against the default configuration in values.yaml. +databaseUpgradeReady: true + +# To Fix ct tool --reuse-values - PASSWORDS ERROR: you must provide your current passwords when upgrade the release +postgresql: + postgresqlPassword: password + persistence: + enabled: false +artifactory: + persistence: + enabled: false + resources: + requests: + memory: "4Gi" + cpu: "2" + limits: + memory: "6Gi" + cpu: "4" + javaOpts: + xms: "4g" + xmx: "4g" diff --git a/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/ci/derby-test-values.yaml b/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/ci/derby-test-values.yaml new file mode 100644 index 0000000000..82ff485457 --- /dev/null +++ b/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/ci/derby-test-values.yaml @@ -0,0 +1,19 @@ +databaseUpgradeReady: true + +postgresql: + enabled: false +artifactory: + podSecurityContext: + fsGroupChangePolicy: "OnRootMismatch" + persistence: + enabled: false + resources: + requests: + memory: "4Gi" + cpu: "2" + limits: + memory: "6Gi" + cpu: "4" + javaOpts: + xms: "4g" + xmx: "4g" diff --git a/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/ci/global-values.yaml b/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/ci/global-values.yaml new file mode 100644 index 0000000000..33bbf04a20 --- /dev/null +++ b/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/ci/global-values.yaml @@ -0,0 +1,247 @@ +databaseUpgradeReady: true +# To Fix ct tool --reuse-values - PASSWORDS ERROR: you must provide your current passwords when upgrade the release +postgresql: + postgresqlPassword: password + persistence: + enabled: false +artifactory: + persistence: + enabled: false + resources: + requests: + memory: "4Gi" + cpu: "2" + limits: + memory: "6Gi" + cpu: "4" + javaOpts: + xms: "4g" + xmx: "4g" + customInitContainersBegin: | + - name: "custom-init-begin-local" + image: {{ include "artifactory.getImageInfoByValue" (list . "initContainers") }} + imagePullPolicy: {{ .Values.initContainers.image.pullPolicy }} + command: + - 'sh' + - '-c' + - echo "running in local" + volumeMounts: + - mountPath: "{{ .Values.artifactory.persistence.mountPath }}" + name: artifactory-volume + customInitContainers: | + - name: "custom-init-local" + image: {{ include "artifactory.getImageInfoByValue" (list . "initContainers") }} + imagePullPolicy: {{ .Values.initContainers.image.pullPolicy }} + command: + - 'sh' + - '-c' + - echo "running in local" + volumeMounts: + - mountPath: "{{ .Values.artifactory.persistence.mountPath }}" + name: artifactory-volume + # Add custom volumes + customVolumes: | + - name: custom-script-local + emptyDir: + sizeLimit: 100Mi + # Add custom volumesMounts + customVolumeMounts: | + - name: custom-script-local + mountPath: "/scriptslocal" + # Add custom sidecar containers + customSidecarContainers: | + - name: "sidecar-list-local" + image: {{ include "artifactory.getImageInfoByValue" (list . "initContainers") }} + imagePullPolicy: {{ .Values.initContainers.image.pullPolicy }} + securityContext: + allowPrivilegeEscalation: false + capabilities: + drop: + - NET_RAW + command: ["sh","-c","echo 'Sidecar is running in local' >> /scriptslocal/sidecarlocal.txt; cat /scriptslocal/sidecarlocal.txt; while true; do sleep 30; done"] + volumeMounts: + - mountPath: "/scriptslocal" + name: custom-script-local + resources: + requests: + memory: "32Mi" + cpu: "50m" + limits: + memory: "128Mi" + cpu: "100m" + +global: + masterKey: FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + joinKey: EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE + customInitContainersBegin: | + - name: "custom-init-begin-global" + image: {{ include "artifactory.getImageInfoByValue" (list . "initContainers") }} + imagePullPolicy: {{ .Values.initContainers.image.pullPolicy }} + command: + - 'sh' + - '-c' + - echo "running in global" + volumeMounts: + - mountPath: "{{ .Values.artifactory.persistence.mountPath }}" + name: artifactory-volume + customInitContainers: | + - name: "custom-init-global" + image: {{ include "artifactory.getImageInfoByValue" (list . "initContainers") }} + imagePullPolicy: {{ .Values.initContainers.image.pullPolicy }} + command: + - 'sh' + - '-c' + - echo "running in global" + volumeMounts: + - mountPath: "{{ .Values.artifactory.persistence.mountPath }}" + name: artifactory-volume + # Add custom volumes + customVolumes: | + - name: custom-script-global + emptyDir: + sizeLimit: 100Mi + # Add custom volumesMounts + customVolumeMounts: | + - name: custom-script-global + mountPath: "/scripts" + # Add custom sidecar containers + customSidecarContainers: | + - name: "sidecar-list-global" + image: {{ include "artifactory.getImageInfoByValue" (list . "initContainers") }} + imagePullPolicy: {{ .Values.initContainers.image.pullPolicy }} + securityContext: + allowPrivilegeEscalation: false + capabilities: + drop: + - NET_RAW + command: ["sh","-c","echo 'Sidecar is running in global' >> /scripts/sidecarglobal.txt; cat /scripts/sidecarglobal.txt; while true; do sleep 30; done"] + volumeMounts: + - mountPath: "/scripts" + name: custom-script-global + resources: + requests: + memory: "32Mi" + cpu: "50m" + limits: + memory: "128Mi" + cpu: "100m" + +nginx: + customInitContainers: | + - name: "custom-init-begin-nginx" + image: {{ include "artifactory.getImageInfoByValue" (list . "initContainers") }} + imagePullPolicy: {{ .Values.initContainers.image.pullPolicy }} + command: + - 'sh' + - '-c' + - echo "running in nginx" + volumeMounts: + - mountPath: "{{ .Values.artifactory.persistence.mountPath }}" + name: custom-script-local + customSidecarContainers: | + - name: "sidecar-list-nginx" + image: {{ include "artifactory.getImageInfoByValue" (list . "initContainers") }} + imagePullPolicy: {{ .Values.initContainers.image.pullPolicy }} + securityContext: + allowPrivilegeEscalation: false + capabilities: + drop: + - NET_RAW + command: ["sh","-c","echo 'Sidecar is running in local' >> /scriptslocal/sidecarlocal.txt; cat /scriptslocal/sidecarlocal.txt; while true; do sleep 30; done"] + volumeMounts: + - mountPath: "/scriptslocal" + name: custom-script-local + resources: + requests: + memory: "32Mi" + cpu: "50m" + limits: + memory: "128Mi" + cpu: "100m" + # Add custom volumes + customVolumes: | + - name: custom-script-local + emptyDir: + sizeLimit: 100Mi + + artifactoryConf: | + {{- if .Values.nginx.https.enabled }} + ssl_protocols TLSv1 TLSv1.1 TLSv1.2 TLSv1.3; + ssl_certificate {{ .Values.nginx.persistence.mountPath }}/ssl/tls.crt; + ssl_certificate_key {{ .Values.nginx.persistence.mountPath }}/ssl/tls.key; + ssl_session_cache shared:SSL:1m; + ssl_prefer_server_ciphers on; + {{- end }} + ## server configuration + server { + listen 8088; + {{- if .Values.nginx.internalPortHttps }} + listen {{ .Values.nginx.internalPortHttps }} ssl; + {{- else -}} + {{- if .Values.nginx.https.enabled }} + listen {{ .Values.nginx.https.internalPort }} ssl; + {{- end }} + {{- end }} + {{- if .Values.nginx.internalPortHttp }} + listen {{ .Values.nginx.internalPortHttp }}; + {{- else -}} + {{- if .Values.nginx.http.enabled }} + listen {{ .Values.nginx.http.internalPort }}; + {{- end }} + {{- end }} + server_name ~(?.+)\.{{ include "artifactory.fullname" . }} {{ include "artifactory.fullname" . }} + {{- range .Values.ingress.hosts -}} + {{- if contains "." . -}} + {{ "" | indent 0 }} ~(?.+)\.{{ . }} + {{- end -}} + {{- end -}}; + + if ($http_x_forwarded_proto = '') { + set $http_x_forwarded_proto $scheme; + } + ## Application specific logs + ## access_log /var/log/nginx/artifactory-access.log timing; + ## error_log /var/log/nginx/artifactory-error.log; + rewrite ^/artifactory/?$ / redirect; + if ( $repo != "" ) { + rewrite ^/(v1|v2)/(.*) /artifactory/api/docker/$repo/$1/$2 break; + } + chunked_transfer_encoding on; + client_max_body_size 0; + + location / { + proxy_read_timeout 900; + proxy_pass_header Server; + proxy_cookie_path ~*^/.* /; + proxy_pass {{ include "artifactory.scheme" . }}://{{ include "artifactory.fullname" . }}:{{ .Values.artifactory.externalPort }}/; + {{- if .Values.nginx.service.ssloffload}} + proxy_set_header X-JFrog-Override-Base-Url $http_x_forwarded_proto://$host; + {{- else }} + proxy_set_header X-JFrog-Override-Base-Url $http_x_forwarded_proto://$host:$server_port; + proxy_set_header X-Forwarded-Port $server_port; + {{- end }} + proxy_set_header X-Forwarded-Proto $http_x_forwarded_proto; + proxy_set_header Host $http_host; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always; + + location /artifactory/ { + if ( $request_uri ~ ^/artifactory/(.*)$ ) { + proxy_pass {{ include "artifactory.scheme" . }}://{{ include "artifactory.fullname" . }}:{{ .Values.artifactory.externalArtifactoryPort }}/artifactory/$1; + } + proxy_pass {{ include "artifactory.scheme" . }}://{{ include "artifactory.fullname" . }}:{{ .Values.artifactory.externalArtifactoryPort }}/artifactory/; + } + } + } + + ## A list of custom ports to expose on the NGINX pod. Follows the conventional Kubernetes yaml syntax for container ports. + customPorts: + - containerPort: 8088 + name: http2 + service: + ## A list of custom ports to expose through the Ingress controller service. Follows the conventional Kubernetes yaml syntax for service ports. + customPorts: + - port: 8088 + targetPort: 8088 + protocol: TCP + name: http2 diff --git a/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/ci/large-values.yaml b/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/ci/large-values.yaml new file mode 100644 index 0000000000..94a485d6f4 --- /dev/null +++ b/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/ci/large-values.yaml @@ -0,0 +1,82 @@ +# Leave this file empty to ensure that CI runs builds against the default configuration in values.yaml. +databaseUpgradeReady: true + +# To Fix ct tool --reuse-values - PASSWORDS ERROR: you must provide your current passwords when upgrade the release +postgresql: + postgresqlPassword: password + persistence: + enabled: false +artifactory: + persistence: + enabled: false + database: + maxOpenConnections: 150 + tomcat: + connector: + maxThreads: 300 + resources: + requests: + memory: "6Gi" + cpu: "2" + limits: + memory: "10Gi" + cpu: "8" + javaOpts: + xms: "8g" + xmx: "10g" +access: + database: + maxOpenConnections: 150 + tomcat: + connector: + maxThreads: 100 +router: + resources: + requests: + memory: "200Mi" + cpu: "200m" + limits: + memory: "1Gi" + cpu: "1" +frontend: + resources: + requests: + memory: "200Mi" + cpu: "200m" + limits: + memory: "1Gi" + cpu: "1" +metadata: + database: + maxOpenConnections: 150 + resources: + requests: + memory: "200Mi" + cpu: "200m" + limits: + memory: "1Gi" + cpu: "1" +event: + resources: + requests: + memory: "200Mi" + cpu: "200m" + limits: + memory: "1Gi" + cpu: "1" +jfconnect: + resources: + requests: + memory: "200Mi" + cpu: "200m" + limits: + memory: "1Gi" + cpu: "1" +observability: + resources: + requests: + memory: "200Mi" + cpu: "200m" + limits: + memory: "1Gi" + cpu: "1" diff --git a/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/ci/loggers-values.yaml b/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/ci/loggers-values.yaml new file mode 100644 index 0000000000..03c94be953 --- /dev/null +++ b/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/ci/loggers-values.yaml @@ -0,0 +1,43 @@ +# Leave this file empty to ensure that CI runs builds against the default configuration in values.yaml. +databaseUpgradeReady: true + +# To Fix ct tool --reuse-values - PASSWORDS ERROR: you must provide your current passwords when upgrade the release +postgresql: + postgresqlPassword: password + persistence: + enabled: false +artifactory: + persistence: + enabled: false + resources: + requests: + memory: "4Gi" + cpu: "2" + limits: + memory: "6Gi" + cpu: "4" + javaOpts: + xms: "4g" + xmx: "4g" + + loggers: + - access-audit.log + - access-request.log + - access-security-audit.log + - access-service.log + - artifactory-access.log + - artifactory-event.log + - artifactory-import-export.log + - artifactory-request.log + - artifactory-service.log + - frontend-request.log + - frontend-service.log + - metadata-request.log + - metadata-service.log + - router-request.log + - router-service.log + - router-traefik.log + + catalinaLoggers: + - tomcat-catalina.log + - tomcat-localhost.log diff --git a/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/ci/medium-values.yaml b/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/ci/medium-values.yaml new file mode 100644 index 0000000000..35044dc36d --- /dev/null +++ b/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/ci/medium-values.yaml @@ -0,0 +1,82 @@ +# Leave this file empty to ensure that CI runs builds against the default configuration in values.yaml. +databaseUpgradeReady: true + +# To Fix ct tool --reuse-values - PASSWORDS ERROR: you must provide your current passwords when upgrade the release +postgresql: + postgresqlPassword: password + persistence: + enabled: false +artifactory: + persistence: + enabled: false + database: + maxOpenConnections: 100 + tomcat: + connector: + maxThreads: 200 + resources: + requests: + memory: "4Gi" + cpu: "2" + limits: + memory: "8Gi" + cpu: "6" + javaOpts: + xms: "6g" + xmx: "8g" +access: + database: + maxOpenConnections: 100 + tomcat: + connector: + maxThreads: 50 +router: + resources: + requests: + memory: "200Mi" + cpu: "200m" + limits: + memory: "1Gi" + cpu: "1" +frontend: + resources: + requests: + memory: "200Mi" + cpu: "200m" + limits: + memory: "1Gi" + cpu: "1" +metadata: + database: + maxOpenConnections: 100 + resources: + requests: + memory: "200Mi" + cpu: "200m" + limits: + memory: "1Gi" + cpu: "1" +event: + resources: + requests: + memory: "200Mi" + cpu: "200m" + limits: + memory: "1Gi" + cpu: "1" +jfconnect: + resources: + requests: + memory: "200Mi" + cpu: "200m" + limits: + memory: "1Gi" + cpu: "1" +observability: + resources: + requests: + memory: "200Mi" + cpu: "200m" + limits: + memory: "1Gi" + cpu: "1" diff --git a/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/ci/migration-disabled-values.yaml b/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/ci/migration-disabled-values.yaml new file mode 100644 index 0000000000..092756fb65 --- /dev/null +++ b/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/ci/migration-disabled-values.yaml @@ -0,0 +1,21 @@ +databaseUpgradeReady: true +# To Fix ct tool --reuse-values - PASSWORDS ERROR: you must provide your current passwords when upgrade the release +postgresql: + postgresqlPassword: password + persistence: + enabled: false +artifactory: + migration: + enabled: false + persistence: + enabled: false + resources: + requests: + memory: "4Gi" + cpu: "2" + limits: + memory: "6Gi" + cpu: "4" + javaOpts: + xms: "4g" + xmx: "4g" diff --git a/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/ci/nginx-autoreload-values.yaml b/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/ci/nginx-autoreload-values.yaml new file mode 100644 index 0000000000..09616c5bf4 --- /dev/null +++ b/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/ci/nginx-autoreload-values.yaml @@ -0,0 +1,42 @@ +# Leave this file empty to ensure that CI runs builds against the default configuration in values.yaml. +databaseUpgradeReady: true + +# To Fix ct tool --reuse-values - PASSWORDS ERROR: you must provide your current passwords when upgrade the release +postgresql: + postgresqlPassword: password + persistence: + enabled: false +artifactory: + persistence: + enabled: false + resources: + requests: + memory: "4Gi" + cpu: "2" + limits: + memory: "6Gi" + cpu: "4" + javaOpts: + xms: "4g" + xmx: "4g" + +nginx: + customVolumes: | + - name: scripts + configMap: + name: {{ template "artifactory.fullname" . }}-nginx-scripts + defaultMode: 0550 + customVolumeMounts: | + - name: scripts + mountPath: /var/opt/jfrog/nginx/scripts/ + customCommand: + - /bin/sh + - -c + - | + # watch for configmap changes + /sbin/inotifyd /var/opt/jfrog/nginx/scripts/configreloader.sh {{ .Values.nginx.persistence.mountPath -}}/conf.d:n & + {{ if .Values.nginx.https.enabled -}} + # watch for tls secret changes + /sbin/inotifyd /var/opt/jfrog/nginx/scripts/configreloader.sh {{ .Values.nginx.persistence.mountPath -}}/ssl:n & + {{ end -}} + nginx -g 'daemon off;' diff --git a/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/ci/rtsplit-values-access-tls-values.yaml b/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/ci/rtsplit-values-access-tls-values.yaml new file mode 100644 index 0000000000..a38969a8ff --- /dev/null +++ b/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/ci/rtsplit-values-access-tls-values.yaml @@ -0,0 +1,96 @@ +databaseUpgradeReady: true +artifactory: + joinKey: EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE + masterKey: FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + persistence: + enabled: false + resources: + requests: + memory: "4Gi" + cpu: "2" + limits: + memory: "6Gi" + cpu: "4" + javaOpts: + xms: "4g" + xmx: "4g" + +access: + accessConfig: + security: + tls: true + resetAccessCAKeys: true + +postgresql: + postgresqlPassword: password + postgresqlExtendedConf: + maxConnections: 102 + persistence: + enabled: false + +rbac: + create: true +serviceAccount: + create: true + automountServiceAccountToken: true + +ingress: + enabled: true + className: "testclass" + hosts: + - demonow.xyz +nginx: + enabled: false +jfconnect: + enabled: true + resources: + requests: + memory: "100Mi" + cpu: "100m" + limits: + memory: "1Gi" + cpu: "1" +mc: + enabled: true +splitServicesToContainers: true + +router: + resources: + requests: + memory: "100Mi" + cpu: "100m" + limits: + memory: "1Gi" + cpu: "1" +frontend: + resources: + requests: + memory: "100Mi" + cpu: "100m" + limits: + memory: "1Gi" + cpu: "1" +metadata: + resources: + requests: + memory: "100Mi" + cpu: "100m" + limits: + memory: "1Gi" + cpu: "1" +event: + resources: + requests: + memory: "100Mi" + cpu: "100m" + limits: + memory: "1Gi" + cpu: "1" +observability: + resources: + requests: + memory: "100Mi" + cpu: "100m" + limits: + memory: "1Gi" + cpu: "1" diff --git a/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/ci/rtsplit-values.yaml b/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/ci/rtsplit-values.yaml new file mode 100644 index 0000000000..057ae9bf36 --- /dev/null +++ b/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/ci/rtsplit-values.yaml @@ -0,0 +1,151 @@ +databaseUpgradeReady: true +artifactory: + replicaCount: 1 + joinKey: EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE + masterKey: FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + persistence: + enabled: false + resources: + requests: + memory: "4Gi" + cpu: "2" + limits: + memory: "6Gi" + cpu: "4" + javaOpts: + xms: "4g" + xmx: "4g" + + # Add lifecycle hooks for artifactory container + lifecycle: + postStart: + exec: + command: ["/bin/sh", "-c", "echo Hello from the artifactory postStart handler >> /tmp/message"] + preStop: + exec: + command: ["/bin/sh", "-c", "echo Hello from the artifactory postStart handler >> /tmp/message"] + +postgresql: + postgresqlPassword: password + postgresqlExtendedConf: + maxConnections: 100 + persistence: + enabled: false + +rbac: + create: true +serviceAccount: + create: true + automountServiceAccountToken: true + +ingress: + enabled: true + className: "testclass" + hosts: + - demonow.xyz +nginx: + enabled: false +jfconnect: + enabled: true + resources: + requests: + memory: "100Mi" + cpu: "100m" + limits: + memory: "1Gi" + cpu: "1" + # Add lifecycle hooks for jfconect container + lifecycle: + postStart: + exec: + command: ["/bin/sh", "-c", "echo Hello from the jfconnect postStart handler >> /tmp/message"] + preStop: + exec: + command: ["/bin/sh", "-c", "echo Hello from the jfconnect postStart handler >> /tmp/message"] + + +mc: + enabled: true +splitServicesToContainers: true + +router: + resources: + requests: + memory: "100Mi" + cpu: "100m" + limits: + memory: "1Gi" + cpu: "1" + # Add lifecycle hooks for router container + lifecycle: + postStart: + exec: + command: ["/bin/sh", "-c", "echo Hello from the router postStart handler >> /tmp/message"] + preStop: + exec: + command: ["/bin/sh", "-c", "echo Hello from the router postStart handler >> /tmp/message"] + +frontend: + resources: + requests: + memory: "100Mi" + cpu: "100m" + limits: + memory: "1Gi" + cpu: "1" + # Add lifecycle hooks for frontend container + lifecycle: + postStart: + exec: + command: ["/bin/sh", "-c", "echo Hello from the frontend postStart handler >> /tmp/message"] + preStop: + exec: + command: ["/bin/sh", "-c", "echo Hello from the frontend postStart handler >> /tmp/message"] + +metadata: + resources: + requests: + memory: "100Mi" + cpu: "100m" + limits: + memory: "1Gi" + cpu: "1" + lifecycle: + postStart: + exec: + command: ["/bin/sh", "-c", "echo Hello from the metadata postStart handler >> /tmp/message"] + preStop: + exec: + command: ["/bin/sh", "-c", "echo Hello from the metadata postStart handler >> /tmp/message"] + +event: + resources: + requests: + memory: "100Mi" + cpu: "100m" + limits: + memory: "1Gi" + cpu: "1" + lifecycle: + postStart: + exec: + command: ["/bin/sh", "-c", "echo Hello from the event postStart handler >> /tmp/message"] + preStop: + exec: + command: ["/bin/sh", "-c", "echo Hello from the event postStart handler >> /tmp/message"] + +observability: + resources: + requests: + memory: "100Mi" + cpu: "100m" + limits: + memory: "1Gi" + cpu: "1" + lifecycle: + postStart: + exec: + command: ["/bin/sh", "-c", "echo Hello from the observability postStart handler >> /tmp/message"] + preStop: + exec: + command: ["/bin/sh", "-c", "echo Hello from the observability postStart handler >> /tmp/message"] diff --git a/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/ci/small-values.yaml b/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/ci/small-values.yaml new file mode 100644 index 0000000000..cd594e59ef --- /dev/null +++ b/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/ci/small-values.yaml @@ -0,0 +1,85 @@ +# Leave this file empty to ensure that CI runs builds against the default configuration in values.yaml. +databaseUpgradeReady: true + +# To Fix ct tool --reuse-values - PASSWORDS ERROR: you must provide your current passwords when upgrade the release +postgresql: + postgresqlPassword: password + persistence: + enabled: false +artifactory: + persistence: + enabled: false + database: + maxOpenConnections: 80 + tomcat: + connector: + maxThreads: 200 + resources: + requests: + memory: "4Gi" + cpu: "2" + limits: + memory: "6Gi" + cpu: "4" + javaOpts: + xms: "4g" + xmx: "6g" +access: + database: + maxOpenConnections: 80 + tomcat: + connector: + maxThreads: 50 +router: + resources: + requests: + memory: "100Mi" + cpu: "100m" + limits: + memory: "1Gi" + cpu: "1" +frontend: + resources: + requests: + memory: "100Mi" + cpu: "100m" + limits: + memory: "1Gi" + cpu: "1" +metadata: + database: + maxOpenConnections: 80 + resources: + requests: + memory: "100Mi" + cpu: "100m" + limits: + memory: "1Gi" + cpu: "1" +event: + resources: + requests: + memory: "100Mi" + cpu: "100m" + limits: + memory: "1Gi" + cpu: "1" +jfconnect: + resources: + requests: + memory: "100Mi" + cpu: "100m" + limits: + memory: "1Gi" + cpu: "1" +observability: + resources: + requests: + memory: "100Mi" + cpu: "100m" + limits: + memory: "1Gi" + cpu: "1" + +rtfs: + enabled: true diff --git a/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/ci/test-values.yaml b/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/ci/test-values.yaml new file mode 100644 index 0000000000..d2beb0eff6 --- /dev/null +++ b/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/ci/test-values.yaml @@ -0,0 +1,84 @@ +databaseUpgradeReady: true +artifactory: + replicaCount: 3 + joinKey: EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE + masterKey: FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + unifiedSecretInstallation: false + metrics: + enabled: true + persistence: + enabled: false + resources: + requests: + memory: "4Gi" + cpu: "2" + limits: + memory: "6Gi" + cpu: "4" + javaOpts: + xms: "4g" + xmx: "4g" + statefulset: + annotations: + artifactory: test + +postgresql: + postgresqlPassword: password + postgresqlExtendedConf: + maxConnections: 100 + persistence: + enabled: false + +rbac: + create: true +serviceAccount: + create: true + automountServiceAccountToken: true + +ingress: + enabled: true + className: "testclass" + hosts: + - demonow.xyz +nginx: + enabled: false + +jfconnect: + enabled: false + +autoscaling: + enabled: false + minReplicas: 1 + maxReplicas: 3 + targetCPUUtilizationPercentage: 70 + +## filebeat sidecar +filebeat: + enabled: true + filebeatYml: | + logging.level: info + path.data: {{ .Values.artifactory.persistence.mountPath }}/log/filebeat + name: artifactory-filebeat + queue.spool: + file: + permissions: 0760 + filebeat.inputs: + - type: log + enabled: true + close_eof: ${CLOSE:false} + paths: + - {{ .Values.artifactory.persistence.mountPath }}/log/*.log + fields: + service: "jfrt" + log_type: "artifactory" + output.file: + path: "/tmp/filebeat" + filename: filebeat + readinessProbe: + exec: + command: + - sh + - -c + - | + #!/usr/bin/env bash -e + curl --fail 127.0.0.1:5066 diff --git a/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/files/binarystore.xml b/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/files/binarystore.xml new file mode 100644 index 0000000000..8d71072e29 --- /dev/null +++ b/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/files/binarystore.xml @@ -0,0 +1,429 @@ +{{- if eq .Values.artifactory.persistence.type "nfs" -}} + + {{- if (.Values.artifactory.persistence.maxCacheSize) }} + + + + + + {{- else }} + + + + {{- end }} + + {{- if .Values.artifactory.persistence.maxCacheSize }} + + {{ .Values.artifactory.persistence.maxCacheSize | int64 }} + {{ .Values.artifactory.persistence.cacheProviderDir }} + {{- if .Values.artifactory.persistence.maxFileSizeLimit }} + {{.Values.artifactory.persistence.maxFileSizeLimit | int64}} + {{- end }} + {{- if .Values.artifactory.persistence.skipDuringUpload }} + {{.Values.artifactory.persistence.skipDuringUpload}} + {{- end }} + + {{- end }} + + + {{ .Values.artifactory.persistence.nfs.dataDir }}/filestore + + +{{- end }} +{{- if eq .Values.artifactory.persistence.type "file-system" -}} + + + + {{- if .Values.artifactory.persistence.fileSystem.cache.enabled }} + + {{- end }} + + {{- if .Values.artifactory.persistence.fileSystem.cache.enabled }} + + {{- end }} + + + {{- if .Values.artifactory.persistence.fileSystem.cache.enabled }} + + {{ .Values.artifactory.persistence.maxCacheSize | int64 }} + {{ .Values.artifactory.persistence.cacheProviderDir }} + {{- if .Values.artifactory.persistence.maxFileSizeLimit }} + {{.Values.artifactory.persistence.maxFileSizeLimit | int64}} + {{- end }} + {{- if .Values.artifactory.persistence.skipDuringUpload }} + {{.Values.artifactory.persistence.skipDuringUpload}} + {{- end }} + + {{- end }} + +{{- end }} +{{- if eq .Values.artifactory.persistence.type "cluster-file-system" -}} + + + + + + crossNetworkStrategy + crossNetworkStrategy + {{ .Values.artifactory.persistence.redundancy }} + {{ .Values.artifactory.persistence.lenientLimit }} + 2 + + + + + + + + + {{ .Values.artifactory.persistence.maxCacheSize | int64 }} + {{ .Values.artifactory.persistence.cacheProviderDir }} + {{- if .Values.artifactory.persistence.maxFileSizeLimit }} + {{.Values.artifactory.persistence.maxFileSizeLimit | int64}} + {{- end }} + {{- if .Values.artifactory.persistence.skipDuringUpload }} + {{.Values.artifactory.persistence.skipDuringUpload}} + {{- end }} + + + + + shard-fs-1 + local + + + + + 30 + tester-remote1 + 10000 + remote + + + +{{- end }} +{{- if or (eq .Values.artifactory.persistence.type "google-storage") (eq .Values.artifactory.persistence.type "google-storage-v2") (eq .Values.artifactory.persistence.type "cluster-google-storage-v2") (eq .Values.artifactory.persistence.type "google-storage-v2-direct") }} + + + {{- if or (eq .Values.artifactory.persistence.type "google-storage") (eq .Values.artifactory.persistence.type "google-storage-v2") }} + + + + + + + + + + {{- else if eq .Values.artifactory.persistence.type "cluster-google-storage-v2" }} + + + + crossNetworkStrategy + crossNetworkStrategy + {{ .Values.artifactory.persistence.redundancy }} + {{ .Values.artifactory.persistence.lenientLimit }} + 2 + + + + + + + + + + + {{- else if eq .Values.artifactory.persistence.type "google-storage-v2-direct" }} + + + + + + {{- end }} + + + + {{ .Values.artifactory.persistence.maxCacheSize | int64 }} + {{ .Values.artifactory.persistence.cacheProviderDir }} + {{- if .Values.artifactory.persistence.maxFileSizeLimit }} + {{.Values.artifactory.persistence.maxFileSizeLimit | int64}} + {{- end }} + {{- if .Values.artifactory.persistence.skipDuringUpload }} + {{.Values.artifactory.persistence.skipDuringUpload}} + {{- end }} + + + {{- if eq .Values.artifactory.persistence.type "cluster-google-storage-v2" }} + + local + + + + 30 + 10000 + remote + + {{- end }} + + + {{- if .Values.artifactory.persistence.googleStorage.useInstanceCredentials }} + true + {{- else }} + false + {{- end }} + {{ .Values.artifactory.persistence.googleStorage.enableSignedUrlRedirect }} + google-cloud-storage + {{ .Values.artifactory.persistence.googleStorage.endpoint }} + {{ .Values.artifactory.persistence.googleStorage.httpsOnly }} + {{ .Values.artifactory.persistence.googleStorage.bucketName }} + {{ .Values.artifactory.persistence.googleStorage.path }} + {{ .Values.artifactory.persistence.googleStorage.bucketExists }} + {{- if .Values.artifactory.persistence.googleStorage.signedUrlExpirySeconds }} + {{ .Values.artifactory.persistence.googleStorage.signedUrlExpirySeconds | int64 }} + {{- end }} + + +{{- end }} +{{- if or (eq .Values.artifactory.persistence.type "aws-s3-v3") (eq .Values.artifactory.persistence.type "s3-storage-v3-direct") (eq .Values.artifactory.persistence.type "cluster-s3-storage-v3") (eq .Values.artifactory.persistence.type "s3-storage-v3-archive") }} + + + {{- if eq .Values.artifactory.persistence.type "aws-s3-v3" }} + + + + + + + + + + {{- else if eq .Values.artifactory.persistence.type "s3-storage-v3-direct" }} + + + + + + {{- else if eq .Values.artifactory.persistence.type "cluster-s3-storage-v3" }} + + + + + + + + + + + + + {{- else if eq .Values.artifactory.persistence.type "s3-storage-v3-archive" }} + + + + + + + {{- end }} + + {{- if or (eq .Values.artifactory.persistence.type "aws-s3-v3") (eq .Values.artifactory.persistence.type "s3-storage-v3-direct") (eq .Values.artifactory.persistence.type "cluster-s3-storage-v3") }} + + + {{ .Values.artifactory.persistence.maxCacheSize | int64}} + {{ .Values.artifactory.persistence.cacheProviderDir }} + {{- if .Values.artifactory.persistence.maxFileSizeLimit }} + {{.Values.artifactory.persistence.maxFileSizeLimit | int64}} + {{- end }} + {{- if .Values.artifactory.persistence.skipDuringUpload }} + {{.Values.artifactory.persistence.skipDuringUpload}} + {{- end }} + + {{- end }} + + {{- if eq .Values.artifactory.persistence.type "cluster-s3-storage-v3" }} + + crossNetworkStrategy + crossNetworkStrategy + {{ .Values.artifactory.persistence.redundancy }} + {{ .Values.artifactory.persistence.lenientLimit }} + + + + + remote + + + + local + + {{- end }} + + {{- with .Values.artifactory.persistence.awsS3V3 }} + + {{ .testConnection }} + {{- if .identity }} + {{ .identity }} + {{- end }} + {{- if .credential }} + {{ .credential }} + {{- end }} + {{ .region }} + {{ .bucketName }} + {{ .path }} + {{ .endpoint }} + {{- with .port }} + {{ . }} + {{- end }} + {{- with .useHttp }} + {{ . }} + {{- end }} + {{- with .maxConnections }} + {{ . }} + {{- end }} + {{- with .connectionTimeout }} + {{ . }} + {{- end }} + {{- with .socketTimeout }} + {{ . }} + {{- end }} + {{- with .kmsServerSideEncryptionKeyId }} + {{ . }} + {{- end }} + {{- with .kmsKeyRegion }} + {{ . }} + {{- end }} + {{- with .kmsCryptoMode }} + {{ . }} + {{- end }} + {{- if .useInstanceCredentials }} + true + {{- else }} + false + {{- end }} + {{ .usePresigning }} + {{ .signatureExpirySeconds }} + {{ .signedUrlExpirySeconds }} + {{- with .cloudFrontDomainName }} + {{ . }} + {{- end }} + {{- with .cloudFrontKeyPairId }} + {{ . }} + {{- end }} + {{- with .cloudFrontPrivateKey }} + {{ . }} + {{- end }} + {{- with .enableSignedUrlRedirect }} + {{ . }} + {{- end }} + {{- with .enablePathStyleAccess }} + {{ . }} + {{- end }} + {{- with .multiPartLimit }} + {{ . | int64 }} + {{- end }} + {{- with .multipartElementSize }} + {{ . | int64 }} + {{- end }} + + {{- end }} + +{{- end }} + +{{- if or (eq .Values.artifactory.persistence.type "azure-blob") (eq .Values.artifactory.persistence.type "azure-blob-storage-direct") (eq .Values.artifactory.persistence.type "cluster-azure-blob-storage") }} + + + {{- if or (eq .Values.artifactory.persistence.type "azure-blob") }} + + + + + + + + + + {{- else if eq .Values.artifactory.persistence.type "azure-blob-storage-direct" }} + + + + + + {{- else if eq .Values.artifactory.persistence.type "cluster-azure-blob-storage" }} + + + + + + + + + + + + + {{- end }} + + + + {{ .Values.artifactory.persistence.maxCacheSize | int64 }} + {{ .Values.artifactory.persistence.cacheProviderDir }} + {{- if .Values.artifactory.persistence.maxFileSizeLimit }} + {{.Values.artifactory.persistence.maxFileSizeLimit | int64}} + {{- end }} + {{- if .Values.artifactory.persistence.skipDuringUpload }} + {{.Values.artifactory.persistence.skipDuringUpload}} + {{- end }} + + + {{- if eq .Values.artifactory.persistence.type "cluster-azure-blob-storage" }} + + + crossNetworkStrategy + crossNetworkStrategy + {{ .Values.artifactory.persistence.redundancy }} + {{ .Values.artifactory.persistence.lenientLimit }} + + + + remote + + + local + + {{- end }} + + + {{ .Values.artifactory.persistence.azureBlob.accountName }} + {{ .Values.artifactory.persistence.azureBlob.accountKey }} + {{ .Values.artifactory.persistence.azureBlob.endpoint }} + {{ .Values.artifactory.persistence.azureBlob.containerName }} + {{ .Values.artifactory.persistence.azureBlob.multiPartLimit | int64 }} + {{ .Values.artifactory.persistence.azureBlob.multipartElementSize | int64 }} + {{ .Values.artifactory.persistence.azureBlob.testConnection }} + + +{{- end }} +{{- if eq .Values.artifactory.persistence.type "azure-blob-storage-v2-direct" -}} + + + + {{ .Values.artifactory.persistence.maxCacheSize | int64 }} + {{ .Values.artifactory.persistence.cacheProviderDir }} + {{- if .Values.artifactory.persistence.maxFileSizeLimit }} + {{.Values.artifactory.persistence.maxFileSizeLimit | int64}} + {{- end }} + {{- if .Values.artifactory.persistence.skipDuringUpload }} + {{.Values.artifactory.persistence.skipDuringUpload}} + {{- end }} + + + {{ .Values.artifactory.persistence.azureBlob.accountName }} + {{ .Values.artifactory.persistence.azureBlob.accountKey }} + {{ .Values.artifactory.persistence.azureBlob.endpoint }} + {{ .Values.artifactory.persistence.azureBlob.containerName }} + {{ .Values.artifactory.persistence.azureBlob.multiPartLimit | int64 }} + {{ .Values.artifactory.persistence.azureBlob.multipartElementSize | int64 }} + {{ .Values.artifactory.persistence.azureBlob.testConnection }} + + +{{- end }} \ No newline at end of file diff --git a/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/files/installer-info.json b/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/files/installer-info.json new file mode 100644 index 0000000000..79f42ed16d --- /dev/null +++ b/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/files/installer-info.json @@ -0,0 +1,32 @@ +{ + "productId": "Helm_artifactory/{{ .Chart.Version }}", + "features": [ + { + "featureId": "Platform/{{ printf "%s-%s" "kubernetes" .Capabilities.KubeVersion.Version }}" + }, + { + "featureId": "Database/{{ .Values.database.type }}" + }, + { + "featureId": "PostgreSQL_Enabled/{{ .Values.postgresql.enabled }}" + }, + { + "featureId": "Nginx_Enabled/{{ .Values.nginx.enabled }}" + }, + { + "featureId": "ArtifactoryPersistence_Type/{{ .Values.artifactory.persistence.type }}" + }, + { + "featureId": "SplitServicesToContainers_Enabled/{{ .Values.splitServicesToContainers }}" + }, + { + "featureId": "UnifiedSecretInstallation_Enabled/{{ .Values.artifactory.unifiedSecretInstallation }}" + }, + { + "featureId": "Filebeat_Enabled/{{ .Values.filebeat.enabled }}" + }, + { + "featureId": "ReplicaCount/{{ .Values.artifactory.replicaCount }}" + } + ] +} \ No newline at end of file diff --git a/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/files/migrate.sh b/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/files/migrate.sh new file mode 100644 index 0000000000..ba44160f47 --- /dev/null +++ b/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/files/migrate.sh @@ -0,0 +1,4311 @@ +#!/bin/bash + +# Flags +FLAG_Y="y" +FLAG_N="n" +FLAGS_Y_N="$FLAG_Y $FLAG_N" +FLAG_NOT_APPLICABLE="_NA_" + +CURRENT_VERSION=$1 + +WRAPPER_SCRIPT_TYPE_RPMDEB="RPMDEB" +WRAPPER_SCRIPT_TYPE_DOCKER_COMPOSE="DOCKERCOMPOSE" + +SENSITIVE_KEY_VALUE="__sensitive_key_hidden___" + +# Shared system keys +SYS_KEY_SHARED_JFROGURL="shared.jfrogUrl" +SYS_KEY_SHARED_SECURITY_JOINKEY="shared.security.joinKey" +SYS_KEY_SHARED_SECURITY_MASTERKEY="shared.security.masterKey" + +SYS_KEY_SHARED_NODE_ID="shared.node.id" +SYS_KEY_SHARED_JAVAHOME="shared.javaHome" + +SYS_KEY_SHARED_DATABASE_TYPE="shared.database.type" +SYS_KEY_SHARED_DATABASE_TYPE_VALUE_POSTGRES="postgresql" +SYS_KEY_SHARED_DATABASE_DRIVER="shared.database.driver" +SYS_KEY_SHARED_DATABASE_URL="shared.database.url" +SYS_KEY_SHARED_DATABASE_USERNAME="shared.database.username" +SYS_KEY_SHARED_DATABASE_PASSWORD="shared.database.password" + +SYS_KEY_SHARED_ELASTICSEARCH_URL="shared.elasticsearch.url" +SYS_KEY_SHARED_ELASTICSEARCH_USERNAME="shared.elasticsearch.username" +SYS_KEY_SHARED_ELASTICSEARCH_PASSWORD="shared.elasticsearch.password" +SYS_KEY_SHARED_ELASTICSEARCH_CLUSTERSETUP="shared.elasticsearch.clusterSetup" +SYS_KEY_SHARED_ELASTICSEARCH_UNICASTFILE="shared.elasticsearch.unicastFile" +SYS_KEY_SHARED_ELASTICSEARCH_CLUSTERSETUP_VALUE="YES" + +# Define this in product specific script. Should contain the path to unitcast file +# File used by insight server to write cluster active nodes info. This will be read by elasticsearch +#SYS_KEY_SHARED_ELASTICSEARCH_UNICASTFILE_VALUE="" + +SYS_KEY_RABBITMQ_ACTIVE_NODE_NAME="shared.rabbitMq.active.node.name" +SYS_KEY_RABBITMQ_ACTIVE_NODE_IP="shared.rabbitMq.active.node.ip" + +# Filenames +FILE_NAME_SYSTEM_YAML="system.yaml" +FILE_NAME_JOIN_KEY="join.key" +FILE_NAME_MASTER_KEY="master.key" +FILE_NAME_INSTALLER_YAML="installer.yaml" + +# Global constants used in business logic +NODE_TYPE_STANDALONE="standalone" +NODE_TYPE_CLUSTER_NODE="node" +NODE_TYPE_DATABASE="database" + +# External(isable) databases +DATABASE_POSTGRES="POSTGRES" +DATABASE_ELASTICSEARCH="ELASTICSEARCH" +DATABASE_RABBITMQ="RABBITMQ" + +POSTGRES_LABEL="PostgreSQL" +ELASTICSEARCH_LABEL="Elasticsearch" +RABBITMQ_LABEL="Rabbitmq" + +ARTIFACTORY_LABEL="Artifactory" +JFMC_LABEL="Mission Control" +DISTRIBUTION_LABEL="Distribution" +XRAY_LABEL="Xray" + +POSTGRES_CONTAINER="postgres" +ELASTICSEARCH_CONTAINER="elasticsearch" +RABBITMQ_CONTAINER="rabbitmq" +REDIS_CONTAINER="redis" + +#Adding a small timeout before a read ensures it is positioned correctly in the screen +read_timeout=0.5 + +# Options related to data directory location +PROMPT_DATA_DIR_LOCATION="Installation Directory" +KEY_DATA_DIR_LOCATION="installer.data_dir" + +SYS_KEY_SHARED_NODE_HAENABLED="shared.node.haEnabled" +PROMPT_ADD_TO_CLUSTER="Are you adding an additional node to an existing product cluster?" +KEY_ADD_TO_CLUSTER="installer.ha" +VALID_VALUES_ADD_TO_CLUSTER="$FLAGS_Y_N" + +MESSAGE_POSTGRES_INSTALL="The installer can install a $POSTGRES_LABEL database, or you can connect to an existing compatible $POSTGRES_LABEL database\n(compatible databases: https://www.jfrog.com/confluence/display/JFROG/System+Requirements#SystemRequirements-RequirementsMatrix)" +PROMPT_POSTGRES_INSTALL="Do you want to install $POSTGRES_LABEL?" +KEY_POSTGRES_INSTALL="installer.install_postgresql" +VALID_VALUES_POSTGRES_INSTALL="$FLAGS_Y_N" + +# Postgres connection details +RPM_DEB_POSTGRES_HOME_DEFAULT="/var/opt/jfrog/postgres" +RPM_DEB_MESSAGE_STANDALONE_POSTGRES_DATA="$POSTGRES_LABEL home will have data and its configuration" +RPM_DEB_PROMPT_STANDALONE_POSTGRES_DATA="Type desired $POSTGRES_LABEL home location" +RPM_DEB_KEY_STANDALONE_POSTGRES_DATA="installer.postgresql.home" + +MESSAGE_DATABASE_URL="Provide the database connection details" +PROMPT_DATABASE_URL(){ + local databaseURlExample= + case "$PRODUCT_NAME" in + $ARTIFACTORY_LABEL) + databaseURlExample="jdbc:postgresql://:/artifactory" + ;; + $JFMC_LABEL) + databaseURlExample="postgresql://:/mission_control?sslmode=disable" + ;; + $DISTRIBUTION_LABEL) + databaseURlExample="jdbc:postgresql://:/distribution?sslmode=disable" + ;; + $XRAY_LABEL) + databaseURlExample="postgres://:/xraydb?sslmode=disable" + ;; + esac + if [ -z "$databaseURlExample" ]; then + echo -n "$POSTGRES_LABEL URL" # For consistency with username and password + return + fi + echo -n "$POSTGRES_LABEL url. Example: [$databaseURlExample]" +} +REGEX_DATABASE_URL(){ + local databaseURlExample= + case "$PRODUCT_NAME" in + $ARTIFACTORY_LABEL) + databaseURlExample="jdbc:postgresql://.*/artifactory.*" + ;; + $JFMC_LABEL) + databaseURlExample="postgresql://.*/mission_control.*" + ;; + $DISTRIBUTION_LABEL) + databaseURlExample="jdbc:postgresql://.*/distribution.*" + ;; + $XRAY_LABEL) + databaseURlExample="postgres://.*/xraydb.*" + ;; + esac + echo -n "^$databaseURlExample\$" +} +ERROR_MESSAGE_DATABASE_URL="Invalid $POSTGRES_LABEL URL" +KEY_DATABASE_URL="$SYS_KEY_SHARED_DATABASE_URL" +#NOTE: It is important to display the label. Since the message may be hidden if URL is known +PROMPT_DATABASE_USERNAME="$POSTGRES_LABEL username" +KEY_DATABASE_USERNAME="$SYS_KEY_SHARED_DATABASE_USERNAME" +#NOTE: It is important to display the label. Since the message may be hidden if URL is known +PROMPT_DATABASE_PASSWORD="$POSTGRES_LABEL password" +KEY_DATABASE_PASSWORD="$SYS_KEY_SHARED_DATABASE_PASSWORD" +IS_SENSITIVE_DATABASE_PASSWORD="$FLAG_Y" + +MESSAGE_STANDALONE_ELASTICSEARCH_INSTALL="The installer can install a $ELASTICSEARCH_LABEL database or you can connect to an existing compatible $ELASTICSEARCH_LABEL database" +PROMPT_STANDALONE_ELASTICSEARCH_INSTALL="Do you want to install $ELASTICSEARCH_LABEL?" +KEY_STANDALONE_ELASTICSEARCH_INSTALL="installer.install_elasticsearch" +VALID_VALUES_STANDALONE_ELASTICSEARCH_INSTALL="$FLAGS_Y_N" + +# Elasticsearch connection details +MESSAGE_ELASTICSEARCH_DETAILS="Provide the $ELASTICSEARCH_LABEL connection details" +PROMPT_ELASTICSEARCH_URL="$ELASTICSEARCH_LABEL URL" +KEY_ELASTICSEARCH_URL="$SYS_KEY_SHARED_ELASTICSEARCH_URL" + +PROMPT_ELASTICSEARCH_USERNAME="$ELASTICSEARCH_LABEL username" +KEY_ELASTICSEARCH_USERNAME="$SYS_KEY_SHARED_ELASTICSEARCH_USERNAME" + +PROMPT_ELASTICSEARCH_PASSWORD="$ELASTICSEARCH_LABEL password" +KEY_ELASTICSEARCH_PASSWORD="$SYS_KEY_SHARED_ELASTICSEARCH_PASSWORD" +IS_SENSITIVE_ELASTICSEARCH_PASSWORD="$FLAG_Y" + +# Cluster related questions +MESSAGE_CLUSTER_MASTER_KEY="Provide the cluster's master key. It can be found in the data directory of the first node under /etc/security/master.key" +PROMPT_CLUSTER_MASTER_KEY="Master Key" +KEY_CLUSTER_MASTER_KEY="$SYS_KEY_SHARED_SECURITY_MASTERKEY" +IS_SENSITIVE_CLUSTER_MASTER_KEY="$FLAG_Y" + +MESSAGE_JOIN_KEY="The Join key is the secret key used to establish trust between services in the JFrog Platform.\n(You can copy the Join Key from Admin > User Management > Settings)" +PROMPT_JOIN_KEY="Join Key" +KEY_JOIN_KEY="$SYS_KEY_SHARED_SECURITY_JOINKEY" +IS_SENSITIVE_JOIN_KEY="$FLAG_Y" +REGEX_JOIN_KEY="^[a-zA-Z0-9]{16,}\$" +ERROR_MESSAGE_JOIN_KEY="Invalid Join Key" + +# Rabbitmq related cluster information +MESSAGE_RABBITMQ_ACTIVE_NODE_NAME="Provide an active ${RABBITMQ_LABEL} node name. Run the command [ hostname -s ] on any of the existing nodes in the product cluster to get this" +PROMPT_RABBITMQ_ACTIVE_NODE_NAME="${RABBITMQ_LABEL} active node name" +KEY_RABBITMQ_ACTIVE_NODE_NAME="$SYS_KEY_RABBITMQ_ACTIVE_NODE_NAME" + +# Rabbitmq related cluster information (necessary only for docker-compose) +PROMPT_RABBITMQ_ACTIVE_NODE_IP="${RABBITMQ_LABEL} active node ip" +KEY_RABBITMQ_ACTIVE_NODE_IP="$SYS_KEY_RABBITMQ_ACTIVE_NODE_IP" + +MESSAGE_JFROGURL(){ + echo -e "The JFrog URL allows ${PRODUCT_NAME} to connect to a JFrog Platform Instance.\n(You can copy the JFrog URL from Administration > User Management > Settings > Connection details)" +} +PROMPT_JFROGURL="JFrog URL" +KEY_JFROGURL="$SYS_KEY_SHARED_JFROGURL" +REGEX_JFROGURL="^https?://.*:{0,}[0-9]{0,4}\$" +ERROR_MESSAGE_JFROGURL="Invalid JFrog URL" + + +# Set this to FLAG_Y on upgrade +IS_UPGRADE="${FLAG_N}" + +# This belongs in JFMC but is the ONLY one that needs it so keeping it here for now. Can be made into a method and overridden if necessary +MESSAGE_MULTIPLE_PG_SCHEME="Please setup $POSTGRES_LABEL with schema as described in https://www.jfrog.com/confluence/display/JFROG/Installing+Mission+Control" + +_getMethodOutputOrVariableValue() { + unset EFFECTIVE_MESSAGE + local keyToSearch=$1 + local effectiveMessage= + local result="0" + # logSilly "Searching for method: [$keyToSearch]" + LC_ALL=C type "$keyToSearch" > /dev/null 2>&1 || result="$?" + if [[ "$result" == "0" ]]; then + # logSilly "Found method for [$keyToSearch]" + EFFECTIVE_MESSAGE="$($keyToSearch)" + return + fi + eval EFFECTIVE_MESSAGE=\${$keyToSearch} + if [ ! -z "$EFFECTIVE_MESSAGE" ]; then + return + fi + # logSilly "Didn't find method or variable for [$keyToSearch]" +} + + +# REF https://misc.flogisoft.com/bash/tip_colors_and_formatting +cClear="\e[0m" +cBlue="\e[38;5;69m" +cRedDull="\e[1;31m" +cYellow="\e[1;33m" +cRedBright="\e[38;5;197m" +cBold="\e[1m" + + +_loggerGetModeRaw() { + local MODE="$1" + case $MODE in + INFO) + printf "" + ;; + DEBUG) + printf "%s" "[${MODE}] " + ;; + WARN) + printf "${cRedDull}%s%s${cClear}" "[" "${MODE}" "] " + ;; + ERROR) + printf "${cRedBright}%s%s${cClear}" "[" "${MODE}" "] " + ;; + esac +} + + +_loggerGetMode() { + local MODE="$1" + case $MODE in + INFO) + printf "${cBlue}%s%-5s%s${cClear}" "[" "${MODE}" "]" + ;; + DEBUG) + printf "%-7s" "[${MODE}]" + ;; + WARN) + printf "${cRedDull}%s%-5s%s${cClear}" "[" "${MODE}" "]" + ;; + ERROR) + printf "${cRedBright}%s%-5s%s${cClear}" "[" "${MODE}" "]" + ;; + esac +} + +# Capitalises the first letter of the message +_loggerGetMessage() { + local originalMessage="$*" + local firstChar=$(echo "${originalMessage:0:1}" | awk '{ print toupper($0) }') + local resetOfMessage="${originalMessage:1}" + echo "$firstChar$resetOfMessage" +} + +# The spec also says content should be left-trimmed but this is not necessary in our case. We don't reach the limit. +_loggerGetStackTrace() { + printf "%s%-30s%s" "[" "$1:$2" "]" +} + +_loggerGetThread() { + printf "%s" "[main]" +} + +_loggerGetServiceType() { + printf "%s%-5s%s" "[" "shell" "]" +} + +#Trace ID is not applicable to scripts +_loggerGetTraceID() { + printf "%s" "[]" +} + +logRaw() { + echo "" + printf "$1" + echo "" +} + +logBold(){ + echo "" + printf "${cBold}$1${cClear}" + echo "" +} + +# The date binary works differently based on whether it is GNU/BSD +is_date_supported=0 +date --version > /dev/null 2>&1 || is_date_supported=1 +IS_GNU=$(echo $is_date_supported) + +_loggerGetTimestamp() { + if [ "${IS_GNU}" == "0" ]; then + echo -n $(date -u +%FT%T.%3NZ) + else + echo -n $(date -u +%FT%T.000Z) + fi +} + +# https://www.shellscript.sh/tips/spinner/ +_spin() +{ + spinner="/|\\-/|\\-" + while : + do + for i in `seq 0 7` + do + echo -n "${spinner:$i:1}" + echo -en "\010" + sleep 1 + done + done +} + +showSpinner() { + # Start the Spinner: + _spin & + # Make a note of its Process ID (PID): + SPIN_PID=$! + # Kill the spinner on any signal, including our own exit. + trap "kill -9 $SPIN_PID" `seq 0 15` &> /dev/null || return 0 +} + +stopSpinner() { + local occurrences=$(ps -ef | grep -wc "${SPIN_PID}") + let "occurrences+=0" + # validate that it is present (2 since this search itself will show up in the results) + if [ $occurrences -gt 1 ]; then + kill -9 $SPIN_PID &>/dev/null || return 0 + wait $SPIN_ID &>/dev/null + fi +} + +_getEffectiveMessage(){ + local MESSAGE="$1" + local MODE=${2-"INFO"} + + if [ -z "$CONTEXT" ]; then + CONTEXT=$(caller) + fi + + _EFFECTIVE_MESSAGE= + if [ -z "$LOG_BEHAVIOR_ADD_META" ]; then + _EFFECTIVE_MESSAGE="$(_loggerGetModeRaw $MODE)$(_loggerGetMessage $MESSAGE)" + else + local SERVICE_TYPE="script" + local TRACE_ID="" + local THREAD="main" + + local CONTEXT_LINE=$(echo "$CONTEXT" | awk '{print $1}') + local CONTEXT_FILE=$(echo "$CONTEXT" | awk -F"/" '{print $NF}') + + _EFFECTIVE_MESSAGE="$(_loggerGetTimestamp) $(_loggerGetServiceType) $(_loggerGetMode $MODE) $(_loggerGetTraceID) $(_loggerGetStackTrace $CONTEXT_FILE $CONTEXT_LINE) $(_loggerGetThread) - $(_loggerGetMessage $MESSAGE)" + fi + CONTEXT= +} + +# Important - don't call any log method from this method. Will become an infinite loop. Use echo to debug +_logToFile() { + local MODE=${1-"INFO"} + local targetFile="$LOG_BEHAVIOR_ADD_REDIRECTION" + # IF the file isn't passed, abort + if [ -z "$targetFile" ]; then + return + fi + # IF this is not being run in verbose mode and mode is debug or lower, abort + if [ "${VERBOSE_MODE}" != "$FLAG_Y" ] && [ "${VERBOSE_MODE}" != "true" ] && [ "${VERBOSE_MODE}" != "debug" ]; then + if [ "$MODE" == "DEBUG" ] || [ "$MODE" == "SILLY" ]; then + return + fi + fi + + # Create the file if it doesn't exist + if [ ! -f "${targetFile}" ]; then + return + # touch $targetFile > /dev/null 2>&1 || true + fi + # # Make it readable + # chmod 640 $targetFile > /dev/null 2>&1 || true + + # Log contents + printf "%s\n" "$_EFFECTIVE_MESSAGE" >> "$targetFile" || true +} + +logger() { + if [ "$LOG_BEHAVIOR_ADD_NEW_LINE" == "$FLAG_Y" ]; then + echo "" + fi + _getEffectiveMessage "$@" + local MODE=${2-"INFO"} + printf "%s\n" "$_EFFECTIVE_MESSAGE" + _logToFile "$MODE" +} + +logDebug(){ + VERBOSE_MODE=${VERBOSE_MODE-"false"} + CONTEXT=$(caller) + if [ "${VERBOSE_MODE}" == "$FLAG_Y" ] || [ "${VERBOSE_MODE}" == "true" ] || [ "${VERBOSE_MODE}" == "debug" ];then + logger "$1" "DEBUG" + else + logger "$1" "DEBUG" >&6 + fi + CONTEXT= +} + +logSilly(){ + VERBOSE_MODE=${VERBOSE_MODE-"false"} + CONTEXT=$(caller) + if [ "${VERBOSE_MODE}" == "silly" ];then + logger "$1" "DEBUG" + else + logger "$1" "DEBUG" >&6 + fi + CONTEXT= +} + +logError() { + CONTEXT=$(caller) + logger "$1" "ERROR" + CONTEXT= +} + +errorExit () { + CONTEXT=$(caller) + logger "$1" "ERROR" + CONTEXT= + exit 1 +} + +warn () { + CONTEXT=$(caller) + logger "$1" "WARN" + CONTEXT= +} + +note () { + CONTEXT=$(caller) + logger "$1" "NOTE" + CONTEXT= +} + +bannerStart() { + title=$1 + echo + echo -e "\033[1m${title}\033[0m" + echo +} + +bannerSection() { + title=$1 + echo + echo -e "******************************** ${title} ********************************" + echo +} + +bannerSubSection() { + title=$1 + echo + echo -e "************** ${title} *******************" + echo +} + +bannerMessge() { + title=$1 + echo + echo -e "********************************" + echo -e "${title}" + echo -e "********************************" + echo +} + +setRed () { + local input="$1" + echo -e \\033[31m${input}\\033[0m +} +setGreen () { + local input="$1" + echo -e \\033[32m${input}\\033[0m +} +setYellow () { + local input="$1" + echo -e \\033[33m${input}\\033[0m +} + +logger_addLinebreak () { + echo -e "---\n" +} + +bannerImportant() { + title=$1 + local bold="\033[1m" + local noColour="\033[0m" + echo + echo -e "${bold}######################################## IMPORTANT ########################################${noColour}" + echo -e "${bold}${title}${noColour}" + echo -e "${bold}###########################################################################################${noColour}" + echo +} + +bannerEnd() { + #TODO pass a title and calculate length dynamically so that start and end look alike + echo + echo "*****************************************************************************" + echo +} + +banner() { + title=$1 + content=$2 + bannerStart "${title}" + echo -e "$content" +} + +# The logic below helps us redirect content we'd normally hide to the log file. + # + # We have several commands which clutter the console with output and so use + # `cmd > /dev/null` - this redirects the command's output to null. + # + # However, the information we just hid maybe useful for support. Using the code pattern + # `cmd >&6` (instead of `cmd> >/dev/null` ), the command's output is hidden from the console + # but redirected to the installation log file + # + +#Default value of 6 is just null +exec 6>>/dev/null +redirectLogsToFile() { + echo "" + # local file=$1 + + # [ ! -z "${file}" ] || return 0 + + # local logDir=$(dirname "$file") + + # if [ ! -f "${file}" ]; then + # [ -d "${logDir}" ] || mkdir -p ${logDir} || \ + # ( echo "WARNING : Could not create parent directory (${logDir}) to redirect console log : ${file}" ; return 0 ) + # fi + + # #6 now points to the log file + # exec 6>>${file} + # #reference https://unix.stackexchange.com/questions/145651/using-exec-and-tee-to-redirect-logs-to-stdout-and-a-log-file-in-the-same-time + # exec 2>&1 > >(tee -a "${file}") +} + +# Check if a give key contains any sensitive string as part of it +# Based on the result, the caller can decide its value can be displayed or not +# Sample usage : isKeySensitive "${key}" && displayValue="******" || displayValue=${value} +isKeySensitive(){ + local key=$1 + local sensitiveKeys="password|secret|key|token" + + if [ -z "${key}" ]; then + return 1 + else + local lowercaseKey=$(echo "${key}" | tr '[:upper:]' '[:lower:]' 2>/dev/null) + [[ "${lowercaseKey}" =~ ${sensitiveKeys} ]] && return 0 || return 1 + fi +} + +getPrintableValueOfKey(){ + local displayValue= + local key="$1" + if [ -z "$key" ]; then + # This is actually an incorrect usage of this method but any logging will cause unexpected content in the caller + echo -n "" + return + fi + + local value="$2" + isKeySensitive "${key}" && displayValue="$SENSITIVE_KEY_VALUE" || displayValue="${value}" + echo -n $displayValue +} + +_createConsoleLog(){ + if [ -z "${JF_PRODUCT_HOME}" ]; then + return + fi + local targetFile="${JF_PRODUCT_HOME}/var/log/console.log" + mkdir -p "${JF_PRODUCT_HOME}/var/log" || true + if [ ! -f ${targetFile} ]; then + touch $targetFile > /dev/null 2>&1 || true + fi + chmod 640 $targetFile > /dev/null 2>&1 || true +} + +# Output from application's logs are piped to this method. It checks a configuration variable to determine if content should be logged to +# the common console.log file +redirectServiceLogsToFile() { + + local result="0" + # check if the function getSystemValue exists + LC_ALL=C type getSystemValue > /dev/null 2>&1 || result="$?" + if [[ "$result" != "0" ]]; then + warn "Couldn't find the systemYamlHelper. Skipping log redirection" + return 0 + fi + + getSystemValue "shared.consoleLog" "NOT_SET" + if [[ "${YAML_VALUE}" == "false" ]]; then + logger "Redirection is set to false. Skipping log redirection" + return 0; + fi + + if [ -z "${JF_PRODUCT_HOME}" ] || [ "${JF_PRODUCT_HOME}" == "" ]; then + warn "JF_PRODUCT_HOME is unavailable. Skipping log redirection" + return 0 + fi + + local targetFile="${JF_PRODUCT_HOME}/var/log/console.log" + + _createConsoleLog + + while read -r line; do + printf '%s\n' "${line}" >> $targetFile || return 0 # Don't want to log anything - might clutter the screen + done +} + +## Display environment variables starting with JF_ along with its value +## Value of sensitive keys will be displayed as "******" +## +## Sample Display : +## +## ======================== +## JF Environment variables +## ======================== +## +## JF_SHARED_NODE_ID : locahost +## JF_SHARED_JOINKEY : ****** +## +## +displayEnv() { + local JFEnv=$(printenv | grep ^JF_ 2>/dev/null) + local key= + local value= + + if [ -z "${JFEnv}" ]; then + return + fi + + cat << ENV_START_MESSAGE + +======================== +JF Environment variables +======================== +ENV_START_MESSAGE + + for entry in ${JFEnv}; do + key=$(echo "${entry}" | awk -F'=' '{print $1}') + value=$(echo "${entry}" | awk -F'=' '{print $2}') + + isKeySensitive "${key}" && value="******" || value=${value} + + printf "\n%-35s%s" "${key}" " : ${value}" + done + echo; +} + +_addLogRotateConfiguration() { + logDebug "Method ${FUNCNAME[0]}" + # mandatory inputs + local confFile="$1" + local logFile="$2" + + # Method available in _ioOperations.sh + LC_ALL=C type io_setYQPath > /dev/null 2>&1 || return 1 + + io_setYQPath + + # Method available in _systemYamlHelper.sh + LC_ALL=C type getSystemValue > /dev/null 2>&1 || return 1 + + local frequency="daily" + local archiveFolder="archived" + + local compressLogFiles= + getSystemValue "shared.logging.rotation.compress" "true" + if [[ "${YAML_VALUE}" == "true" ]]; then + compressLogFiles="compress" + fi + + getSystemValue "shared.logging.rotation.maxFiles" "10" + local noOfBackupFiles="${YAML_VALUE}" + + getSystemValue "shared.logging.rotation.maxSizeMb" "25" + local sizeOfFile="${YAML_VALUE}M" + + logDebug "Adding logrotate configuration for [$logFile] to [$confFile]" + + # Add configuration to file + local confContent=$(cat << LOGROTATECONF +$logFile { + $frequency + missingok + rotate $noOfBackupFiles + $compressLogFiles + notifempty + olddir $archiveFolder + dateext + extension .log + dateformat -%Y-%m-%d + size ${sizeOfFile} +} +LOGROTATECONF +) + echo "${confContent}" > ${confFile} || return 1 +} + +_operationIsBySameUser() { + local targetUser="$1" + local currentUserID=$(id -u) + local currentUserName=$(id -un) + + if [ $currentUserID == $targetUser ] || [ $currentUserName == $targetUser ]; then + echo -n "yes" + else + echo -n "no" + fi +} + +_addCronJobForLogrotate() { + logDebug "Method ${FUNCNAME[0]}" + + # Abort if logrotate is not available + [ "$(io_commandExists 'crontab')" != "yes" ] && warn "cron is not available" && return 1 + + # mandatory inputs + local productHome="$1" + local confFile="$2" + local cronJobOwner="$3" + + # We want to use our binary if possible. It may be more recent than the one in the OS + local logrotateBinary="$productHome/app/third-party/logrotate/logrotate" + + if [ ! -f "$logrotateBinary" ]; then + logrotateBinary="logrotate" + [ "$(io_commandExists 'logrotate')" != "yes" ] && warn "logrotate is not available" && return 1 + fi + local cmd="$logrotateBinary ${confFile} --state $productHome/var/etc/logrotate/logrotate-state" #--verbose + + id -u $cronJobOwner > /dev/null 2>&1 || { warn "User $cronJobOwner does not exist. Aborting logrotate configuration" && return 1; } + + # Remove the existing line + removeLogRotation "$productHome" "$cronJobOwner" || true + + # Run logrotate daily at 23:55 hours + local cronInterval="55 23 * * * $cmd" + + local standaloneMode=$(_operationIsBySameUser "$cronJobOwner") + + # If this is standalone mode, we cannot use -u - the user running this process may not have the necessary privileges + if [ "$standaloneMode" == "no" ]; then + (crontab -l -u $cronJobOwner 2>/dev/null; echo "$cronInterval") | crontab -u $cronJobOwner - + else + (crontab -l 2>/dev/null; echo "$cronInterval") | crontab - + fi +} + +## Configure logrotate for a product +## Failure conditions: +## If logrotation could not be setup for some reason +## Parameters: +## $1: The product name +## $2: The product home +## Depends on global: none +## Updates global: none +## Returns: NA + +configureLogRotation() { + logDebug "Method ${FUNCNAME[0]}" + + # mandatory inputs + local productName="$1" + if [ -z $productName ]; then + warn "Incorrect usage. A product name is necessary for configuring log rotation" && return 1 + fi + + local productHome="$2" + if [ -z $productHome ]; then + warn "Incorrect usage. A product home folder is necessary for configuring log rotation" && return 1 + fi + + local logFile="${productHome}/var/log/console.log" + if [[ $(uname) == "Darwin" ]]; then + logger "Log rotation for [$logFile] has not been configured. Please setup manually" + return 0 + fi + + local userID="$3" + if [ -z $userID ]; then + warn "Incorrect usage. A userID is necessary for configuring log rotation" && return 1 + fi + + local groupID=${4:-$userID} + local logConfigOwner=${5:-$userID} + + logDebug "Configuring log rotation as user [$userID], group [$groupID], effective cron User [$logConfigOwner]" + + local errorMessage="Could not configure logrotate. Please configure log rotation of the file: [$logFile] manually" + + local confFile="${productHome}/var/etc/logrotate/logrotate.conf" + + # TODO move to recursive method + createDir "${productHome}" "$userID" "$groupID" || { warn "${errorMessage}" && return 1; } + createDir "${productHome}/var" "$userID" "$groupID" || { warn "${errorMessage}" && return 1; } + createDir "${productHome}/var/log" "$userID" "$groupID" || { warn "${errorMessage}" && return 1; } + createDir "${productHome}/var/log/archived" "$userID" "$groupID" || { warn "${errorMessage}" && return 1; } + + # TODO move to recursive method + createDir "${productHome}/var/etc" "$userID" "$groupID" || { warn "${errorMessage}" && return 1; } + createDir "${productHome}/var/etc/logrotate" "$logConfigOwner" || { warn "${errorMessage}" && return 1; } + + # conf file should be owned by the user running the script + createFile "${confFile}" "${logConfigOwner}" || { warn "Could not create configuration file [$confFile]" return 1; } + + _addLogRotateConfiguration "${confFile}" "${logFile}" "$userID" "$groupID" || { warn "${errorMessage}" && return 1; } + _addCronJobForLogrotate "${productHome}" "${confFile}" "${logConfigOwner}" || { warn "${errorMessage}" && return 1; } +} + +_pauseExecution() { + if [ "${VERBOSE_MODE}" == "debug" ]; then + + local breakPoint="$1" + if [ ! -z "$breakPoint" ]; then + printf "${cBlue}Breakpoint${cClear} [$breakPoint] " + echo "" + fi + printf "${cBlue}Press enter once you are ready to continue${cClear}" + read -s choice + echo "" + fi +} + +# removeLogRotation "$productHome" "$cronJobOwner" || true +removeLogRotation() { + logDebug "Method ${FUNCNAME[0]}" + if [[ $(uname) == "Darwin" ]]; then + logDebug "Not implemented for Darwin." + return 0 + fi + local productHome="$1" + local cronJobOwner="$2" + local standaloneMode=$(_operationIsBySameUser "$cronJobOwner") + + local confFile="${productHome}/var/etc/logrotate/logrotate.conf" + + if [ "$standaloneMode" == "no" ]; then + crontab -l -u $cronJobOwner 2>/dev/null | grep -v "$confFile" | crontab -u $cronJobOwner - + else + crontab -l 2>/dev/null | grep -v "$confFile" | crontab - + fi +} + +# NOTE: This method does not check the configuration to see if redirection is necessary. +# This is intentional. If we don't redirect, tomcat logs might get redirected to a folder/file +# that does not exist, causing the service itself to not start +setupTomcatRedirection() { + logDebug "Method ${FUNCNAME[0]}" + local consoleLog="${JF_PRODUCT_HOME}/var/log/console.log" + _createConsoleLog + export CATALINA_OUT="${consoleLog}" +} + +setupScriptLogsRedirection() { + logDebug "Method ${FUNCNAME[0]}" + if [ -z "${JF_PRODUCT_HOME}" ]; then + logDebug "No JF_PRODUCT_HOME. Returning" + return + fi + # Create the console.log file if it is not already present + # _createConsoleLog || true + # # Ensure any logs (logger/logError/warn) also get redirected to the console.log + # # Using installer.log as a temparory fix. Please change this to console.log once INST-291 is fixed + export LOG_BEHAVIOR_ADD_REDIRECTION="${JF_PRODUCT_HOME}/var/log/console.log" + export LOG_BEHAVIOR_ADD_META="$FLAG_Y" +} + +# Returns Y if this method is run inside a container +isRunningInsideAContainer() { + local check1=$(grep -sq 'docker\|kubepods' /proc/1/cgroup; echo $?) + local check2=$(grep -sq 'containers' /proc/self/mountinfo; echo $?) + if [[ $check1 == 0 || $check2 == 0 || -f "/.dockerenv" ]]; then + echo -n "$FLAG_Y" + else + echo -n "$FLAG_N" + fi +} + +POSTGRES_USER=999 +NGINX_USER=104 +NGINX_GROUP=107 +ES_USER=1000 +REDIS_USER=999 +MONGO_USER=999 +RABBITMQ_USER=999 +LOG_FILE_PERMISSION=640 +PID_FILE_PERMISSION=644 + +# Copy file +copyFile(){ + local source=$1 + local target=$2 + local mode=${3:-overwrite} + local enableVerbose=${4:-"${FLAG_N}"} + local verboseFlag="" + + if [ ! -z "${enableVerbose}" ] && [ "${enableVerbose}" == "${FLAG_Y}" ]; then + verboseFlag="-v" + fi + + if [[ ! ( $source && $target ) ]]; then + warn "Source and target is mandatory to copy file" + return 1 + fi + + if [[ -f "${target}" ]]; then + [[ "$mode" = "overwrite" ]] && ( cp ${verboseFlag} -f "$source" "$target" || errorExit "Unable to copy file, command : cp -f ${source} ${target}") || true + else + cp ${verboseFlag} -f "$source" "$target" || errorExit "Unable to copy file, command : cp -f ${source} ${target}" + fi +} + +# Copy files recursively from given source directory to destination directory +# This method wil copy but will NOT overwrite +# Destination will be created if its not available +copyFilesNoOverwrite(){ + local src=$1 + local dest=$2 + local enableVerboseCopy="${3:-${FLAG_Y}}" + + if [[ -z "${src}" || -z "${dest}" ]]; then + return + fi + + if [ -d "${src}" ] && [ "$(ls -A ${src})" ]; then + local relativeFilePath="" + local targetFilePath="" + + for file in $(find ${src} -type f 2>/dev/null) ; do + # Derive relative path and attach it to destination + # Example : + # src=/extra_config + # dest=/var/opt/jfrog/artifactory/etc + # file=/extra_config/config.xml + # relativeFilePath=config.xml + # targetFilePath=/var/opt/jfrog/artifactory/etc/config.xml + relativeFilePath=${file/${src}/} + targetFilePath=${dest}${relativeFilePath} + + createDir "$(dirname "$targetFilePath")" + copyFile "${file}" "${targetFilePath}" "no_overwrite" "${enableVerboseCopy}" + done + fi +} + +# TODO : WINDOWS ? +# Check the max open files and open processes set on the system +checkULimits () { + local minMaxOpenFiles=${1:-32000} + local minMaxOpenProcesses=${2:-1024} + local setValue=${3:-true} + local warningMsgForFiles=${4} + local warningMsgForProcesses=${5} + + logger "Checking open files and processes limits" + + local currentMaxOpenFiles=$(ulimit -n) + logger "Current max open files is $currentMaxOpenFiles" + if [ ${currentMaxOpenFiles} != "unlimited" ] && [ "$currentMaxOpenFiles" -lt "$minMaxOpenFiles" ]; then + if [ "${setValue}" ]; then + ulimit -n "${minMaxOpenFiles}" >/dev/null 2>&1 || warn "Max number of open files $currentMaxOpenFiles is low!" + [ -z "${warningMsgForFiles}" ] || warn "${warningMsgForFiles}" + else + errorExit "Max number of open files $currentMaxOpenFiles, is too low. Cannot run the application!" + fi + fi + + local currentMaxOpenProcesses=$(ulimit -u) + logger "Current max open processes is $currentMaxOpenProcesses" + if [ "$currentMaxOpenProcesses" != "unlimited" ] && [ "$currentMaxOpenProcesses" -lt "$minMaxOpenProcesses" ]; then + if [ "${setValue}" ]; then + ulimit -u "${minMaxOpenProcesses}" >/dev/null 2>&1 || warn "Max number of open files $currentMaxOpenFiles is low!" + [ -z "${warningMsgForProcesses}" ] || warn "${warningMsgForProcesses}" + else + errorExit "Max number of open files $currentMaxOpenProcesses, is too low. Cannot run the application!" + fi + fi +} + +createDirs() { + local appDataDir=$1 + local serviceName=$2 + local folders="backup bootstrap data etc logs work" + + [ -z "${appDataDir}" ] && errorExit "An application directory is mandatory to create its data structure" || true + [ -z "${serviceName}" ] && errorExit "A service name is mandatory to create service data structure" || true + + for folder in ${folders} + do + folder=${appDataDir}/${folder}/${serviceName} + if [ ! -d "${folder}" ]; then + logger "Creating folder : ${folder}" + mkdir -p "${folder}" || errorExit "Failed to create ${folder}" + fi + done +} + + +testReadWritePermissions () { + local dir_to_check=$1 + local error=false + + [ -d ${dir_to_check} ] || errorExit "'${dir_to_check}' is not a directory" + + local test_file=${dir_to_check}/test-permissions + + # Write file + if echo test > ${test_file} 1> /dev/null 2>&1; then + # Write succeeded. Testing read... + if cat ${test_file} > /dev/null; then + rm -f ${test_file} + else + error=true + fi + else + error=true + fi + + if [ ${error} == true ]; then + return 1 + else + return 0 + fi +} + +# Test directory has read/write permissions for current user +testDirectoryPermissions () { + local dir_to_check=$1 + local error=false + + [ -d ${dir_to_check} ] || errorExit "'${dir_to_check}' is not a directory" + + local u_id=$(id -u) + local id_str="id ${u_id}" + + logger "Testing directory ${dir_to_check} has read/write permissions for user ${id_str}" + + if ! testReadWritePermissions ${dir_to_check}; then + error=true + fi + + if [ "${error}" == true ]; then + local stat_data=$(stat -Lc "Directory: %n, permissions: %a, owner: %U, group: %G" ${dir_to_check}) + logger "###########################################################" + logger "${dir_to_check} DOES NOT have proper permissions for user ${id_str}" + logger "${stat_data}" + logger "Mounted directory must have read/write permissions for user ${id_str}" + logger "###########################################################" + errorExit "Directory ${dir_to_check} has bad permissions for user ${id_str}" + fi + logger "Permissions for ${dir_to_check} are good" +} + +# Utility method to create a directory path recursively with chown feature as +# Failure conditions: +## Exits if unable to create a directory +# Parameters: +## $1: Root directory from where the path can be created +## $2: List of recursive child directories separated by space +## $3: user who should own the directory. Optional +## $4: group who should own the directory. Optional +# Depends on global: none +# Updates global: none +# Returns: NA +# +# Usage: +# createRecursiveDir "/opt/jfrog/product/var" "bootstrap tomcat lib" "user_name" "group_name" +createRecursiveDir(){ + local rootDir=$1 + local pathDirs=$2 + local user=$3 + local group=${4:-${user}} + local fullPath= + + [ ! -z "${rootDir}" ] || return 0 + + createDir "${rootDir}" "${user}" "${group}" + + [ ! -z "${pathDirs}" ] || return 0 + + fullPath=${rootDir} + + for dir in ${pathDirs}; do + fullPath=${fullPath}/${dir} + createDir "${fullPath}" "${user}" "${group}" + done +} + +# Utility method to create a directory +# Failure conditions: +## Exits if unable to create a directory +# Parameters: +## $1: directory to create +## $2: user who should own the directory. Optional +## $3: group who should own the directory. Optional +# Depends on global: none +# Updates global: none +# Returns: NA + +createDir(){ + local dirName="$1" + local printMessage=no + logSilly "Method ${FUNCNAME[0]} invoked with [$dirName]" + [ -z "${dirName}" ] && return + + logDebug "Attempting to create ${dirName}" + mkdir -p "${dirName}" || errorExit "Unable to create directory: [${dirName}]" + local userID="$2" + local groupID=${3:-$userID} + + # If UID/GID is passed, chown the folder + if [ ! -z "$userID" ] && [ ! -z "$groupID" ]; then + # Earlier, this line would have returned 1 if it failed. Now it just warns. + # This is intentional. Earlier, this line would NOT be reached if the folder already existed. + # Since it will always come to this line and the script may be running as a non-root user, this method will just warn if + # setting permissions fails (so as to not affect any existing flows) + io_setOwnershipNonRecursive "$dirName" "$userID" "$groupID" || warn "Could not set owner of [$dirName] to [$userID:$groupID]" + fi + # logging message to print created dir with user and group + local logMessage=${4:-$printMessage} + if [[ "${logMessage}" == "yes" ]]; then + logger "Successfully created directory [${dirName}]. Owner: [${userID}:${groupID}]" + fi +} + +removeSoftLinkAndCreateDir () { + local dirName="$1" + local userID="$2" + local groupID="$3" + local logMessage="$4" + removeSoftLink "${dirName}" + createDir "${dirName}" "${userID}" "${groupID}" "${logMessage}" +} + +# Utility method to remove a soft link +removeSoftLink () { + local dirName="$1" + if [[ -L "${dirName}" ]]; then + targetLink=$(readlink -f "${dirName}") + logger "Removing the symlink [${dirName}] pointing to [${targetLink}]" + rm -f "${dirName}" + fi +} + +# Check Directory exist in the path +checkDirExists () { + local directoryPath="$1" + + [[ -d "${directoryPath}" ]] && echo -n "true" || echo -n "false" +} + + +# Utility method to create a file +# Failure conditions: +# Parameters: +## $1: file to create +# Depends on global: none +# Updates global: none +# Returns: NA + +createFile(){ + local fileName="$1" + logSilly "Method ${FUNCNAME[0]} [$fileName]" + [ -f "${fileName}" ] && return 0 + touch "${fileName}" || return 1 + + local userID="$2" + local groupID=${3:-$userID} + + # If UID/GID is passed, chown the folder + if [ ! -z "$userID" ] && [ ! -z "$groupID" ]; then + io_setOwnership "$fileName" "$userID" "$groupID" || return 1 + fi +} + +# Check File exist in the filePath +# IMPORTANT- DON'T ADD LOGGING to this method +checkFileExists () { + local filePath="$1" + + [[ -f "${filePath}" ]] && echo -n "true" || echo -n "false" +} + +# Check for directories contains any (files or sub directories) +# IMPORTANT- DON'T ADD LOGGING to this method +checkDirContents () { + local directoryPath="$1" + if [[ "$(ls -1 "${directoryPath}" | wc -l)" -gt 0 ]]; then + echo -n "true" + else + echo -n "false" + fi +} + +# Check contents exist in directory +# IMPORTANT- DON'T ADD LOGGING to this method +checkContentExists () { + local source="$1" + + if [[ "$(checkDirContents "${source}")" != "true" ]]; then + echo -n "false" + else + echo -n "true" + fi +} + +# Resolve the variable +# IMPORTANT- DON'T ADD LOGGING to this method +evalVariable () { + local output="$1" + local input="$2" + + eval "${output}"=\${"${input}"} + eval echo \${"${output}"} +} + +# Usage: if [ "$(io_commandExists 'curl')" == "yes" ] +# IMPORTANT- DON'T ADD LOGGING to this method +io_commandExists() { + local commandToExecute="$1" + hash "${commandToExecute}" 2>/dev/null + local rt=$? + if [ "$rt" == 0 ]; then echo -n "yes"; else echo -n "no"; fi +} + +# Usage: if [ "$(io_curlExists)" != "yes" ] +# IMPORTANT- DON'T ADD LOGGING to this method +io_curlExists() { + io_commandExists "curl" +} + + +io_hasMatch() { + logSilly "Method ${FUNCNAME[0]}" + local result=0 + logDebug "Executing [echo \"$1\" | grep \"$2\" >/dev/null 2>&1]" + echo "$1" | grep "$2" >/dev/null 2>&1 || result=1 + return $result +} + +# Utility method to check if the string passed (usually a connection url) corresponds to this machine itself +# Failure conditions: None +# Parameters: +## $1: string to check against +# Depends on global: none +# Updates global: IS_LOCALHOST with value "yes/no" +# Returns: NA + +io_getIsLocalhost() { + logSilly "Method ${FUNCNAME[0]}" + IS_LOCALHOST="$FLAG_N" + local inputString="$1" + logDebug "Parsing [$inputString] to check if we are dealing with this machine itself" + + io_hasMatch "$inputString" "localhost" && { + logDebug "Found localhost. Returning [$FLAG_Y]" + IS_LOCALHOST="$FLAG_Y" && return; + } || logDebug "Did not find match for localhost" + + local hostIP=$(io_getPublicHostIP) + io_hasMatch "$inputString" "$hostIP" && { + logDebug "Found $hostIP. Returning [$FLAG_Y]" + IS_LOCALHOST="$FLAG_Y" && return; + } || logDebug "Did not find match for $hostIP" + + local hostID=$(io_getPublicHostID) + io_hasMatch "$inputString" "$hostID" && { + logDebug "Found $hostID. Returning [$FLAG_Y]" + IS_LOCALHOST="$FLAG_Y" && return; + } || logDebug "Did not find match for $hostID" + + local hostName=$(io_getPublicHostName) + io_hasMatch "$inputString" "$hostName" && { + logDebug "Found $hostName. Returning [$FLAG_Y]" + IS_LOCALHOST="$FLAG_Y" && return; + } || logDebug "Did not find match for $hostName" + +} + +# Usage: if [ "$(io_tarExists)" != "yes" ] +# IMPORTANT- DON'T ADD LOGGING to this method +io_tarExists() { + io_commandExists "tar" +} + +# IMPORTANT- DON'T ADD LOGGING to this method +io_getPublicHostIP() { + local OS_TYPE=$(uname) + local publicHostIP= + if [ "${OS_TYPE}" == "Darwin" ]; then + ipStatus=$(ifconfig en0 | grep "status" | awk '{print$2}') + if [ "${ipStatus}" == "active" ]; then + publicHostIP=$(ifconfig en0 | grep inet | grep -v inet6 | awk '{print $2}') + else + errorExit "Host IP could not be resolved!" + fi + elif [ "${OS_TYPE}" == "Linux" ]; then + publicHostIP=$(hostname -i 2>/dev/null || echo "127.0.0.1") + fi + publicHostIP=$(echo "${publicHostIP}" | awk '{print $1}') + echo -n "${publicHostIP}" +} + +# Will return the short host name (up to the first dot) +# IMPORTANT- DON'T ADD LOGGING to this method +io_getPublicHostName() { + echo -n "$(hostname -s)" +} + +# Will return the full host name (use this as much as possible) +# IMPORTANT- DON'T ADD LOGGING to this method +io_getPublicHostID() { + echo -n "$(hostname)" +} + +# Utility method to backup a file +# Failure conditions: NA +# Parameters: filePath +# Depends on global: none, +# Updates global: none +# Returns: NA +io_backupFile() { + logSilly "Method ${FUNCNAME[0]}" + fileName="$1" + if [ ! -f "${filePath}" ]; then + logDebug "No file: [${filePath}] to backup" + return + fi + dateTime=$(date +"%Y-%m-%d-%H-%M-%S") + targetFileName="${fileName}.backup.${dateTime}" + yes | \cp -f "$fileName" "${targetFileName}" + logger "File [${fileName}] backedup as [${targetFileName}]" +} + +# Reference https://stackoverflow.com/questions/4023830/how-to-compare-two-strings-in-dot-separated-version-format-in-bash/4025065#4025065 +is_number() { + case "$BASH_VERSION" in + 3.1.*) + PATTERN='\^\[0-9\]+\$' + ;; + *) + PATTERN='^[0-9]+$' + ;; + esac + + [[ "$1" =~ $PATTERN ]] +} + +io_compareVersions() { + if [[ $# != 2 ]] + then + echo "Usage: min_version current minimum" + return + fi + + A="${1%%.*}" + B="${2%%.*}" + + if [[ "$A" != "$1" && "$B" != "$2" && "$A" == "$B" ]] + then + io_compareVersions "${1#*.}" "${2#*.}" + else + if is_number "$A" && is_number "$B" + then + if [[ "$A" -eq "$B" ]]; then + echo "0" + elif [[ "$A" -gt "$B" ]]; then + echo "1" + elif [[ "$A" -lt "$B" ]]; then + echo "-1" + fi + fi + fi +} + +# Reference https://stackoverflow.com/questions/369758/how-to-trim-whitespace-from-a-bash-variable +# Strip all leading and trailing spaces +# IMPORTANT- DON'T ADD LOGGING to this method +io_trim() { + local var="$1" + # remove leading whitespace characters + var="${var#"${var%%[![:space:]]*}"}" + # remove trailing whitespace characters + var="${var%"${var##*[![:space:]]}"}" + echo -n "$var" +} + +# temporary function will be removing it ASAP +# search for string and replace text in file +replaceText_migration_hook () { + local regexString="$1" + local replaceText="$2" + local file="$3" + + if [[ "$(checkFileExists "${file}")" != "true" ]]; then + return + fi + if [[ $(uname) == "Darwin" ]]; then + sed -i '' -e "s/${regexString}/${replaceText}/" "${file}" || warn "Failed to replace the text in ${file}" + else + sed -i -e "s/${regexString}/${replaceText}/" "${file}" || warn "Failed to replace the text in ${file}" + fi +} + +# search for string and replace text in file +replaceText () { + local regexString="$1" + local replaceText="$2" + local file="$3" + + if [[ "$(checkFileExists "${file}")" != "true" ]]; then + return + fi + if [[ $(uname) == "Darwin" ]]; then + sed -i '' -e "s#${regexString}#${replaceText}#" "${file}" || warn "Failed to replace the text in ${file}" + else + sed -i -e "s#${regexString}#${replaceText}#" "${file}" || warn "Failed to replace the text in ${file}" + logDebug "Replaced [$regexString] with [$replaceText] in [$file]" + fi +} + +# search for string and prepend text in file +prependText () { + local regexString="$1" + local text="$2" + local file="$3" + + if [[ "$(checkFileExists "${file}")" != "true" ]]; then + return + fi + if [[ $(uname) == "Darwin" ]]; then + sed -i '' -e '/'"${regexString}"'/i\'$'\n\\'"${text}"''$'\n' "${file}" || warn "Failed to prepend the text in ${file}" + else + sed -i -e '/'"${regexString}"'/i\'$'\n\\'"${text}"''$'\n' "${file}" || warn "Failed to prepend the text in ${file}" + fi +} + +# add text to beginning of the file +addText () { + local text="$1" + local file="$2" + + if [[ "$(checkFileExists "${file}")" != "true" ]]; then + return + fi + if [[ $(uname) == "Darwin" ]]; then + sed -i '' -e '1s/^/'"${text}"'\'$'\n/' "${file}" || warn "Failed to add the text in ${file}" + else + sed -i -e '1s/^/'"${text}"'\'$'\n/' "${file}" || warn "Failed to add the text in ${file}" + fi +} + +io_replaceString () { + local value="$1" + local firstString="$2" + local secondString="$3" + local separator=${4:-"/"} + local updateValue= + if [[ $(uname) == "Darwin" ]]; then + updateValue=$(echo "${value}" | sed "s${separator}${firstString}${separator}${secondString}${separator}") + else + updateValue=$(echo "${value}" | sed "s${separator}${firstString}${separator}${secondString}${separator}") + fi + echo -n "${updateValue}" +} + +_findYQ() { + # logSilly "Method ${FUNCNAME[0]}" (Intentionally not logging. Does not add value) + local parentDir="$1" + if [ -z "$parentDir" ]; then + return + fi + logDebug "Executing command [find "${parentDir}" -name third-party -type d]" + local yq=$(find "${parentDir}" -name third-party -type d) + if [ -d "${yq}/yq" ]; then + export YQ_PATH="${yq}/yq" + fi +} + + +io_setYQPath() { + # logSilly "Method ${FUNCNAME[0]}" (Intentionally not logging. Does not add value) + if [ "$(io_commandExists 'yq')" == "yes" ]; then + return + fi + + if [ ! -z "${JF_PRODUCT_HOME}" ] && [ -d "${JF_PRODUCT_HOME}" ]; then + _findYQ "${JF_PRODUCT_HOME}" + fi + + if [ -z "${YQ_PATH}" ] && [ ! -z "${COMPOSE_HOME}" ] && [ -d "${COMPOSE_HOME}" ]; then + _findYQ "${COMPOSE_HOME}" + fi + # TODO We can remove this block after all the code is restructured. + if [ -z "${YQ_PATH}" ] && [ ! -z "${SCRIPT_HOME}" ] && [ -d "${SCRIPT_HOME}" ]; then + _findYQ "${SCRIPT_HOME}" + fi + +} + +io_getLinuxDistribution() { + LINUX_DISTRIBUTION= + + # Make sure running on Linux + [ $(uname -s) != "Linux" ] && return + + # Find out what Linux distribution we are on + + cat /etc/*-release | grep -i Red >/dev/null 2>&1 && LINUX_DISTRIBUTION=RedHat || true + + # OS 6.x + cat /etc/issue.net | grep Red >/dev/null 2>&1 && LINUX_DISTRIBUTION=RedHat || true + + # OS 7.x + cat /etc/*-release | grep -i centos >/dev/null 2>&1 && LINUX_DISTRIBUTION=CentOS && LINUX_DISTRIBUTION_VER="7" || true + + # OS 8.x + grep -q -i "release 8" /etc/redhat-release >/dev/null 2>&1 && LINUX_DISTRIBUTION_VER="8" || true + + # OS 7.x + grep -q -i "release 7" /etc/redhat-release >/dev/null 2>&1 && LINUX_DISTRIBUTION_VER="7" || true + + # OS 6.x + grep -q -i "release 6" /etc/redhat-release >/dev/null 2>&1 && LINUX_DISTRIBUTION_VER="6" || true + + cat /etc/*-release | grep -i Red | grep -i 'VERSION=7' >/dev/null 2>&1 && LINUX_DISTRIBUTION=RedHat && LINUX_DISTRIBUTION_VER="7" || true + + cat /etc/*-release | grep -i debian >/dev/null 2>&1 && LINUX_DISTRIBUTION=Debian || true + + cat /etc/*-release | grep -i ubuntu >/dev/null 2>&1 && LINUX_DISTRIBUTION=Ubuntu || true +} + +## Utility method to check ownership of folders/files +## Failure conditions: + ## If invoked with incorrect inputs - FATAL + ## If file is not owned by the user & group +## Parameters: + ## user + ## group + ## folder to chown +## Globals: none +## Returns: none +## NOTE: The method does NOTHING if the OS is Mac +io_checkOwner () { + logSilly "Method ${FUNCNAME[0]}" + local osType=$(uname) + + if [ "${osType}" != "Linux" ]; then + logDebug "Unsupported OS. Skipping check" + return 0 + fi + + local file_to_check=$1 + local user_id_to_check=$2 + + + if [ -z "$user_id_to_check" ] || [ -z "$file_to_check" ]; then + errorExit "Invalid invocation of method. Missing mandatory inputs" + fi + + local group_id_to_check=${3:-$user_id_to_check} + local check_user_name=${4:-"no"} + + logDebug "Checking permissions on [$file_to_check] for user [$user_id_to_check] & group [$group_id_to_check]" + + local stat= + + if [ "${check_user_name}" == "yes" ]; then + stat=( $(stat -Lc "%U %G" ${file_to_check}) ) + else + stat=( $(stat -Lc "%u %g" ${file_to_check}) ) + fi + + local user_id=${stat[0]} + local group_id=${stat[1]} + + if [[ "${user_id}" != "${user_id_to_check}" ]] || [[ "${group_id}" != "${group_id_to_check}" ]] ; then + logDebug "Ownership mismatch. [${file_to_check}] is not owned by [${user_id_to_check}:${group_id_to_check}]" + return 1 + else + return 0 + fi +} + +## Utility method to change ownership of a file/folder - NON recursive +## Failure conditions: + ## If invoked with incorrect inputs - FATAL + ## If chown operation fails - returns 1 +## Parameters: + ## user + ## group + ## file to chown +## Globals: none +## Returns: none +## NOTE: The method does NOTHING if the OS is Mac + +io_setOwnershipNonRecursive() { + + local osType=$(uname) + if [ "${osType}" != "Linux" ]; then + return + fi + + local targetFile=$1 + local user=$2 + + if [ -z "$user" ] || [ -z "$targetFile" ]; then + errorExit "Invalid invocation of method. Missing mandatory inputs" + fi + + local group=${3:-$user} + logDebug "Method ${FUNCNAME[0]}. Executing [chown ${user}:${group} ${targetFile}]" + chown ${user}:${group} ${targetFile} || return 1 +} + +## Utility method to change ownership of a file. +## IMPORTANT +## If being called on a folder, should ONLY be called for fresh folders or may cause performance issues +## Failure conditions: + ## If invoked with incorrect inputs - FATAL + ## If chown operation fails - returns 1 +## Parameters: + ## user + ## group + ## file to chown +## Globals: none +## Returns: none +## NOTE: The method does NOTHING if the OS is Mac + +io_setOwnership() { + + local osType=$(uname) + if [ "${osType}" != "Linux" ]; then + return + fi + + local targetFile=$1 + local user=$2 + + if [ -z "$user" ] || [ -z "$targetFile" ]; then + errorExit "Invalid invocation of method. Missing mandatory inputs" + fi + + local group=${3:-$user} + logDebug "Method ${FUNCNAME[0]}. Executing [chown -R ${user}:${group} ${targetFile}]" + chown -R ${user}:${group} ${targetFile} || return 1 +} + +## Utility method to create third party folder structure necessary for Postgres +## Failure conditions: +## If creation of directory or assigning permissions fails +## Parameters: none +## Globals: +## POSTGRESQL_DATA_ROOT +## Returns: none +## NOTE: The method does NOTHING if the folder already exists +io_createPostgresDir() { + logDebug "Method ${FUNCNAME[0]}" + [ -z "${POSTGRESQL_DATA_ROOT}" ] && return 0 + + logDebug "Property [${POSTGRESQL_DATA_ROOT}] exists. Proceeding" + + createDir "${POSTGRESQL_DATA_ROOT}/data" + io_setOwnership "${POSTGRESQL_DATA_ROOT}" "${POSTGRES_USER}" "${POSTGRES_USER}" || errorExit "Setting ownership of [${POSTGRESQL_DATA_ROOT}] to [${POSTGRES_USER}:${POSTGRES_USER}] failed" +} + +## Utility method to create third party folder structure necessary for Nginx +## Failure conditions: +## If creation of directory or assigning permissions fails +## Parameters: none +## Globals: +## NGINX_DATA_ROOT +## Returns: none +## NOTE: The method does NOTHING if the folder already exists +io_createNginxDir() { + logDebug "Method ${FUNCNAME[0]}" + [ -z "${NGINX_DATA_ROOT}" ] && return 0 + + logDebug "Property [${NGINX_DATA_ROOT}] exists. Proceeding" + + createDir "${NGINX_DATA_ROOT}" + io_setOwnership "${NGINX_DATA_ROOT}" "${NGINX_USER}" "${NGINX_GROUP}" || errorExit "Setting ownership of [${NGINX_DATA_ROOT}] to [${NGINX_USER}:${NGINX_GROUP}] failed" +} + +## Utility method to create third party folder structure necessary for ElasticSearch +## Failure conditions: +## If creation of directory or assigning permissions fails +## Parameters: none +## Globals: +## ELASTIC_DATA_ROOT +## Returns: none +## NOTE: The method does NOTHING if the folder already exists +io_createElasticSearchDir() { + logDebug "Method ${FUNCNAME[0]}" + [ -z "${ELASTIC_DATA_ROOT}" ] && return 0 + + logDebug "Property [${ELASTIC_DATA_ROOT}] exists. Proceeding" + + createDir "${ELASTIC_DATA_ROOT}/data" + io_setOwnership "${ELASTIC_DATA_ROOT}" "${ES_USER}" "${ES_USER}" || errorExit "Setting ownership of [${ELASTIC_DATA_ROOT}] to [${ES_USER}:${ES_USER}] failed" +} + +## Utility method to create third party folder structure necessary for Redis +## Failure conditions: +## If creation of directory or assigning permissions fails +## Parameters: none +## Globals: +## REDIS_DATA_ROOT +## Returns: none +## NOTE: The method does NOTHING if the folder already exists +io_createRedisDir() { + logDebug "Method ${FUNCNAME[0]}" + [ -z "${REDIS_DATA_ROOT}" ] && return 0 + + logDebug "Property [${REDIS_DATA_ROOT}] exists. Proceeding" + + createDir "${REDIS_DATA_ROOT}" + io_setOwnership "${REDIS_DATA_ROOT}" "${REDIS_USER}" "${REDIS_USER}" || errorExit "Setting ownership of [${REDIS_DATA_ROOT}] to [${REDIS_USER}:${REDIS_USER}] failed" +} + +## Utility method to create third party folder structure necessary for Mongo +## Failure conditions: +## If creation of directory or assigning permissions fails +## Parameters: none +## Globals: +## MONGODB_DATA_ROOT +## Returns: none +## NOTE: The method does NOTHING if the folder already exists +io_createMongoDir() { + logDebug "Method ${FUNCNAME[0]}" + [ -z "${MONGODB_DATA_ROOT}" ] && return 0 + + logDebug "Property [${MONGODB_DATA_ROOT}] exists. Proceeding" + + createDir "${MONGODB_DATA_ROOT}/logs" + createDir "${MONGODB_DATA_ROOT}/configdb" + createDir "${MONGODB_DATA_ROOT}/db" + io_setOwnership "${MONGODB_DATA_ROOT}" "${MONGO_USER}" "${MONGO_USER}" || errorExit "Setting ownership of [${MONGODB_DATA_ROOT}] to [${MONGO_USER}:${MONGO_USER}] failed" +} + +## Utility method to create third party folder structure necessary for RabbitMQ +## Failure conditions: +## If creation of directory or assigning permissions fails +## Parameters: none +## Globals: +## RABBITMQ_DATA_ROOT +## Returns: none +## NOTE: The method does NOTHING if the folder already exists +io_createRabbitMQDir() { + logDebug "Method ${FUNCNAME[0]}" + [ -z "${RABBITMQ_DATA_ROOT}" ] && return 0 + + logDebug "Property [${RABBITMQ_DATA_ROOT}] exists. Proceeding" + + createDir "${RABBITMQ_DATA_ROOT}" + io_setOwnership "${RABBITMQ_DATA_ROOT}" "${RABBITMQ_USER}" "${RABBITMQ_USER}" || errorExit "Setting ownership of [${RABBITMQ_DATA_ROOT}] to [${RABBITMQ_USER}:${RABBITMQ_USER}] failed" +} + +# Add or replace a property in provided properties file +addOrReplaceProperty() { + local propertyName=$1 + local propertyValue=$2 + local propertiesPath=$3 + local delimiter=${4:-"="} + + # Return if any of the inputs are empty + [[ -z "$propertyName" || "$propertyName" == "" ]] && return + [[ -z "$propertyValue" || "$propertyValue" == "" ]] && return + [[ -z "$propertiesPath" || "$propertiesPath" == "" ]] && return + + grep "^${propertyName}\s*${delimiter}.*$" ${propertiesPath} > /dev/null 2>&1 + [ $? -ne 0 ] && echo -e "\n${propertyName}${delimiter}${propertyValue}" >> ${propertiesPath} + sed -i -e "s|^${propertyName}\s*${delimiter}.*$|${propertyName}${delimiter}${propertyValue}|g;" ${propertiesPath} +} + +# Set property only if its not set +io_setPropertyNoOverride(){ + local propertyName=$1 + local propertyValue=$2 + local propertiesPath=$3 + + # Return if any of the inputs are empty + [[ -z "$propertyName" || "$propertyName" == "" ]] && return + [[ -z "$propertyValue" || "$propertyValue" == "" ]] && return + [[ -z "$propertiesPath" || "$propertiesPath" == "" ]] && return + + grep "^${propertyName}:" ${propertiesPath} > /dev/null 2>&1 + if [ $? -ne 0 ]; then + echo -e "${propertyName}: ${propertyValue}" >> ${propertiesPath} || warn "Setting property ${propertyName}: ${propertyValue} in [ ${propertiesPath} ] failed" + else + logger "Skipping update of property : ${propertyName}" >&6 + fi +} + +# Add a line to a file if it doesn't already exist +addLine() { + local line_to_add=$1 + local target_file=$2 + logger "Trying to add line $1 to $2" >&6 2>&1 + cat "$target_file" | grep -F "$line_to_add" -wq >&6 2>&1 + if [ $? != 0 ]; then + logger "Line does not exist and will be added" >&6 2>&1 + echo $line_to_add >> $target_file || errorExit "Could not update $target_file" + fi +} + +# Utility method to check if a value (first parameter) exists in an array (2nd parameter) +# 1st parameter "value to find" +# 2nd parameter "The array to search in. Please pass a string with each value separated by space" +# Example: containsElement "y" "y Y n N" +containsElement () { + local searchElement=$1 + local searchArray=($2) + local found=1 + for elementInIndex in "${searchArray[@]}";do + if [[ $elementInIndex == $searchElement ]]; then + found=0 + fi + done + return $found +} + +# Utility method to get user's choice +# 1st parameter "what to ask the user" +# 2nd parameter "what choices to accept, separated by spaces" +# 3rd parameter "what is the default choice (to use if the user simply presses Enter)" +# Example 'getUserChoice "Are you feeling lucky? Punk!" "y n Y N" "y"' +getUserChoice(){ + configureLogOutput + read_timeout=${read_timeout:-0.5} + local choice="na" + local text_to_display=$1 + local choices=$2 + local default_choice=$3 + users_choice= + + until containsElement "$choice" "$choices"; do + echo "";echo ""; + sleep $read_timeout #This ensures correct placement of the question. + read -p "$text_to_display :" choice + : ${choice:=$default_choice} + done + users_choice=$choice + echo -e "\n$text_to_display: $users_choice" >&6 + sleep $read_timeout #This ensures correct logging +} + +setFilePermission () { + local permission=$1 + local file=$2 + chmod "${permission}" "${file}" || warn "Setting permission ${permission} to file [ ${file} ] failed" +} + + +#setting required paths +setAppDir (){ + SCRIPT_DIR=$(dirname $0) + SCRIPT_HOME="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" + APP_DIR="`cd "${SCRIPT_HOME}";pwd`" +} + +ZIP_TYPE="zip" +COMPOSE_TYPE="compose" +HELM_TYPE="helm" +RPM_TYPE="rpm" +DEB_TYPE="debian" + +sourceScript () { + local file="$1" + + [ ! -z "${file}" ] || errorExit "target file is not passed to source a file" + + if [ ! -f "${file}" ]; then + errorExit "${file} file is not found" + else + source "${file}" || errorExit "Unable to source ${file}, please check if the user ${USER} has permissions to perform this action" + fi +} +# Source required helpers +initHelpers () { + local systemYamlHelper="${APP_DIR}/systemYamlHelper.sh" + local thirdPartyDir=$(find ${APP_DIR}/.. -name third-party -type d) + export YQ_PATH="${thirdPartyDir}/yq" + LIBXML2_PATH="${thirdPartyDir}/libxml2/bin/xmllint" + export LD_LIBRARY_PATH="${thirdPartyDir}/libxml2/lib" + sourceScript "${systemYamlHelper}" +} +# Check migration info yaml file available in the path +checkMigrationInfoYaml () { + + if [[ -f "${APP_DIR}/migrationHelmInfo.yaml" ]]; then + MIGRATION_SYSTEM_YAML_INFO="${APP_DIR}/migrationHelmInfo.yaml" + INSTALLER="${HELM_TYPE}" + elif [[ -f "${APP_DIR}/migrationZipInfo.yaml" ]]; then + MIGRATION_SYSTEM_YAML_INFO="${APP_DIR}/migrationZipInfo.yaml" + INSTALLER="${ZIP_TYPE}" + elif [[ -f "${APP_DIR}/migrationRpmInfo.yaml" ]]; then + MIGRATION_SYSTEM_YAML_INFO="${APP_DIR}/migrationRpmInfo.yaml" + INSTALLER="${RPM_TYPE}" + elif [[ -f "${APP_DIR}/migrationDebInfo.yaml" ]]; then + MIGRATION_SYSTEM_YAML_INFO="${APP_DIR}/migrationDebInfo.yaml" + INSTALLER="${DEB_TYPE}" + elif [[ -f "${APP_DIR}/migrationComposeInfo.yaml" ]]; then + MIGRATION_SYSTEM_YAML_INFO="${APP_DIR}/migrationComposeInfo.yaml" + INSTALLER="${COMPOSE_TYPE}" + else + errorExit "File migration Info yaml does not exist in [${APP_DIR}]" + fi +} + +retrieveYamlValue () { + local yamlPath="$1" + local value="$2" + local output="$3" + local message="$4" + + [[ -z "${yamlPath}" ]] && errorExit "yamlPath is mandatory to get value from ${MIGRATION_SYSTEM_YAML_INFO}" + + getYamlValue "${yamlPath}" "${MIGRATION_SYSTEM_YAML_INFO}" "false" + value="${YAML_VALUE}" + if [[ -z "${value}" ]]; then + if [[ "${output}" == "Warning" ]]; then + warn "Empty value for ${yamlPath} in [${MIGRATION_SYSTEM_YAML_INFO}]" + elif [[ "${output}" == "Skip" ]]; then + return + else + errorExit "${message}" + fi + fi +} + +checkEnv () { + + if [[ "${INSTALLER}" == "${ZIP_TYPE}" ]]; then + # check Environment JF_PRODUCT_HOME is set before migration + NEW_DATA_DIR="$(evalVariable "NEW_DATA_DIR" "JF_PRODUCT_HOME")" + if [[ -z "${NEW_DATA_DIR}" ]]; then + errorExit "Environment variable JF_PRODUCT_HOME is not set, this is required to perform Migration" + fi + # appending var directory to $JF_PRODUCT_HOME + NEW_DATA_DIR="${NEW_DATA_DIR}/var" + elif [[ "${INSTALLER}" == "${HELM_TYPE}" ]]; then + getCustomDataDir_hook + NEW_DATA_DIR="${OLD_DATA_DIR}" + if [[ -z "${NEW_DATA_DIR}" ]] && [[ -z "${OLD_DATA_DIR}" ]]; then + errorExit "Could not find ${PROMPT_DATA_DIR_LOCATION} to perform Migration" + fi + else + # check Environment JF_ROOT_DATA_DIR is set before migration + OLD_DATA_DIR="$(evalVariable "OLD_DATA_DIR" "JF_ROOT_DATA_DIR")" + # check Environment JF_ROOT_DATA_DIR is set before migration + NEW_DATA_DIR="$(evalVariable "NEW_DATA_DIR" "JF_ROOT_DATA_DIR")" + if [[ -z "${NEW_DATA_DIR}" ]] && [[ -z "${OLD_DATA_DIR}" ]]; then + errorExit "Could not find ${PROMPT_DATA_DIR_LOCATION} to perform Migration" + fi + # appending var directory to $JF_PRODUCT_HOME + NEW_DATA_DIR="${NEW_DATA_DIR}/var" + fi + +} + +getDataDir () { + + if [[ "${INSTALLER}" == "${ZIP_TYPE}" || "${INSTALLER}" == "${COMPOSE_TYPE}"|| "${INSTALLER}" == "${HELM_TYPE}" ]]; then + checkEnv + else + getCustomDataDir_hook + NEW_DATA_DIR="`cd "${APP_DIR}"/../../;pwd`" + NEW_DATA_DIR="${NEW_DATA_DIR}/var" + fi +} + +# Retrieve Product name from MIGRATION_SYSTEM_YAML_INFO +getProduct () { + retrieveYamlValue "migration.product" "${YAML_VALUE}" "Fail" "Empty value under ${yamlPath} in [${MIGRATION_SYSTEM_YAML_INFO}]" + PRODUCT="${YAML_VALUE}" + PRODUCT=$(echo "${PRODUCT}" | tr '[:upper:]' '[:lower:]' 2>/dev/null) + if [[ "${PRODUCT}" != "artifactory" && "${PRODUCT}" != "distribution" && "${PRODUCT}" != "xray" ]]; then + errorExit "migration.product in [${MIGRATION_SYSTEM_YAML_INFO}] is not correct, please set based on product as ARTIFACTORY or DISTRIBUTION" + fi + if [[ "${INSTALLER}" == "${HELM_TYPE}" ]]; then + JF_USER="${PRODUCT}" + fi +} +# Compare product version with minProductVersion and maxProductVersion +migrateCheckVersion () { + local productVersion="$1" + local minProductVersion="$2" + local maxProductVersion="$3" + local productVersion618="6.18.0" + local unSupportedProductVersions7=("7.2.0 7.2.1") + + if [[ "$(io_compareVersions "${productVersion}" "${maxProductVersion}")" -eq 0 || "$(io_compareVersions "${productVersion}" "${maxProductVersion}")" -eq 1 ]]; then + logger "Migration not necessary. ${PRODUCT} is already ${productVersion}" + exit 11 + elif [[ "$(io_compareVersions "${productVersion}" "${minProductVersion}")" -eq 0 || "$(io_compareVersions "${productVersion}" "${minProductVersion}")" -eq 1 ]]; then + if [[ ("$(io_compareVersions "${productVersion}" "${productVersion618}")" -eq 0 || "$(io_compareVersions "${productVersion}" "${productVersion618}")" -eq 1) && " ${unSupportedProductVersions7[@]} " =~ " ${CURRENT_VERSION} " ]]; then + touch /tmp/error; + errorExit "Current ${PRODUCT} version (${productVersion}) does not support migration to ${CURRENT_VERSION}" + else + bannerStart "Detected ${PRODUCT} ${productVersion}, initiating migration" + fi + else + logger "Current ${PRODUCT} ${productVersion} version is not supported for migration" + exit 1 + fi +} + +getProductVersion () { + local minProductVersion="$1" + local maxProductVersion="$2" + local newfilePath="$3" + local oldfilePath="$4" + local propertyInDocker="$5" + local property="$6" + local productVersion= + local status= + + if [[ "$INSTALLER" == "${COMPOSE_TYPE}" ]]; then + if [[ -f "${oldfilePath}" ]]; then + if [[ "${PRODUCT}" == "artifactory" ]]; then + productVersion="$(readKey "${property}" "${oldfilePath}")" + else + productVersion="$(cat "${oldfilePath}")" + fi + status="success" + elif [[ -f "${newfilePath}" ]]; then + productVersion="$(readKey "${propertyInDocker}" "${newfilePath}")" + status="fail" + else + logger "File [${oldfilePath}] or [${newfilePath}] not found to get current version." + exit 0 + fi + elif [[ "$INSTALLER" == "${HELM_TYPE}" ]]; then + if [[ -f "${oldfilePath}" ]]; then + if [[ "${PRODUCT}" == "artifactory" ]]; then + productVersion="$(readKey "${property}" "${oldfilePath}")" + else + productVersion="$(cat "${oldfilePath}")" + fi + status="success" + else + productVersion="${CURRENT_VERSION}" + [[ -z "${productVersion}" || "${productVersion}" == "" ]] && logger "${PRODUCT} CURRENT_VERSION is not set" && exit 0 + fi + else + if [[ -f "${newfilePath}" ]]; then + productVersion="$(readKey "${property}" "${newfilePath}")" + status="fail" + elif [[ -f "${oldfilePath}" ]]; then + productVersion="$(readKey "${property}" "${oldfilePath}")" + status="success" + else + if [[ "${INSTALLER}" == "${ZIP_TYPE}" ]]; then + logger "File [${newfilePath}] not found to get current version." + else + logger "File [${oldfilePath}] or [${newfilePath}] not found to get current version." + fi + exit 0 + fi + fi + if [[ -z "${productVersion}" || "${productVersion}" == "" ]]; then + [[ "${status}" == "success" ]] && logger "No version found in file [${oldfilePath}]." + [[ "${status}" == "fail" ]] && logger "No version found in file [${newfilePath}]." + exit 0 + fi + + migrateCheckVersion "${productVersion}" "${minProductVersion}" "${maxProductVersion}" +} + +readKey () { + local property="$1" + local file="$2" + local version= + + while IFS='=' read -r key value || [ -n "${key}" ]; + do + [[ ! "${key}" =~ \#.* && ! -z "${key}" && ! -z "${value}" ]] + key="$(io_trim "${key}")" + if [[ "${key}" == "${property}" ]]; then + version="${value}" && check=true && break + else + check=false + fi + done < "${file}" + if [[ "${check}" == "false" ]]; then + return + fi + echo "${version}" +} + +# create Log directory +createLogDir () { + if [[ "${INSTALLER}" == "${DEB_TYPE}" || "${INSTALLER}" == "${RPM_TYPE}" ]]; then + getUserAndGroupFromFile + removeSoftLinkAndCreateDir "${NEW_DATA_DIR}/log" "${USER_TO_CHECK}" "${GROUP_TO_CHECK}" + fi +} + +# Creating migration log file +creationMigrateLog () { + local LOG_FILE_NAME="migration.log" + createLogDir + local MIGRATION_LOG_FILE="${NEW_DATA_DIR}/log/${LOG_FILE_NAME}" + if [[ "${INSTALLER}" == "${COMPOSE_TYPE}" || "${INSTALLER}" == "${HELM_TYPE}" ]]; then + MIGRATION_LOG_FILE="${SCRIPT_HOME}/${LOG_FILE_NAME}" + fi + touch "${MIGRATION_LOG_FILE}" + setFilePermission "${LOG_FILE_PERMISSION}" "${MIGRATION_LOG_FILE}" + exec &> >(tee -a "${MIGRATION_LOG_FILE}") +} +# Set path where system.yaml should create +setSystemYamlPath () { + SYSTEM_YAML_PATH="${NEW_DATA_DIR}/etc/system.yaml" + if [[ "${INSTALLER}" != "${HELM_TYPE}" ]]; then + logger "system.yaml will be created in path [${SYSTEM_YAML_PATH}]" + fi +} +# Create directory +createDirectory () { + local directory="$1" + local output="$2" + local check=false + local message="Could not create directory ${directory}, please check if the user ${USER} has permissions to perform this action" + removeSoftLink "${directory}" + mkdir -p "${directory}" && check=true || check=false + if [[ "${check}" == "false" ]]; then + if [[ "${output}" == "Warning" ]]; then + warn "${message}" + else + errorExit "${message}" + fi + fi + setOwnershipBasedOnInstaller "${directory}" +} + +setOwnershipBasedOnInstaller () { + local directory="$1" + if [[ "${INSTALLER}" == "${DEB_TYPE}" || "${INSTALLER}" == "${RPM_TYPE}" ]]; then + getUserAndGroupFromFile + chown -R ${USER_TO_CHECK}:${GROUP_TO_CHECK} "${directory}" || warn "Setting ownership on $directory failed" + elif [[ "${INSTALLER}" == "${COMPOSE_TYPE}" || "${INSTALLER}" == "${HELM_TYPE}" ]]; then + io_setOwnership "${directory}" "${JF_USER}" "${JF_USER}" + fi +} + +getUserAndGroup () { + local file="$1" + read uid gid <<<$(stat -c '%U %G' ${file}) + USER_TO_CHECK="${uid}" + GROUP_TO_CHECK="${gid}" +} + +# set ownership +getUserAndGroupFromFile () { + case $PRODUCT in + artifactory) + getUserAndGroup "/etc/opt/jfrog/artifactory/artifactory.properties" + ;; + distribution) + getUserAndGroup "${OLD_DATA_DIR}/etc/versions.properties" + ;; + xray) + getUserAndGroup "${OLD_DATA_DIR}/security/master.key" + ;; + esac +} + +# creating required directories +createRequiredDirs () { + bannerSubSection "CREATING REQUIRED DIRECTORIES" + if [[ "${INSTALLER}" == "${COMPOSE_TYPE}" || "${INSTALLER}" == "${HELM_TYPE}" ]]; then + removeSoftLinkAndCreateDir "${NEW_DATA_DIR}/etc/security" "${JF_USER}" "${JF_USER}" "yes" + removeSoftLinkAndCreateDir "${NEW_DATA_DIR}/data" "${JF_USER}" "${JF_USER}" "yes" + removeSoftLinkAndCreateDir "${NEW_DATA_DIR}/log/archived" "${JF_USER}" "${JF_USER}" "yes" + removeSoftLinkAndCreateDir "${NEW_DATA_DIR}/work" "${JF_USER}" "${JF_USER}" "yes" + removeSoftLinkAndCreateDir "${NEW_DATA_DIR}/backup" "${JF_USER}" "${JF_USER}" "yes" + io_setOwnership "${NEW_DATA_DIR}" "${JF_USER}" "${JF_USER}" + if [[ "${INSTALLER}" == "${COMPOSE_TYPE}" ]]; then + removeSoftLinkAndCreateDir "${NEW_DATA_DIR}/data/postgres" "${POSTGRES_USER}" "${POSTGRES_USER}" "yes" + fi + elif [[ "${INSTALLER}" == "${DEB_TYPE}" || "${INSTALLER}" == "${RPM_TYPE}" ]]; then + getUserAndGroupFromFile + removeSoftLinkAndCreateDir "${NEW_DATA_DIR}/etc" "${USER_TO_CHECK}" "${GROUP_TO_CHECK}" "yes" + removeSoftLinkAndCreateDir "${NEW_DATA_DIR}/etc/security" "${USER_TO_CHECK}" "${GROUP_TO_CHECK}" "yes" + removeSoftLinkAndCreateDir "${NEW_DATA_DIR}/data" "${USER_TO_CHECK}" "${GROUP_TO_CHECK}" "yes" + removeSoftLinkAndCreateDir "${NEW_DATA_DIR}/log/archived" "${USER_TO_CHECK}" "${GROUP_TO_CHECK}" "yes" + removeSoftLinkAndCreateDir "${NEW_DATA_DIR}/work" "${USER_TO_CHECK}" "${GROUP_TO_CHECK}" "yes" + removeSoftLinkAndCreateDir "${NEW_DATA_DIR}/backup" "${USER_TO_CHECK}" "${GROUP_TO_CHECK}" "yes" + fi +} + +# Check entry in map is format +checkMapEntry () { + local entry="$1" + + [[ "${entry}" != *"="* ]] && echo -n "false" || echo -n "true" +} +# Check value Empty and warn +warnIfEmpty () { + local filePath="$1" + local yamlPath="$2" + local check= + + if [[ -z "${filePath}" ]]; then + warn "Empty value in yamlpath [${yamlPath} in [${MIGRATION_SYSTEM_YAML_INFO}]" + check=false + else + check=true + fi + echo "${check}" +} + +logCopyStatus () { + local status="$1" + local logMessage="$2" + local warnMessage="$3" + + [[ "${status}" == "success" ]] && logger "${logMessage}" + [[ "${status}" == "fail" ]] && warn "${warnMessage}" +} +# copy contents from source to destination +copyCmd () { + local source="$1" + local target="$2" + local mode="$3" + local status= + + case $mode in + unique) + cp -up "${source}"/* "${target}"/ && status="success" || status="fail" + logCopyStatus "${status}" "Successfully copied directory contents from [${source}] to [${target}]" "Failed to copy directory contents from [${source}] to [${target}]" + ;; + specific) + cp -pf "${source}" "${target}"/ && status="success" || status="fail" + logCopyStatus "${status}" "Successfully copied file [${source}] to [${target}]" "Failed to copy file [${source}] to [${target}]" + ;; + patternFiles) + cp -pf "${source}"* "${target}"/ && status="success" || status="fail" + logCopyStatus "${status}" "Successfully copied files matching [${source}*] to [${target}]" "Failed to copy files matching [${source}*] to [${target}]" + ;; + full) + cp -prf "${source}"/* "${target}"/ && status="success" || status="fail" + logCopyStatus "${status}" "Successfully copied directory contents from [${source}] to [${target}]" "Failed to copy directory contents from [${source}] to [${target}]" + ;; + esac +} +# Check contents exist in source before copying +copyOnContentExist () { + local source="$1" + local target="$2" + local mode="$3" + + if [[ "$(checkContentExists "${source}")" == "true" ]]; then + copyCmd "${source}" "${target}" "${mode}" + else + logger "No contents to copy from [${source}]" + fi +} + +# move source to destination +moveCmd () { + local source="$1" + local target="$2" + local status= + + mv -f "${source}" "${target}" && status="success" || status="fail" + [[ "${status}" == "success" ]] && logger "Successfully moved directory [${source}] to [${target}]" + [[ "${status}" == "fail" ]] && warn "Failed to move directory [${source}] to [${target}]" +} + +# symlink target to source +symlinkCmd () { + local source="$1" + local target="$2" + local symlinkSubDir="$3" + local check=false + + if [[ "${symlinkSubDir}" == "subDir" ]]; then + ln -sf "${source}"/* "${target}" && check=true || check=false + else + ln -sf "${source}" "${target}" && check=true || check=false + fi + + [[ "${check}" == "true" ]] && logger "Successfully symlinked directory [${target}] to old [${source}]" + [[ "${check}" == "false" ]] && warn "Symlink operation failed" +} +# Check contents exist in source before symlinking +symlinkOnExist () { + local source="$1" + local target="$2" + local symlinkSubDir="$3" + + if [[ "$(checkContentExists "${source}")" == "true" ]]; then + if [[ "${symlinkSubDir}" == "subDir" ]]; then + symlinkCmd "${source}" "${target}" "subDir" + else + symlinkCmd "${source}" "${target}" + fi + else + logger "No contents to symlink from [${source}]" + fi +} + +prependDir () { + local absolutePath="$1" + local fullPath="$2" + local sourcePath= + + if [[ "${absolutePath}" = \/* ]]; then + sourcePath="${absolutePath}" + else + sourcePath="${fullPath}" + fi + echo "${sourcePath}" +} + +getFirstEntry (){ + local entry="$1" + + [[ -z "${entry}" ]] && return + echo "${entry}" | awk -F"=" '{print $1}' +} + +getSecondEntry () { + local entry="$1" + + [[ -z "${entry}" ]] && return + echo "${entry}" | awk -F"=" '{print $2}' +} +# To get absolutePath +pathResolver () { + local directoryPath="$1" + local dataDir= + + if [[ "${INSTALLER}" == "${COMPOSE_TYPE}" || "${INSTALLER}" == "${HELM_TYPE}" ]]; then + retrieveYamlValue "migration.oldDataDir" "oldDataDir" "Warning" + dataDir="${YAML_VALUE}" + cd "${dataDir}" + else + cd "${OLD_DATA_DIR}" + fi + absoluteDir="`cd "${directoryPath}";pwd`" + echo "${absoluteDir}" +} + +checkPathResolver () { + local value="$1" + + if [[ "${value}" == \/* ]]; then + value="${value}" + else + value="$(pathResolver "${value}")" + fi + echo "${value}" +} + +propertyMigrate () { + local entry="$1" + local filePath="$2" + local fileName="$3" + local check=false + + local yamlPath="$(getFirstEntry "${entry}")" + local property="$(getSecondEntry "${entry}")" + if [[ -z "${property}" ]]; then + warn "Property is empty in map [${entry}] in the file [${MIGRATION_SYSTEM_YAML_INFO}]" + return + fi + if [[ -z "${yamlPath}" ]]; then + warn "yamlPath is empty for [${property}] in [${MIGRATION_SYSTEM_YAML_INFO}]" + return + fi + local keyValues=$(cat "${NEW_DATA_DIR}/${filePath}/${fileName}" | grep "^[^#]" | grep "[*=*]") + for i in ${keyValues}; do + key=$(echo "${i}" | awk -F"=" '{print $1}') + value=$(echo "${i}" | cut -f 2- -d '=') + [ -z "${key}" ] && continue + [ -z "${value}" ] && continue + if [[ "${key}" == "${property}" ]]; then + if [[ "${PRODUCT}" == "artifactory" ]]; then + value="$(migrateResolveDerbyPath "${key}" "${value}")" + value="$(migrateResolveHaDirPath "${key}" "${value}")" + if [[ "${INSTALLER}" != "${DOCKER_TYPE}" ]]; then + value="$(updatePostgresUrlString_Hook "${yamlPath}" "${value}")" + fi + fi + if [[ "${key}" == "context.url" ]]; then + local ip=$(echo "${value}" | awk -F/ '{print $3}' | sed 's/:.*//') + setSystemValue "shared.node.ip" "${ip}" "${SYSTEM_YAML_PATH}" + logger "Setting [shared.node.ip] with [${ip}] in system.yaml" + fi + setSystemValue "${yamlPath}" "${value}" "${SYSTEM_YAML_PATH}" && logger "Setting [${yamlPath}] with value of the property [${property}] in system.yaml" && check=true && break || check=false + fi + done + [[ "${check}" == "false" ]] && logger "Property [${property}] not found in file [${fileName}]" +} + +setHaEnabled_hook () { + echo "" +} + +migratePropertiesFiles () { + local fileList= + local filePath= + local fileName= + local map= + + retrieveYamlValue "migration.propertyFiles.files" "fileList" "Skip" + fileList="${YAML_VALUE}" + if [[ -z "${fileList}" ]]; then + return + fi + bannerSection "PROCESSING MIGRATION OF PROPERTY FILES" + for file in ${fileList}; + do + bannerSubSection "Processing Migration of $file" + retrieveYamlValue "migration.propertyFiles.$file.filePath" "filePath" "Warning" + filePath="${YAML_VALUE}" + retrieveYamlValue "migration.propertyFiles.$file.fileName" "fileName" "Warning" + fileName="${YAML_VALUE}" + [[ -z "${filePath}" && -z "${fileName}" ]] && continue + if [[ "$(checkFileExists "${NEW_DATA_DIR}/${filePath}/${fileName}")" == "true" ]]; then + logger "File [${fileName}] found in path [${NEW_DATA_DIR}/${filePath}]" + # setting haEnabled with true only if ha-node.properties is present + setHaEnabled_hook "${filePath}" + retrieveYamlValue "migration.propertyFiles.$file.map" "map" "Warning" + map="${YAML_VALUE}" + [[ -z "${map}" ]] && continue + for entry in $map; + do + if [[ "$(checkMapEntry "${entry}")" == "true" ]]; then + propertyMigrate "${entry}" "${filePath}" "${fileName}" + else + warn "map entry [${entry}] in [${MIGRATION_SYSTEM_YAML_INFO}] is not in correct format, correct format i.e yamlPath=property" + fi + done + else + logger "File [${fileName}] was not found in path [${NEW_DATA_DIR}/${filePath}] to migrate" + fi + done +} + +createTargetDir () { + local mountDir="$1" + local target="$2" + + logger "Target directory not found [${mountDir}/${target}], creating it" + createDirectoryRecursive "${mountDir}" "${target}" "Warning" +} + +createDirectoryRecursive () { + local mountDir="$1" + local target="$2" + local output="$3" + local check=false + local message="Could not create directory ${directory}, please check if the user ${USER} has permissions to perform this action" + removeSoftLink "${mountDir}/${target}" + local directory=$(echo "${target}" | tr '/' ' ' ) + local targetDir="${mountDir}" + for dir in ${directory}; + do + targetDir="${targetDir}/${dir}" + mkdir -p "${targetDir}" && check=true || check=false + setOwnershipBasedOnInstaller "${targetDir}" + done + if [[ "${check}" == "false" ]]; then + if [[ "${output}" == "Warning" ]]; then + warn "${message}" + else + errorExit "${message}" + fi + fi +} + +copyOperation () { + local source="$1" + local target="$2" + local mode="$3" + local check=false + local targetDataDir= + local targetLink= + local date= + + # prepend OLD_DATA_DIR only if source is relative path + source="$(prependDir "${source}" "${OLD_DATA_DIR}/${source}")" + if [[ "${INSTALLER}" == "${HELM_TYPE}" ]]; then + targetDataDir="${NEW_DATA_DIR}" + else + targetDataDir="`cd "${NEW_DATA_DIR}"/../;pwd`" + fi + copyLogMessage "${mode}" + #remove source if it is a symlink + if [[ -L "${source}" ]]; then + targetLink=$(readlink -f "${source}") + logger "Removing the symlink [${source}] pointing to [${targetLink}]" + rm -f "${source}" + source=${targetLink} + fi + if [[ "$(checkDirExists "${source}")" != "true" ]]; then + logger "Source [${source}] directory not found in path" + return + fi + if [[ "$(checkDirContents "${source}")" != "true" ]]; then + logger "No contents to copy from [${source}]" + return + fi + if [[ "$(checkDirExists "${targetDataDir}/${target}")" != "true" ]]; then + createTargetDir "${targetDataDir}" "${target}" + fi + copyOnContentExist "${source}" "${targetDataDir}/${target}" "${mode}" +} + +copySpecificFiles () { + local source="$1" + local target="$2" + local mode="$3" + + # prepend OLD_DATA_DIR only if source is relative path + source="$(prependDir "${source}" "${OLD_DATA_DIR}/${source}")" + if [[ "${INSTALLER}" == "${HELM_TYPE}" ]]; then + targetDataDir="${NEW_DATA_DIR}" + else + targetDataDir="`cd "${NEW_DATA_DIR}"/../;pwd`" + fi + copyLogMessage "${mode}" + if [[ "$(checkFileExists "${source}")" != "true" ]]; then + logger "Source file [${source}] does not exist in path" + return + fi + if [[ "$(checkDirExists "${targetDataDir}/${target}")" != "true" ]]; then + createTargetDir "${targetDataDir}" "${target}" + fi + copyCmd "${source}" "${targetDataDir}/${target}" "${mode}" +} + +copyPatternMatchingFiles () { + local source="$1" + local target="$2" + local mode="$3" + local sourcePath="${4}" + + # prepend OLD_DATA_DIR only if source is relative path + sourcePath="$(prependDir "${sourcePath}" "${OLD_DATA_DIR}/${sourcePath}")" + if [[ "${INSTALLER}" == "${HELM_TYPE}" ]]; then + targetDataDir="${NEW_DATA_DIR}" + else + targetDataDir="`cd "${NEW_DATA_DIR}"/../;pwd`" + fi + copyLogMessage "${mode}" + if [[ "$(checkDirExists "${sourcePath}")" != "true" ]]; then + logger "Source [${sourcePath}] directory not found in path" + return + fi + if ls "${sourcePath}/${source}"* 1> /dev/null 2>&1; then + if [[ "$(checkDirExists "${targetDataDir}/${target}")" != "true" ]]; then + createTargetDir "${targetDataDir}" "${target}" + fi + copyCmd "${sourcePath}/${source}" "${targetDataDir}/${target}" "${mode}" + else + logger "Source file [${sourcePath}/${source}*] does not exist in path" + fi +} + +copyLogMessage () { + local mode="$1" + case $mode in + specific) + logger "Copy file [${source}] to target [${targetDataDir}/${target}]" + ;; + patternFiles) + logger "Copy files matching [${sourcePath}/${source}*] to target [${targetDataDir}/${target}]" + ;; + full) + logger "Copy directory contents from source [${source}] to target [${targetDataDir}/${target}]" + ;; + unique) + logger "Copy directory contents from source [${source}] to target [${targetDataDir}/${target}]" + ;; + esac +} + +copyBannerMessages () { + local mode="$1" + local textMode="$2" + case $mode in + specific) + bannerSection "COPY ${textMode} FILES" + ;; + patternFiles) + bannerSection "COPY MATCHING ${textMode}" + ;; + full) + bannerSection "COPY ${textMode} DIRECTORIES CONTENTS" + ;; + unique) + bannerSection "COPY ${textMode} DIRECTORIES CONTENTS" + ;; + esac +} + +invokeCopyFunctions () { + local mode="$1" + local source="$2" + local target="$3" + + case $mode in + specific) + copySpecificFiles "${source}" "${target}" "${mode}" + ;; + patternFiles) + retrieveYamlValue "migration.${copyFormat}.sourcePath" "map" "Warning" + local sourcePath="${YAML_VALUE}" + copyPatternMatchingFiles "${source}" "${target}" "${mode}" "${sourcePath}" + ;; + full) + copyOperation "${source}" "${target}" "${mode}" + ;; + unique) + copyOperation "${source}" "${target}" "${mode}" + ;; + esac +} +# Copies contents from source directory and target directory +copyDataDirectories () { + local copyFormat="$1" + local mode="$2" + local map= + local source= + local target= + local textMode= + local targetDataDir= + local copyFormatValue= + + retrieveYamlValue "migration.${copyFormat}" "${copyFormat}" "Skip" + copyFormatValue="${YAML_VALUE}" + if [[ -z "${copyFormatValue}" ]]; then + return + fi + textMode=$(echo "${mode}" | tr '[:lower:]' '[:upper:]' 2>/dev/null) + copyBannerMessages "${mode}" "${textMode}" + retrieveYamlValue "migration.${copyFormat}.map" "map" "Warning" + map="${YAML_VALUE}" + if [[ "${INSTALLER}" == "${HELM_TYPE}" ]]; then + targetDataDir="${NEW_DATA_DIR}" + else + targetDataDir="`cd "${NEW_DATA_DIR}"/../;pwd`" + fi + for entry in $map; + do + if [[ "$(checkMapEntry "${entry}")" == "true" ]]; then + source="$(getSecondEntry "${entry}")" + target="$(getFirstEntry "${entry}")" + [[ -z "${source}" ]] && warn "source value is empty for [${entry}] in [${MIGRATION_SYSTEM_YAML_INFO}]" && continue + [[ -z "${target}" ]] && warn "target value is empty for [${entry}] in [${MIGRATION_SYSTEM_YAML_INFO}]" && continue + invokeCopyFunctions "${mode}" "${source}" "${target}" + else + warn "map entry [${entry}] in [${MIGRATION_SYSTEM_YAML_INFO}] is not in correct format, correct format i.e target=source" + fi + echo ""; + done +} + +invokeMoveFunctions () { + local source="$1" + local target="$2" + local sourceDataDir= + local targetBasename= + # prepend OLD_DATA_DIR only if source is relative path + sourceDataDir=$(prependDir "${source}" "${OLD_DATA_DIR}/${source}") + targetBasename=$(dirname "${target}") + logger "Moving directory source [${sourceDataDir}] to target [${NEW_DATA_DIR}/${target}]" + if [[ "$(checkDirExists "${sourceDataDir}")" != "true" ]]; then + logger "Directory [${sourceDataDir}] not found in path to move" + return + fi + if [[ "$(checkDirExists "${NEW_DATA_DIR}/${targetBasename}")" != "true" ]]; then + createTargetDir "${NEW_DATA_DIR}" "${targetBasename}" + moveCmd "${sourceDataDir}" "${NEW_DATA_DIR}/${target}" + else + moveCmd "${sourceDataDir}" "${NEW_DATA_DIR}/tempDir" + moveCmd "${NEW_DATA_DIR}/tempDir" "${NEW_DATA_DIR}/${target}" + fi +} + +# Move source directory and target directory +moveDirectories () { + local moveDataDirectories= + local map= + local source= + local target= + + retrieveYamlValue "migration.moveDirectories" "moveDirectories" "Skip" + moveDirectories="${YAML_VALUE}" + if [[ -z "${moveDirectories}" ]]; then + return + fi + bannerSection "MOVE DIRECTORIES" + retrieveYamlValue "migration.moveDirectories.map" "map" "Warning" + map="${YAML_VALUE}" + for entry in $map; + do + if [[ "$(checkMapEntry "${entry}")" == "true" ]]; then + source="$(getSecondEntry "${entry}")" + target="$(getFirstEntry "${entry}")" + [[ -z "${source}" ]] && warn "source value is empty for [${entry}] in [${MIGRATION_SYSTEM_YAML_INFO}]" && continue + [[ -z "${target}" ]] && warn "target value is empty for [${entry}] in [${MIGRATION_SYSTEM_YAML_INFO}]" && continue + invokeMoveFunctions "${source}" "${target}" + else + warn "map entry [${entry}] in [${MIGRATION_SYSTEM_YAML_INFO}] is not in correct format, correct format i.e target=source" + fi + echo ""; + done +} + +# Trim masterKey if its generated using hex 32 +trimMasterKey () { + local masterKeyDir=/opt/jfrog/artifactory/var/etc/security + local oldMasterKey=$(<${masterKeyDir}/master.key) + local oldMasterKey_Length=$(echo ${#oldMasterKey}) + local newMasterKey= + if [[ ${oldMasterKey_Length} -gt 32 ]]; then + bannerSection "TRIM MASTERKEY" + newMasterKey=$(echo ${oldMasterKey:0:32}) + cp ${masterKeyDir}/master.key ${masterKeyDir}/backup_master.key + logger "Original masterKey is backed up : ${masterKeyDir}/backup_master.key" + rm -rf ${masterKeyDir}/master.key + echo ${newMasterKey} > ${masterKeyDir}/master.key + logger "masterKey is trimmed : ${masterKeyDir}/master.key" + fi +} + +copyDirectories () { + + copyDataDirectories "copyFiles" "full" + copyDataDirectories "copyUniqueFiles" "unique" + copyDataDirectories "copySpecificFiles" "specific" + copyDataDirectories "copyPatternMatchingFiles" "patternFiles" +} + +symlinkDir () { + local source="$1" + local target="$2" + local targetDir= + local basename= + local targetParentDir= + + targetDir="$(dirname "${target}")" + if [[ "${targetDir}" == "${source}" ]]; then + # symlink the sub directories + createDirectory "${NEW_DATA_DIR}/${target}" "Warning" + if [[ "$(checkDirExists "${NEW_DATA_DIR}/${target}")" == "true" ]]; then + symlinkOnExist "${OLD_DATA_DIR}/${source}" "${NEW_DATA_DIR}/${target}" "subDir" + basename="$(basename "${target}")" + cd "${NEW_DATA_DIR}/${target}" && rm -f "${basename}" + fi + else + targetParentDir="$(dirname "${NEW_DATA_DIR}/${target}")" + createDirectory "${targetParentDir}" "Warning" + if [[ "$(checkDirExists "${targetParentDir}")" == "true" ]]; then + symlinkOnExist "${OLD_DATA_DIR}/${source}" "${NEW_DATA_DIR}/${target}" + fi + fi +} + +symlinkOperation () { + local source="$1" + local target="$2" + local check=false + local targetLink= + local date= + + # Check if source is a link and do symlink + if [[ -L "${OLD_DATA_DIR}/${source}" ]]; then + targetLink=$(readlink -f "${OLD_DATA_DIR}/${source}") + symlinkOnExist "${targetLink}" "${NEW_DATA_DIR}/${target}" + else + # check if source is directory and do symlink + if [[ "$(checkDirExists "${OLD_DATA_DIR}/${source}")" != "true" ]]; then + logger "Source [${source}] directory not found in path to symlink" + return + fi + if [[ "$(checkDirContents "${OLD_DATA_DIR}/${source}")" != "true" ]]; then + logger "No contents found in [${OLD_DATA_DIR}/${source}] to symlink" + return + fi + if [[ "$(checkDirExists "${NEW_DATA_DIR}/${target}")" != "true" ]]; then + logger "Target directory [${NEW_DATA_DIR}/${target}] does not exist to create symlink, creating it" + symlinkDir "${source}" "${target}" + else + rm -rf "${NEW_DATA_DIR}/${target}" && check=true || check=false + [[ "${check}" == "false" ]] && warn "Failed to remove contents in [${NEW_DATA_DIR}/${target}/]" + symlinkDir "${source}" "${target}" + fi + fi +} +# Creates a symlink path - Source directory to which the symbolic link should point. +symlinkDirectories () { + local linkFiles= + local map= + local source= + local target= + + retrieveYamlValue "migration.linkFiles" "linkFiles" "Skip" + linkFiles="${YAML_VALUE}" + if [[ -z "${linkFiles}" ]]; then + return + fi + bannerSection "SYMLINK DIRECTORIES" + retrieveYamlValue "migration.linkFiles.map" "map" "Warning" + map="${YAML_VALUE}" + for entry in $map; + do + if [[ "$(checkMapEntry "${entry}")" == "true" ]]; then + source="$(getSecondEntry "${entry}")" + target="$(getFirstEntry "${entry}")" + logger "Symlink directory [${NEW_DATA_DIR}/${target}] to old [${OLD_DATA_DIR}/${source}]" + [[ -z "${source}" ]] && warn "source value is empty for [${entry}] in [${MIGRATION_SYSTEM_YAML_INFO}]" && continue + [[ -z "${target}" ]] && warn "target value is empty for [${entry}] in [${MIGRATION_SYSTEM_YAML_INFO}]" && continue + symlinkOperation "${source}" "${target}" + else + warn "map entry [${entry}] in [${MIGRATION_SYSTEM_YAML_INFO}] is not in correct format, correct format i.e target=source" + fi + echo ""; + done +} + +updateConnectionString () { + local yamlPath="$1" + local value="$2" + local mongoPath="shared.mongo.url" + local rabbitmqPath="shared.rabbitMq.url" + local postgresPath="shared.database.url" + local redisPath="shared.redis.connectionString" + local mongoConnectionString="mongo.connectionString" + local sourceKey= + local hostIp=$(io_getPublicHostIP) + local hostKey= + + if [[ "${INSTALLER}" == "${COMPOSE_TYPE}" || "${INSTALLER}" == "${HELM_TYPE}" ]]; then + # Replace @postgres:,@mongodb:,@rabbitmq:,@redis: to @{hostIp}: (Compose Installer) + hostKey="@${hostIp}:" + case $yamlPath in + ${postgresPath}) + sourceKey="@postgres:" + value=$(io_replaceString "${value}" "${sourceKey}" "${hostKey}") + ;; + ${mongoPath}) + sourceKey="@mongodb:" + value=$(io_replaceString "${value}" "${sourceKey}" "${hostKey}") + ;; + ${rabbitmqPath}) + sourceKey="@rabbitmq:" + value=$(io_replaceString "${value}" "${sourceKey}" "${hostKey}") + ;; + ${redisPath}) + sourceKey="@redis:" + value=$(io_replaceString "${value}" "${sourceKey}" "${hostKey}") + ;; + ${mongoConnectionString}) + sourceKey="@mongodb:" + value=$(io_replaceString "${value}" "${sourceKey}" "${hostKey}") + ;; + esac + fi + echo -n "${value}" +} + +yamlMigrate () { + local entry="$1" + local sourceFile="$2" + local value= + local yamlPath= + local key= + yamlPath="$(getFirstEntry "${entry}")" + key="$(getSecondEntry "${entry}")" + if [[ -z "${key}" ]]; then + warn "key is empty in map [${entry}] in the file [${MIGRATION_SYSTEM_YAML_INFO}]" + return + fi + if [[ -z "${yamlPath}" ]]; then + warn "yamlPath is empty for [${key}] in [${MIGRATION_SYSTEM_YAML_INFO}]" + return + fi + getYamlValue "${key}" "${sourceFile}" "false" + value="${YAML_VALUE}" + if [[ ! -z "${value}" ]]; then + value=$(updateConnectionString "${yamlPath}" "${value}") + fi + if [[ -z "${value}" ]]; then + logger "No value for [${key}] in [${sourceFile}]" + else + setSystemValue "${yamlPath}" "${value}" "${SYSTEM_YAML_PATH}" + logger "Setting [${yamlPath}] with value of the key [${key}] in system.yaml" + fi +} + +migrateYamlFile () { + local files= + local filePath= + local fileName= + local sourceFile= + local map= + retrieveYamlValue "migration.yaml.files" "files" "Skip" + files="${YAML_VALUE}" + if [[ -z "${files}" ]]; then + return + fi + bannerSection "MIGRATION OF YAML FILES" + for file in $files; + do + bannerSubSection "Processing Migration of $file" + retrieveYamlValue "migration.yaml.$file.filePath" "filePath" "Warning" + filePath="${YAML_VALUE}" + retrieveYamlValue "migration.yaml.$file.fileName" "fileName" "Warning" + fileName="${YAML_VALUE}" + [[ -z "${filePath}" && -z "${fileName}" ]] && continue + sourceFile="${NEW_DATA_DIR}/${filePath}/${fileName}" + if [[ "$(checkFileExists "${sourceFile}")" == "true" ]]; then + logger "File [${fileName}] found in path [${NEW_DATA_DIR}/${filePath}]" + retrieveYamlValue "migration.yaml.$file.map" "map" "Warning" + map="${YAML_VALUE}" + [[ -z "${map}" ]] && continue + for entry in $map; + do + if [[ "$(checkMapEntry "${entry}")" == "true" ]]; then + yamlMigrate "${entry}" "${sourceFile}" + else + warn "map entry [${entry}] in [${MIGRATION_SYSTEM_YAML_INFO}] is not in correct format, correct format i.e yamlPath=key" + fi + done + else + logger "File [${fileName}] is not found in path [${NEW_DATA_DIR}/${filePath}] to migrate" + fi + done +} +# updates the key and value in system.yaml +updateYamlKeyValue () { + local entry="$1" + local value= + local yamlPath= + local key= + + yamlPath="$(getFirstEntry "${entry}")" + value="$(getSecondEntry "${entry}")" + if [[ -z "${value}" ]]; then + warn "value is empty in map [${entry}] in the file [${MIGRATION_SYSTEM_YAML_INFO}]" + return + fi + if [[ -z "${yamlPath}" ]]; then + warn "yamlPath is empty for [${key}] in [${MIGRATION_SYSTEM_YAML_INFO}]" + return + fi + setSystemValue "${yamlPath}" "${value}" "${SYSTEM_YAML_PATH}" + logger "Setting [${yamlPath}] with value [${value}] in system.yaml" +} + +updateSystemYamlFile () { + local updateYaml= + local map= + + retrieveYamlValue "migration.updateSystemYaml" "updateYaml" "Skip" + updateSystemYaml="${YAML_VALUE}" + if [[ -z "${updateSystemYaml}" ]]; then + return + fi + bannerSection "UPDATE SYSTEM YAML FILE WITH KEY AND VALUES" + retrieveYamlValue "migration.updateSystemYaml.map" "map" "Warning" + map="${YAML_VALUE}" + if [[ -z "${map}" ]]; then + return + fi + for entry in $map; + do + if [[ "$(checkMapEntry "${entry}")" == "true" ]]; then + updateYamlKeyValue "${entry}" + else + warn "map entry [${entry}] in [${MIGRATION_SYSTEM_YAML_INFO}] is not in correct format, correct format i.e yamlPath=key" + fi + done +} + +backupFiles_hook () { + logSilly "Method ${FUNCNAME[0]}" +} + +backupDirectory () { + local backupDir="$1" + local dir="$2" + local targetDir="$3" + local effectiveUser= + local effectiveGroup= + + if [[ "${dir}" = \/* ]]; then + dir=$(echo "${dir/\//}") + fi + + if [[ "${INSTALLER}" == "${COMPOSE_TYPE}" || "${INSTALLER}" == "${HELM_TYPE}" ]]; then + effectiveUser="${JF_USER}" + effectiveGroup="${JF_USER}" + elif [[ "${INSTALLER}" == "${DEB_TYPE}" || "${INSTALLER}" == "${RPM_TYPE}" ]]; then + effectiveUser="${USER_TO_CHECK}" + effectiveGroup="${GROUP_TO_CHECK}" + fi + + removeSoftLinkAndCreateDir "${backupDir}" "${effectiveUser}" "${effectiveGroup}" "yes" + local backupDirectory="${backupDir}/${PRODUCT}" + removeSoftLinkAndCreateDir "${backupDirectory}" "${effectiveUser}" "${effectiveGroup}" "yes" + removeSoftLinkAndCreateDir "${backupDirectory}/${dir}" "${effectiveUser}" "${effectiveGroup}" "yes" + local outputCheckDirExists="$(checkDirExists "${backupDirectory}/${dir}")" + if [[ "${outputCheckDirExists}" == "true" ]]; then + copyOnContentExist "${targetDir}" "${backupDirectory}/${dir}" "full" + fi +} + +removeOldDirectory () { + local backupDir="$1" + local entry="$2" + local check=false + + # prepend OLD_DATA_DIR only if entry is relative path + local targetDir="$(prependDir "${entry}" "${OLD_DATA_DIR}/${entry}")" + local outputCheckDirExists="$(checkDirExists "${targetDir}")" + if [[ "${outputCheckDirExists}" != "true" ]]; then + logger "No [${targetDir}] directory found to delete" + echo ""; + return + fi + backupDirectory "${backupDir}" "${entry}" "${targetDir}" + rm -rf "${targetDir}" && check=true || check=false + [[ "${check}" == "true" ]] && logger "Successfully removed directory [${targetDir}]" + [[ "${check}" == "false" ]] && warn "Failed to remove directory [${targetDir}]" + echo ""; +} + +cleanUpOldDataDirectories () { + local cleanUpOldDataDir= + local map= + local entry= + + retrieveYamlValue "migration.cleanUpOldDataDir" "cleanUpOldDataDir" "Skip" + cleanUpOldDataDir="${YAML_VALUE}" + if [[ -z "${cleanUpOldDataDir}" ]]; then + return + fi + bannerSection "CLEAN UP OLD DATA DIRECTORIES" + retrieveYamlValue "migration.cleanUpOldDataDir.map" "map" "Warning" + map="${YAML_VALUE}" + [[ -z "${map}" ]] && continue + date="$(date +%Y%m%d%H%M)" + backupDir="${NEW_DATA_DIR}/backup/backup-${date}" + bannerImportant "****** Old data configurations are backedup in [${backupDir}] directory ******" + backupFiles_hook "${backupDir}/${PRODUCT}" + for entry in $map; + do + removeOldDirectory "${backupDir}" "${entry}" + done +} + +backupFiles () { + local backupDir="$1" + local dir="$2" + local targetDir="$3" + local fileName="$4" + local effectiveUser= + local effectiveGroup= + + if [[ "${dir}" = \/* ]]; then + dir=$(echo "${dir/\//}") + fi + + if [[ "${INSTALLER}" == "${COMPOSE_TYPE}" || "${INSTALLER}" == "${HELM_TYPE}" ]]; then + effectiveUser="${JF_USER}" + effectiveGroup="${JF_USER}" + elif [[ "${INSTALLER}" == "${DEB_TYPE}" || "${INSTALLER}" == "${RPM_TYPE}" ]]; then + effectiveUser="${USER_TO_CHECK}" + effectiveGroup="${GROUP_TO_CHECK}" + fi + + removeSoftLinkAndCreateDir "${backupDir}" "${effectiveUser}" "${effectiveGroup}" "yes" + local backupDirectory="${backupDir}/${PRODUCT}" + removeSoftLinkAndCreateDir "${backupDirectory}" "${effectiveUser}" "${effectiveGroup}" "yes" + removeSoftLinkAndCreateDir "${backupDirectory}/${dir}" "${effectiveUser}" "${effectiveGroup}" "yes" + local outputCheckDirExists="$(checkDirExists "${backupDirectory}/${dir}")" + if [[ "${outputCheckDirExists}" == "true" ]]; then + copyCmd "${targetDir}/${fileName}" "${backupDirectory}/${dir}" "specific" + fi +} + +removeOldFiles () { + local backupDir="$1" + local directoryName="$2" + local fileName="$3" + local check=false + + # prepend OLD_DATA_DIR only if entry is relative path + local targetDir="$(prependDir "${directoryName}" "${OLD_DATA_DIR}/${directoryName}")" + local outputCheckFileExists="$(checkFileExists "${targetDir}/${fileName}")" + if [[ "${outputCheckFileExists}" != "true" ]]; then + logger "No [${targetDir}/${fileName}] file found to delete" + return + fi + backupFiles "${backupDir}" "${directoryName}" "${targetDir}" "${fileName}" + rm -f "${targetDir}/${fileName}" && check=true || check=false + [[ "${check}" == "true" ]] && logger "Successfully removed file [${targetDir}/${fileName}]" + [[ "${check}" == "false" ]] && warn "Failed to remove file [${targetDir}/${fileName}]" + echo ""; +} + +cleanUpOldFiles () { + local cleanUpFiles= + local map= + local entry= + + retrieveYamlValue "migration.cleanUpOldFiles" "cleanUpOldFiles" "Skip" + cleanUpOldFiles="${YAML_VALUE}" + if [[ -z "${cleanUpOldFiles}" ]]; then + return + fi + bannerSection "CLEAN UP OLD FILES" + retrieveYamlValue "migration.cleanUpOldFiles.map" "map" "Warning" + map="${YAML_VALUE}" + [[ -z "${map}" ]] && continue + date="$(date +%Y%m%d%H%M)" + backupDir="${NEW_DATA_DIR}/backup/backup-${date}" + bannerImportant "****** Old files are backedup in [${backupDir}] directory ******" + for entry in $map; + do + local outputCheckMapEntry="$(checkMapEntry "${entry}")" + if [[ "${outputCheckMapEntry}" != "true" ]]; then + warn "map entry [${entry}] in [${MIGRATION_SYSTEM_YAML_INFO}] is not in correct format, correct format i.e directoryName=fileName" + fi + local fileName="$(getSecondEntry "${entry}")" + local directoryName="$(getFirstEntry "${entry}")" + [[ -z "${fileName}" ]] && warn "File name value is empty for [${entry}] in [${MIGRATION_SYSTEM_YAML_INFO}]" && continue + [[ -z "${directoryName}" ]] && warn "Directory name value is empty for [${entry}] in [${MIGRATION_SYSTEM_YAML_INFO}]" && continue + removeOldFiles "${backupDir}" "${directoryName}" "${fileName}" + echo ""; + done +} + +startMigration () { + bannerSection "STARTING MIGRATION" +} + +endMigration () { + bannerSection "MIGRATION COMPLETED SUCCESSFULLY" +} + +initialize () { + setAppDir + _pauseExecution "setAppDir" + initHelpers + _pauseExecution "initHelpers" + checkMigrationInfoYaml + _pauseExecution "checkMigrationInfoYaml" + getProduct + _pauseExecution "getProduct" + getDataDir + _pauseExecution "getDataDir" +} + +main () { + case $PRODUCT in + artifactory) + migrateArtifactory + ;; + distribution) + migrateDistribution + ;; + xray) + migrationXray + ;; + esac + exit 0 +} + +# Ensures meta data is logged +LOG_BEHAVIOR_ADD_META="$FLAG_Y" + + +migrateResolveDerbyPath () { + local key="$1" + local value="$2" + + if [[ "${key}" == "url" && "${value}" == *"db.home"* ]]; then + if [[ "${INSTALLER}" == "${COMPOSE_TYPE}" ]]; then + derbyPath="/opt/jfrog/artifactory/var/data/artifactory/derby" + value=$(echo "${value}" | sed "s|{db.home}|$derbyPath|") + else + derbyPath="${NEW_DATA_DIR}/data/artifactory/derby" + value=$(echo "${value}" | sed "s|{db.home}|$derbyPath|") + fi + fi + echo "${value}" +} + +migrateResolveHaDirPath () { + local key="$1" + local value="$2" + + if [[ "${INSTALLER}" == "${RPM_TYPE}" || "${INSTALLER}" == "${COMPOSE_TYPE}" || "${INSTALLER}" == "${HELM_TYPE}" || "${INSTALLER}" == "${DEB_TYPE}" ]]; then + if [[ "${key}" == "artifactory.ha.data.dir" || "${key}" == "artifactory.ha.backup.dir" ]]; then + value=$(checkPathResolver "${value}") + fi + fi + echo "${value}" +} +updatePostgresUrlString_Hook () { + local yamlPath="$1" + local value="$2" + local hostIp=$(io_getPublicHostIP) + local sourceKey="//postgresql:" + if [[ "${yamlPath}" == "shared.database.url" ]]; then + value=$(io_replaceString "${value}" "${sourceKey}" "//${hostIp}:" "#") + fi + echo "${value}" +} +# Check Artifactory product version +checkArtifactoryVersion () { + local minProductVersion="6.0.0" + local maxProductVersion="7.0.0" + local propertyInDocker="ARTIFACTORY_VERSION" + local property="artifactory.version" + + if [[ "${INSTALLER}" == "${COMPOSE_TYPE}" ]]; then + local newfilePath="${APP_DIR}/../.env" + local oldfilePath="${OLD_DATA_DIR}/etc/artifactory.properties" + elif [[ "${INSTALLER}" == "${HELM_TYPE}" ]]; then + local oldfilePath="${OLD_DATA_DIR}/etc/artifactory.properties" + elif [[ "${INSTALLER}" == "${ZIP_TYPE}" ]]; then + local newfilePath="${NEW_DATA_DIR}/etc/artifactory/artifactory.properties" + local oldfilePath="${OLD_DATA_DIR}/etc/artifactory.properties" + else + local newfilePath="${NEW_DATA_DIR}/etc/artifactory/artifactory.properties" + local oldfilePath="/etc/opt/jfrog/artifactory/artifactory.properties" + fi + + getProductVersion "${minProductVersion}" "${maxProductVersion}" "${newfilePath}" "${oldfilePath}" "${propertyInDocker}" "${property}" +} + +getCustomDataDir_hook () { + retrieveYamlValue "migration.oldDataDir" "oldDataDir" "Fail" + OLD_DATA_DIR="${YAML_VALUE}" +} + +# Get protocol value of connector +getXmlConnectorProtocol () { + local i="$1" + local filePath="$2" + local fileName="$3" + local protocolValue=$($LIBXML2_PATH --xpath '//Server/Service/Connector['$i']/@protocol' ${filePath}/${fileName} 2>/dev/null |awk -F"=" '{print $2}' | tr -d '"') + echo -e "${protocolValue}" +} + +# Get all attributes of connector +getXmlConnectorAttributes () { + local i="$1" + local filePath="$2" + local fileName="$3" + local connectorAttributes=$($LIBXML2_PATH --xpath '//Server/Service/Connector['$i']/@*' ${filePath}/${fileName} 2>/dev/null) + # strip leading and trailing spaces + connectorAttributes=$(io_trim "${connectorAttributes}") + echo "${connectorAttributes}" +} + +# Get port value of connector +getXmlConnectorPort () { + local i="$1" + local filePath="$2" + local fileName="$3" + local portValue=$($LIBXML2_PATH --xpath '//Server/Service/Connector['$i']/@port' ${filePath}/${fileName} 2>/dev/null | awk -F"=" '{print $2}' | tr -d '"') + echo -e "${portValue}" +} + +# Get maxThreads value of connector +getXmlConnectorMaxThreads () { + local i="$1" + local filePath="$2" + local fileName="$3" + local maxThreadValue=$($LIBXML2_PATH --xpath '//Server/Service/Connector['$i']/@maxThreads' ${filePath}/${fileName} 2>/dev/null | awk -F"=" '{print $2}' | tr -d '"') + echo -e "${maxThreadValue}" +} +# Get sendReasonPhrase value of connector +getXmlConnectorSendReasonPhrase () { + local i="$1" + local filePath="$2" + local fileName="$3" + local sendReasonPhraseValue=$($LIBXML2_PATH --xpath '//Server/Service/Connector['$i']/@sendReasonPhrase' ${filePath}/${fileName} 2>/dev/null | awk -F"=" '{print $2}' | tr -d '"') + echo -e "${sendReasonPhraseValue}" +} +# Get relaxedPathChars value of connector +getXmlConnectorRelaxedPathChars () { + local i="$1" + local filePath="$2" + local fileName="$3" + local relaxedPathCharsValue=$($LIBXML2_PATH --xpath '//Server/Service/Connector['$i']/@relaxedPathChars' ${filePath}/${fileName} 2>/dev/null | awk -F"=" '{print $2}' | tr -d '"') + # strip leading and trailing spaces + relaxedPathCharsValue=$(io_trim "${relaxedPathCharsValue}") + echo -e "${relaxedPathCharsValue}" +} +# Get relaxedQueryChars value of connector +getXmlConnectorRelaxedQueryChars () { + local i="$1" + local filePath="$2" + local fileName="$3" + local relaxedQueryCharsValue=$($LIBXML2_PATH --xpath '//Server/Service/Connector['$i']/@relaxedQueryChars' ${filePath}/${fileName} 2>/dev/null | awk -F"=" '{print $2}' | tr -d '"') + # strip leading and trailing spaces + relaxedQueryCharsValue=$(io_trim "${relaxedQueryCharsValue}") + echo -e "${relaxedQueryCharsValue}" +} + +# Updating system.yaml with Connector port +setConnectorPort () { + local yamlPath="$1" + local valuePort="$2" + local portYamlPath= + if [[ -z "${yamlPath}" ]]; then + return + fi + if [[ -z "${valuePort}" ]]; then + warn "port value is empty, could not migrate to system.yaml" + return + fi + ## Getting port yaml path from migration info yaml + retrieveYamlValue "${yamlPath}" portYamlPath "Warning" + portYamlPath="${YAML_VALUE}" + if [[ -z "${portYamlPath}" ]]; then + return + fi + setSystemValue "${portYamlPath}" "${valuePort}" "${SYSTEM_YAML_PATH}" + logger "Setting [${portYamlPath}] with value [${valuePort}] in system.yaml" +} + +# Updating system.yaml with Connector maxThreads +setConnectorMaxThread () { + local yamlPath="$1" + local threadValue="$2" + local maxThreadYamlPath= + if [[ -z "${yamlPath}" ]]; then + return + fi + if [[ -z "${threadValue}" ]]; then + return + fi + ## Getting max Threads yaml path from migration info yaml + retrieveYamlValue "${yamlPath}" maxThreadYamlPath "Warning" + maxThreadYamlPath="${YAML_VALUE}" + if [[ -z "${maxThreadYamlPath}" ]]; then + return + fi + setSystemValue "${maxThreadYamlPath}" "${threadValue}" "${SYSTEM_YAML_PATH}" + logger "Setting [${maxThreadYamlPath}] with value [${threadValue}] in system.yaml" +} + +# Updating system.yaml with Connector sendReasonPhrase +setConnectorSendReasonPhrase () { + local yamlPath="$1" + local sendReasonPhraseValue="$2" + local sendReasonPhraseYamlPath= + if [[ -z "${yamlPath}" ]]; then + return + fi + if [[ -z "${sendReasonPhraseValue}" ]]; then + return + fi + ## Getting sendReasonPhrase yaml path from migration info yaml + retrieveYamlValue "${yamlPath}" sendReasonPhraseYamlPath "Warning" + sendReasonPhraseYamlPath="${YAML_VALUE}" + if [[ -z "${sendReasonPhraseYamlPath}" ]]; then + return + fi + setSystemValue "${sendReasonPhraseYamlPath}" "${sendReasonPhraseValue}" "${SYSTEM_YAML_PATH}" + logger "Setting [${sendReasonPhraseYamlPath}] with value [${sendReasonPhraseValue}] in system.yaml" +} + +# Updating system.yaml with Connector relaxedPathChars +setConnectorRelaxedPathChars () { + local yamlPath="$1" + local relaxedPathCharsValue="$2" + local relaxedPathCharsYamlPath= + if [[ -z "${yamlPath}" ]]; then + return + fi + if [[ -z "${relaxedPathCharsValue}" ]]; then + return + fi + ## Getting relaxedPathChars yaml path from migration info yaml + retrieveYamlValue "${yamlPath}" relaxedPathCharsYamlPath "Warning" + relaxedPathCharsYamlPath="${YAML_VALUE}" + if [[ -z "${relaxedPathCharsYamlPath}" ]]; then + return + fi + setSystemValue "${relaxedPathCharsYamlPath}" "${relaxedPathCharsValue}" "${SYSTEM_YAML_PATH}" + logger "Setting [${relaxedPathCharsYamlPath}] with value [${relaxedPathCharsValue}] in system.yaml" +} + +# Updating system.yaml with Connector relaxedQueryChars +setConnectorRelaxedQueryChars () { + local yamlPath="$1" + local relaxedQueryCharsValue="$2" + local relaxedQueryCharsYamlPath= + if [[ -z "${yamlPath}" ]]; then + return + fi + if [[ -z "${relaxedQueryCharsValue}" ]]; then + return + fi + ## Getting relaxedQueryChars yaml path from migration info yaml + retrieveYamlValue "${yamlPath}" relaxedQueryCharsYamlPath "Warning" + relaxedQueryCharsYamlPath="${YAML_VALUE}" + if [[ -z "${relaxedQueryCharsYamlPath}" ]]; then + return + fi + setSystemValue "${relaxedQueryCharsYamlPath}" "${relaxedQueryCharsValue}" "${SYSTEM_YAML_PATH}" + logger "Setting [${relaxedQueryCharsYamlPath}] with value [${relaxedQueryCharsValue}] in system.yaml" +} + +# Updating system.yaml with Connectors configurations +setConnectorExtraConfig () { + local yamlPath="$1" + local connectorAttributes="$2" + local extraConfigPath= + if [[ -z "${yamlPath}" ]]; then + return + fi + if [[ -z "${connectorAttributes}" ]]; then + return + fi + ## Getting extraConfig yaml path from migration info yaml + retrieveYamlValue "${yamlPath}" extraConfig "Warning" + extraConfigPath="${YAML_VALUE}" + if [[ -z "${extraConfigPath}" ]]; then + return + fi + # strip leading and trailing spaces + connectorAttributes=$(io_trim "${connectorAttributes}") + setSystemValue "${extraConfigPath}" "${connectorAttributes}" "${SYSTEM_YAML_PATH}" + logger "Setting [${extraConfigPath}] with connector attributes in system.yaml" +} + +# Updating system.yaml with extra Connectors +setExtraConnector () { + local yamlPath="$1" + local extraConnector="$2" + local extraConnectorYamlPath= + if [[ -z "${yamlPath}" ]]; then + return + fi + if [[ -z "${extraConnector}" ]]; then + return + fi + ## Getting extraConnecotr yaml path from migration info yaml + retrieveYamlValue "${yamlPath}" extraConnectorYamlPath "Warning" + extraConnectorYamlPath="${YAML_VALUE}" + if [[ -z "${extraConnectorYamlPath}" ]]; then + return + fi + getYamlValue "${extraConnectorYamlPath}" "${SYSTEM_YAML_PATH}" "false" + local connectorExtra="${YAML_VALUE}" + if [[ -z "${connectorExtra}" ]]; then + setSystemValue "${extraConnectorYamlPath}" "${extraConnector}" "${SYSTEM_YAML_PATH}" + logger "Setting [${extraConnectorYamlPath}] with extra connectors in system.yaml" + else + setSystemValue "${extraConnectorYamlPath}" "\"${connectorExtra} ${extraConnector}\"" "${SYSTEM_YAML_PATH}" + logger "Setting [${extraConnectorYamlPath}] with extra connectors in system.yaml" + fi +} + +# Migrate extra connectors to system.yaml +migrateExtraConnectors () { + local filePath="$1" + local fileName="$2" + local connectorCount="$3" + local excludeDefaultPort="$4" + local i="$5" + local extraConfig= + local extraConnector= + if [[ "${excludeDefaultPort}" == "yes" ]]; then + for ((i = 1 ; i <= "${connectorCount}" ; i++)); + do + local portValue=$(getXmlConnectorPort "$i" "${filePath}" "${fileName}") + [[ "${portValue}" != "${DEFAULT_ACCESS_PORT}" && "${portValue}" != "${DEFAULT_RT_PORT}" ]] || continue + extraConnector=$($LIBXML2_PATH --xpath '//Server/Service/Connector['$i']' ${filePath}/${fileName} 2>/dev/null) + setExtraConnector "${EXTRA_CONFIG_YAMLPATH}" "${extraConnector}" + done + else + extraConnector=$($LIBXML2_PATH --xpath '//Server/Service/Connector['$i']' ${filePath}/${fileName} 2>/dev/null) + setExtraConnector "${EXTRA_CONFIG_YAMLPATH}" "${extraConnector}" + fi +} + +# Migrate connector configurations +migrateConnectorConfig () { + local i="$1" + local protocolType="$2" + local portValue="$3" + local connectorPortYamlPath="$4" + local connectorMaxThreadYamlPath="$5" + local connectorAttributesYamlPath="$6" + local filePath="$7" + local fileName="$8" + local connectorSendReasonPhraseYamlPath="$9" + local connectorRelaxedPathCharsYamlPath="${10}" + local connectorRelaxedQueryCharsYamlPath="${11}" + + # migrate port + setConnectorPort "${connectorPortYamlPath}" "${portValue}" + + # migrate maxThreads + local maxThreadValue=$(getXmlConnectorMaxThreads "$i" "${filePath}" "${fileName}") + setConnectorMaxThread "${connectorMaxThreadYamlPath}" "${maxThreadValue}" + + # migrate sendReasonPhrase + local sendReasonPhraseValue=$(getXmlConnectorSendReasonPhrase "$i" "${filePath}" "${fileName}") + setConnectorSendReasonPhrase "${connectorSendReasonPhraseYamlPath}" "${sendReasonPhraseValue}" + + # migrate relaxedPathChars + local relaxedPathCharsValue=$(getXmlConnectorRelaxedPathChars "$i" "${filePath}" "${fileName}") + setConnectorRelaxedPathChars "${connectorRelaxedPathCharsYamlPath}" "\"${relaxedPathCharsValue}\"" + # migrate relaxedQueryChars + local relaxedQueryCharsValue=$(getXmlConnectorRelaxedQueryChars "$i" "${filePath}" "${fileName}") + setConnectorRelaxedQueryChars "${connectorRelaxedQueryCharsYamlPath}" "\"${relaxedQueryCharsValue}\"" + + # migrate all attributes to extra config except port , maxThread , sendReasonPhrase ,relaxedPathChars and relaxedQueryChars + local connectorAttributes=$(getXmlConnectorAttributes "$i" "${filePath}" "${fileName}") + connectorAttributes=$(echo "${connectorAttributes}" | sed 's/port="'${portValue}'"//g' | sed 's/maxThreads="'${maxThreadValue}'"//g' | sed 's/sendReasonPhrase="'${sendReasonPhraseValue}'"//g' | sed 's/relaxedPathChars="\'${relaxedPathCharsValue}'\"//g' | sed 's/relaxedQueryChars="\'${relaxedQueryCharsValue}'\"//g') + # strip leading and trailing spaces + connectorAttributes=$(io_trim "${connectorAttributes}") + setConnectorExtraConfig "${connectorAttributesYamlPath}" "${connectorAttributes}" +} + +# Check for default port 8040 and 8081 in connectors and migrate +migrateConnectorPort () { + local filePath="$1" + local fileName="$2" + local connectorCount="$3" + local defaultPort="$4" + local connectorPortYamlPath="$5" + local connectorMaxThreadYamlPath="$6" + local connectorAttributesYamlPath="$7" + local connectorSendReasonPhraseYamlPath="$8" + local connectorRelaxedPathCharsYamlPath="$9" + local connectorRelaxedQueryCharsYamlPath="${10}" + local portYamlPath= + local maxThreadYamlPath= + local status= + for ((i = 1 ; i <= "${connectorCount}" ; i++)); + do + local portValue=$(getXmlConnectorPort "$i" "${filePath}" "${fileName}") + local protocolType=$(getXmlConnectorProtocol "$i" "${filePath}" "${fileName}") + [[ "${protocolType}" == *AJP* ]] && continue + [[ "${portValue}" != "${defaultPort}" ]] && continue + if [[ "${portValue}" == "${DEFAULT_RT_PORT}" ]]; then + RT_DEFAULTPORT_STATUS=success + else + AC_DEFAULTPORT_STATUS=success + fi + migrateConnectorConfig "${i}" "${protocolType}" "${portValue}" "${connectorPortYamlPath}" "${connectorMaxThreadYamlPath}" "${connectorAttributesYamlPath}" "${filePath}" "${fileName}" "${connectorSendReasonPhraseYamlPath}" "${connectorRelaxedPathCharsYamlPath}" "${connectorRelaxedQueryCharsYamlPath}" + done +} + +# migrate to extra, connector having default port and protocol is AJP +migrateDefaultPortIfAjp () { + local filePath="$1" + local fileName="$2" + local connectorCount="$3" + local defaultPort="$4" + + for ((i = 1 ; i <= "${connectorCount}" ; i++)); + do + local portValue=$(getXmlConnectorPort "$i" "${filePath}" "${fileName}") + local protocolType=$(getXmlConnectorProtocol "$i" "${filePath}" "${fileName}") + [[ "${protocolType}" != *AJP* ]] && continue + [[ "${portValue}" != "${defaultPort}" ]] && continue + migrateExtraConnectors "${filePath}" "${fileName}" "${connectorCount}" "no" "${i}" + done + +} + +# Comparing max threads in connectors +compareMaxThreads () { + local firstConnectorMaxThread="$1" + local firstConnectorNode="$2" + local secondConnectorMaxThread="$3" + local secondConnectorNode="$4" + local filePath="$5" + local fileName="$6" + + # choose higher maxThreads connector as Artifactory. + if [[ "${firstConnectorMaxThread}" -gt ${secondConnectorMaxThread} || "${firstConnectorMaxThread}" -eq ${secondConnectorMaxThread} ]]; then + # maxThread is higher in firstConnector, + # Taking firstConnector as Artifactory and SecondConnector as Access + # maxThread is equal in both connector,considering firstConnector as Artifactory and SecondConnector as Access + local rtPortValue=$(getXmlConnectorPort "${firstConnectorNode}" "${filePath}" "${fileName}") + migrateConnectorConfig "${firstConnectorNode}" "${protocolType}" "${rtPortValue}" "${RT_PORT_YAMLPATH}" "${RT_MAXTHREADS_YAMLPATH}" "${RT_EXTRACONFIG_YAMLPATH}" "${filePath}" "${fileName}" "${RT_SENDREASONPHRASE_YAMLPATH}" "${RT_RELAXEDPATHCHARS_YAMLPATH}" "${RT_RELAXEDQUERYCHARS_YAMLPATH}" + local acPortValue=$(getXmlConnectorPort "${secondConnectorNode}" "${filePath}" "${fileName}") + migrateConnectorConfig "${secondConnectorNode}" "${protocolType}" "${acPortValue}" "${AC_PORT_YAMLPATH}" "${AC_MAXTHREADS_YAMLPATH}" "${AC_EXTRACONFIG_YAMLPATH}" "${filePath}" "${fileName}" "${AC_SENDREASONPHRASE_YAMLPATH}" + else + # maxThread is higher in SecondConnector, + # Taking SecondConnector as Artifactory and firstConnector as Access + local rtPortValue=$(getXmlConnectorPort "${secondConnectorNode}" "${filePath}" "${fileName}") + migrateConnectorConfig "${secondConnectorNode}" "${protocolType}" "${rtPortValue}" "${RT_PORT_YAMLPATH}" "${RT_MAXTHREADS_YAMLPATH}" "${RT_EXTRACONFIG_YAMLPATH}" "${filePath}" "${fileName}" "${RT_SENDREASONPHRASE_YAMLPATH}" "${RT_RELAXEDPATHCHARS_YAMLPATH}" "${RT_RELAXEDQUERYCHARS_YAMLPATH}" + local acPortValue=$(getXmlConnectorPort "${firstConnectorNode}" "${filePath}" "${fileName}") + migrateConnectorConfig "${firstConnectorNode}" "${protocolType}" "${acPortValue}" "${AC_PORT_YAMLPATH}" "${AC_MAXTHREADS_YAMLPATH}" "${AC_EXTRACONFIG_YAMLPATH}" "${filePath}" "${fileName}" "${AC_SENDREASONPHRASE_YAMLPATH}" + fi +} + +# Check max threads exist to compare +maxThreadsExistToCompare () { + local filePath="$1" + local fileName="$2" + local connectorCount="$3" + local firstConnectorMaxThread= + local secondConnectorMaxThread= + local firstConnectorNode= + local secondConnectorNode= + local status=success + local firstnode=fail + + for ((i = 1 ; i <= "${connectorCount}" ; i++)); + do + local protocolType=$(getXmlConnectorProtocol "$i" "${filePath}" "${fileName}") + if [[ ${protocolType} == *AJP* ]]; then + # Migrate Connectors + migrateExtraConnectors "${filePath}" "${fileName}" "${connectorCount}" "no" "${i}" + continue + fi + # store maxthreads value of each connector + if [[ ${firstnode} == "fail" ]]; then + firstConnectorMaxThread=$(getXmlConnectorMaxThreads "${i}" "${filePath}" "${fileName}") + firstConnectorNode="${i}" + firstnode=success + else + secondConnectorMaxThread=$(getXmlConnectorMaxThreads "${i}" "${filePath}" "${fileName}") + secondConnectorNode="${i}" + fi + done + [[ -z "${firstConnectorMaxThread}" ]] && status=fail + [[ -z "${secondConnectorMaxThread}" ]] && status=fail + # maxThreads is set, now compare MaxThreads + if [[ "${status}" == "success" ]]; then + compareMaxThreads "${firstConnectorMaxThread}" "${firstConnectorNode}" "${secondConnectorMaxThread}" "${secondConnectorNode}" "${filePath}" "${fileName}" + else + # Assume first connector is RT, maxThreads is not set in both connectors + local rtPortValue=$(getXmlConnectorPort "${firstConnectorNode}" "${filePath}" "${fileName}") + migrateConnectorConfig "${firstConnectorNode}" "${protocolType}" "${rtPortValue}" "${RT_PORT_YAMLPATH}" "${RT_MAXTHREADS_YAMLPATH}" "${RT_EXTRACONFIG_YAMLPATH}" "${filePath}" "${fileName}" "${RT_SENDREASONPHRASE_YAMLPATH}" "${RT_RELAXEDPATHCHARS_YAMLPATH}" "${RT_RELAXEDQUERYCHARS_YAMLPATH}" + local acPortValue=$(getXmlConnectorPort "${secondConnectorNode}" "${filePath}" "${fileName}") + migrateConnectorConfig "${secondConnectorNode}" "${protocolType}" "${acPortValue}" "${AC_PORT_YAMLPATH}" "${AC_MAXTHREADS_YAMLPATH}" "${AC_EXTRACONFIG_YAMLPATH}" "${filePath}" "${fileName}" "${AC_SENDREASONPHRASE_YAMLPATH}" + fi +} + +migrateExtraBasedOnNonAjpCount () { + local nonAjpCount="$1" + local filePath="$2" + local fileName="$3" + local connectorCount="$4" + local i="$5" + + local protocolType=$(getXmlConnectorProtocol "$i" "${filePath}" "${fileName}") + if [[ "${protocolType}" == *AJP* ]]; then + if [[ "${nonAjpCount}" -eq 1 ]]; then + # migrateExtraConnectors + migrateExtraConnectors "${filePath}" "${fileName}" "${connectorCount}" "no" "${i}" + continue + else + # migrateExtraConnectors + migrateExtraConnectors "${filePath}" "${fileName}" "${connectorCount}" "yes" + continue + fi + fi +} + +# find RT and AC Connector +findRtAndAcConnector () { + local filePath="$1" + local fileName="$2" + local connectorCount="$3" + local initialAjpCount=0 + local nonAjpCount=0 + + # get the count of non AJP + for ((i = 1 ; i <= "${connectorCount}" ; i++)); + do + local portValue=$(getXmlConnectorPort "$i" "${filePath}" "${fileName}") + local protocolType=$(getXmlConnectorProtocol "$i" "${filePath}" "${fileName}") + [[ "${protocolType}" != *AJP* ]] || continue + nonAjpCount=$((initialAjpCount+1)) + initialAjpCount="${nonAjpCount}" + done + if [[ "${nonAjpCount}" -eq 1 ]]; then + # Add the connector found as access and artifactory connectors + # Mark port as 8040 for access + for ((i = 1 ; i <= "${connectorCount}" ; i++)) + do + migrateExtraBasedOnNonAjpCount "${nonAjpCount}" "${filePath}" "${fileName}" "${connectorCount}" "$i" + local portValue=$(getXmlConnectorPort "$i" "${filePath}" "${fileName}") + migrateConnectorConfig "$i" "${protocolType}" "${portValue}" "${RT_PORT_YAMLPATH}" "${RT_MAXTHREADS_YAMLPATH}" "${RT_EXTRACONFIG_YAMLPATH}" "${filePath}" "${fileName}" "${RT_SENDREASONPHRASE_YAMLPATH}" "${RT_RELAXEDPATHCHARS_YAMLPATH}" "${RT_RELAXEDQUERYCHARS_YAMLPATH}" + migrateConnectorConfig "$i" "${protocolType}" "${portValue}" "${AC_PORT_YAMLPATH}" "${AC_MAXTHREADS_YAMLPATH}" "${AC_EXTRACONFIG_YAMLPATH}" "${filePath}" "${fileName}" "${AC_SENDREASONPHRASE_YAMLPATH}" + setConnectorPort "${AC_PORT_YAMLPATH}" "${DEFAULT_ACCESS_PORT}" + done + elif [[ "${nonAjpCount}" -eq 2 ]]; then + # compare maxThreads in both connectors + maxThreadsExistToCompare "${filePath}" "${fileName}" "${connectorCount}" + elif [[ "${nonAjpCount}" -gt 2 ]]; then + # migrateExtraConnectors + migrateExtraConnectors "${filePath}" "${fileName}" "${connectorCount}" "yes" + elif [[ "${nonAjpCount}" -eq 0 ]]; then + # setting with default port in system.yaml + setConnectorPort "${RT_PORT_YAMLPATH}" "${DEFAULT_RT_PORT}" + setConnectorPort "${AC_PORT_YAMLPATH}" "${DEFAULT_ACCESS_PORT}" + # migrateExtraConnectors + migrateExtraConnectors "${filePath}" "${fileName}" "${connectorCount}" "yes" + fi +} + +# get the count of non AJP +getCountOfNonAjp () { + local port="$1" + local connectorCount="$2" + local filePath=$3 + local fileName=$4 + local initialNonAjpCount=0 + + for ((i = 1 ; i <= "${connectorCount}" ; i++)); + do + local portValue=$(getXmlConnectorPort "$i" "${filePath}" "${fileName}") + local protocolType=$(getXmlConnectorProtocol "$i" "${filePath}" "${fileName}") + [[ "${portValue}" != "${port}" ]] || continue + [[ "${protocolType}" != *AJP* ]] || continue + local nonAjpCount=$((initialNonAjpCount+1)) + initialNonAjpCount="${nonAjpCount}" + done + echo -e "${nonAjpCount}" +} + +# Find for access connector +findAcConnector () { + local filePath="$1" + local fileName="$2" + local connectorCount="$3" + + # get the count of non AJP + local nonAjpCount=$(getCountOfNonAjp "${DEFAULT_RT_PORT}" "${connectorCount}" "${filePath}" "${fileName}") + if [[ "${nonAjpCount}" -eq 1 ]]; then + # Add the connector found as access connector and mark port as that of connector + for ((i = 1 ; i <= "${connectorCount}" ; i++)) + do + migrateExtraBasedOnNonAjpCount "${nonAjpCount}" "${filePath}" "${fileName}" "${connectorCount}" "$i" + local portValue=$(getXmlConnectorPort "$i" "${filePath}" "${fileName}") + if [[ "${portValue}" != "${DEFAULT_RT_PORT}" ]]; then + migrateConnectorConfig "$i" "${protocolType}" "${portValue}" "${AC_PORT_YAMLPATH}" "${AC_MAXTHREADS_YAMLPATH}" "${AC_EXTRACONFIG_YAMLPATH}" "${filePath}" "${fileName}" "${AC_SENDREASONPHRASE_YAMLPATH}" + fi + done + elif [[ "${nonAjpCount}" -gt 1 ]]; then + # Take RT properties into access with 8040 + for ((i = 1 ; i <= "${connectorCount}" ; i++)) + do + migrateExtraBasedOnNonAjpCount "${nonAjpCount}" "${filePath}" "${fileName}" "${connectorCount}" "$i" + local portValue=$(getXmlConnectorPort "$i" "${filePath}" "${fileName}") + if [[ "${portValue}" == "${DEFAULT_RT_PORT}" ]]; then + migrateConnectorConfig "$i" "${protocolType}" "${portValue}" "${AC_PORT_YAMLPATH}" "${AC_MAXTHREADS_YAMLPATH}" "${AC_EXTRACONFIG_YAMLPATH}" "${filePath}" "${fileName}" "${AC_SENDREASONPHRASE_YAMLPATH}" + setConnectorPort "${AC_PORT_YAMLPATH}" "${DEFAULT_ACCESS_PORT}" + fi + done + elif [[ "${nonAjpCount}" -eq 0 ]]; then + # Add RT connector details as access connector and mark port as 8040 + migrateConnectorPort "${filePath}" "${fileName}" "${connectorCount}" "${DEFAULT_RT_PORT}" "${AC_PORT_YAMLPATH}" "${AC_MAXTHREADS_YAMLPATH}" "${AC_EXTRACONFIG_YAMLPATH}" "${AC_SENDREASONPHRASE_YAMLPATH}" + setConnectorPort "${AC_PORT_YAMLPATH}" "${DEFAULT_ACCESS_PORT}" + # migrateExtraConnectors + migrateExtraConnectors "${filePath}" "${fileName}" "${connectorCount}" "yes" + fi +} + +# Find for artifactory connector +findRtConnector () { + local filePath="$1" + local fileName="$2" + local connectorCount="$3" + + # get the count of non AJP + local nonAjpCount=$(getCountOfNonAjp "${DEFAULT_ACCESS_PORT}" "${connectorCount}" "${filePath}" "${fileName}") + if [[ "${nonAjpCount}" -eq 1 ]]; then + # Add the connector found as RT connector + for ((i = 1 ; i <= "${connectorCount}" ; i++)) + do + migrateExtraBasedOnNonAjpCount "${nonAjpCount}" "${filePath}" "${fileName}" "${connectorCount}" "$i" + local portValue=$(getXmlConnectorPort "$i" "${filePath}" "${fileName}") + if [[ "${portValue}" != "${DEFAULT_ACCESS_PORT}" ]]; then + migrateConnectorConfig "$i" "${protocolType}" "${portValue}" "${RT_PORT_YAMLPATH}" "${RT_MAXTHREADS_YAMLPATH}" "${RT_EXTRACONFIG_YAMLPATH}" "${filePath}" "${fileName}" "${RT_SENDREASONPHRASE_YAMLPATH}" "${RT_RELAXEDPATHCHARS_YAMLPATH}" "${RT_RELAXEDQUERYCHARS_YAMLPATH}" + fi + done + elif [[ "${nonAjpCount}" -gt 1 ]]; then + # Take access properties into artifactory with 8081 + for ((i = 1 ; i <= "${connectorCount}" ; i++)) + do + migrateExtraBasedOnNonAjpCount "${nonAjpCount}" "${filePath}" "${fileName}" "${connectorCount}" "$i" + local portValue=$(getXmlConnectorPort "$i" "${filePath}" "${fileName}") + if [[ "${portValue}" == "${DEFAULT_ACCESS_PORT}" ]]; then + migrateConnectorConfig "$i" "${protocolType}" "${portValue}" "${RT_PORT_YAMLPATH}" "${RT_MAXTHREADS_YAMLPATH}" "${RT_EXTRACONFIG_YAMLPATH}" "${filePath}" "${fileName}" "${RT_SENDREASONPHRASE_YAMLPATH}" "${RT_RELAXEDPATHCHARS_YAMLPATH}" "${RT_RELAXEDQUERYCHARS_YAMLPATH}" + setConnectorPort "${RT_PORT_YAMLPATH}" "${DEFAULT_RT_PORT}" + fi + done + elif [[ "${nonAjpCount}" -eq 0 ]]; then + # Add access connector details as RT connector and mark as ${DEFAULT_RT_PORT} + migrateConnectorPort "${filePath}" "${fileName}" "${connectorCount}" "${DEFAULT_ACCESS_PORT}" "${RT_PORT_YAMLPATH}" "${RT_MAXTHREADS_YAMLPATH}" "${RT_EXTRACONFIG_YAMLPATH}" "${RT_SENDREASONPHRASE_YAMLPATH}" "${RT_RELAXEDPATHCHARS_YAMLPATH}" "${RT_RELAXEDQUERYCHARS_YAMLPATH}" + setConnectorPort "${RT_PORT_YAMLPATH}" "${DEFAULT_RT_PORT}" + # migrateExtraConnectors + migrateExtraConnectors "${filePath}" "${fileName}" "${connectorCount}" "yes" + fi +} + +checkForTlsConnector () { + local filePath="$1" + local fileName="$2" + local connectorCount="$3" + for ((i = 1 ; i <= "${connectorCount}" ; i++)) + do + local sslProtocolValue=$($LIBXML2_PATH --xpath '//Server/Service/Connector['$i']/@sslProtocol' ${filePath}/${fileName} 2>/dev/null | awk -F"=" '{print $2}' | tr -d '"') + if [[ "${sslProtocolValue}" == "TLS" ]]; then + bannerImportant "NOTE: Ignoring TLS connector during migration, modify the system yaml to enable TLS. Original server.xml is saved in path [${filePath}/${fileName}]" + TLS_CONNECTOR_EXISTS=${FLAG_Y} + continue + fi + done +} + +# set custom tomcat server Listeners to system.yaml +setListenerConnector () { + local filePath="$1" + local fileName="$2" + local listenerCount="$3" + for ((i = 1 ; i <= "${listenerCount}" ; i++)) + do + local listenerConnector=$($LIBXML2_PATH --xpath '//Server/Listener['$i']' ${filePath}/${fileName} 2>/dev/null) + local listenerClassName=$($LIBXML2_PATH --xpath '//Server/Listener['$i']/@className' ${filePath}/${fileName} 2>/dev/null | awk -F"=" '{print $2}' | tr -d '"') + if [[ "${listenerClassName}" == *Apr* ]]; then + setExtraConnector "${EXTRA_LISTENER_CONFIG_YAMLPATH}" "${listenerConnector}" + fi + done +} +# add custom tomcat server Listeners +addTomcatServerListeners () { + local filePath="$1" + local fileName="$2" + local listenerCount="$3" + if [[ "${listenerCount}" == "0" ]]; then + logger "No listener connectors found in the [${filePath}/${fileName}],skipping migration of listener connectors" + else + setListenerConnector "${filePath}" "${fileName}" "${listenerCount}" + setSystemValue "${RT_TOMCAT_HTTPSCONNECTOR_ENABLED}" "true" "${SYSTEM_YAML_PATH}" + logger "Setting [${RT_TOMCAT_HTTPSCONNECTOR_ENABLED}] with value [true] in system.yaml" + fi +} + +# server.xml migration operations +xmlMigrateOperation () { + local filePath="$1" + local fileName="$2" + local connectorCount="$3" + local listenerCount="$4" + RT_DEFAULTPORT_STATUS=fail + AC_DEFAULTPORT_STATUS=fail + TLS_CONNECTOR_EXISTS=${FLAG_N} + + # Check for connector with TLS , if found ignore migrating it + checkForTlsConnector "${filePath}" "${fileName}" "${connectorCount}" + if [[ "${TLS_CONNECTOR_EXISTS}" == "${FLAG_Y}" ]]; then + return + fi + addTomcatServerListeners "${filePath}" "${fileName}" "${listenerCount}" + # Migrate RT default port from connectors + migrateConnectorPort "${filePath}" "${fileName}" "${connectorCount}" "${DEFAULT_RT_PORT}" "${RT_PORT_YAMLPATH}" "${RT_MAXTHREADS_YAMLPATH}" "${RT_EXTRACONFIG_YAMLPATH}" "${RT_SENDREASONPHRASE_YAMLPATH}" "${RT_RELAXEDPATHCHARS_YAMLPATH}" "${RT_RELAXEDQUERYCHARS_YAMLPATH}" + # Migrate to extra if RT default ports are AJP + migrateDefaultPortIfAjp "${filePath}" "${fileName}" "${connectorCount}" "${DEFAULT_RT_PORT}" + # Migrate AC default port from connectors + migrateConnectorPort "${filePath}" "${fileName}" "${connectorCount}" "${DEFAULT_ACCESS_PORT}" "${AC_PORT_YAMLPATH}" "${AC_MAXTHREADS_YAMLPATH}" "${AC_EXTRACONFIG_YAMLPATH}" "${AC_SENDREASONPHRASE_YAMLPATH}" + # Migrate to extra if access default ports are AJP + migrateDefaultPortIfAjp "${filePath}" "${fileName}" "${connectorCount}" "${DEFAULT_ACCESS_PORT}" + + if [[ "${AC_DEFAULTPORT_STATUS}" == "success" && "${RT_DEFAULTPORT_STATUS}" == "success" ]]; then + # RT and AC default port found + logger "Artifactory 8081 and Access 8040 default port are found" + migrateExtraConnectors "${filePath}" "${fileName}" "${connectorCount}" "yes" + elif [[ "${AC_DEFAULTPORT_STATUS}" == "success" && "${RT_DEFAULTPORT_STATUS}" == "fail" ]]; then + # Only AC default port found,find RT connector + logger "Found Access default 8040 port" + findRtConnector "${filePath}" "${fileName}" "${connectorCount}" + elif [[ "${AC_DEFAULTPORT_STATUS}" == "fail" && "${RT_DEFAULTPORT_STATUS}" == "success" ]]; then + # Only RT default port found,find AC connector + logger "Found Artifactory default 8081 port" + findAcConnector "${filePath}" "${fileName}" "${connectorCount}" + elif [[ "${AC_DEFAULTPORT_STATUS}" == "fail" && "${RT_DEFAULTPORT_STATUS}" == "fail" ]]; then + # RT and AC default port not found, find connector + logger "Artifactory 8081 and Access 8040 default port are not found" + findRtAndAcConnector "${filePath}" "${fileName}" "${connectorCount}" + fi +} + +# get count of connectors +getXmlConnectorCount () { + local filePath="$1" + local fileName="$2" + local count=$($LIBXML2_PATH --xpath 'count(/Server/Service/Connector)' ${filePath}/${fileName}) + echo -e "${count}" +} + +# get count of listener connectors +getTomcatServerListenersCount () { + local filePath="$1" + local fileName="$2" + local count=$($LIBXML2_PATH --xpath 'count(/Server/Listener)' ${filePath}/${fileName}) + echo -e "${count}" +} + +# Migrate server.xml configuration to system.yaml +migrateXmlFile () { + local xmlFiles= + local fileName= + local filePath= + local sourceFilePath= + DEFAULT_ACCESS_PORT="8040" + DEFAULT_RT_PORT="8081" + AC_PORT_YAMLPATH="migration.xmlFiles.serverXml.access.port" + AC_MAXTHREADS_YAMLPATH="migration.xmlFiles.serverXml.access.maxThreads" + AC_SENDREASONPHRASE_YAMLPATH="migration.xmlFiles.serverXml.access.sendReasonPhrase" + AC_EXTRACONFIG_YAMLPATH="migration.xmlFiles.serverXml.access.extraConfig" + RT_PORT_YAMLPATH="migration.xmlFiles.serverXml.artifactory.port" + RT_MAXTHREADS_YAMLPATH="migration.xmlFiles.serverXml.artifactory.maxThreads" + RT_SENDREASONPHRASE_YAMLPATH='migration.xmlFiles.serverXml.artifactory.sendReasonPhrase' + RT_RELAXEDPATHCHARS_YAMLPATH='migration.xmlFiles.serverXml.artifactory.relaxedPathChars' + RT_RELAXEDQUERYCHARS_YAMLPATH='migration.xmlFiles.serverXml.artifactory.relaxedQueryChars' + RT_EXTRACONFIG_YAMLPATH="migration.xmlFiles.serverXml.artifactory.extraConfig" + ROUTER_PORT_YAMLPATH="migration.xmlFiles.serverXml.router.port" + EXTRA_CONFIG_YAMLPATH="migration.xmlFiles.serverXml.extra.config" + EXTRA_LISTENER_CONFIG_YAMLPATH="migration.xmlFiles.serverXml.extra.listener" + RT_TOMCAT_HTTPSCONNECTOR_ENABLED="artifactory.tomcat.httpsConnector.enabled" + + retrieveYamlValue "migration.xmlFiles" "xmlFiles" "Skip" + xmlFiles="${YAML_VALUE}" + if [[ -z "${xmlFiles}" ]]; then + return + fi + bannerSection "PROCESSING MIGRATION OF XML FILES" + retrieveYamlValue "migration.xmlFiles.serverXml.fileName" "fileName" "Warning" + fileName="${YAML_VALUE}" + if [[ -z "${fileName}" ]]; then + return + fi + bannerSubSection "Processing Migration of $fileName" + retrieveYamlValue "migration.xmlFiles.serverXml.filePath" "filePath" "Warning" + filePath="${YAML_VALUE}" + if [[ -z "${filePath}" ]]; then + return + fi + # prepend NEW_DATA_DIR only if filePath is relative path + sourceFilePath=$(prependDir "${filePath}" "${NEW_DATA_DIR}/${filePath}") + if [[ "$(checkFileExists "${sourceFilePath}/${fileName}")" == "true" ]]; then + logger "File [${fileName}] is found in path [${sourceFilePath}]" + local connectorCount=$(getXmlConnectorCount "${sourceFilePath}" "${fileName}") + if [[ "${connectorCount}" == "0" ]]; then + logger "No connectors found in the [${filePath}/${fileName}],skipping migration of xml configuration" + return + fi + local listenerCount=$(getTomcatServerListenersCount "${sourceFilePath}" "${fileName}") + xmlMigrateOperation "${sourceFilePath}" "${fileName}" "${connectorCount}" "${listenerCount}" + else + logger "File [${fileName}] is not found in path [${sourceFilePath}] to migrate" + fi +} + +compareArtifactoryUser () { + local property="$1" + local oldPropertyValue="$2" + local newPropertyValue="$3" + local yamlPath="$4" + local sourceFile="$5" + + if [[ "${oldPropertyValue}" != "${newPropertyValue}" ]]; then + setSystemValue "${yamlPath}" "${oldPropertyValue}" "${SYSTEM_YAML_PATH}" + logger "Setting [${yamlPath}] with value of the property [${property}] in system.yaml" + else + logger "No change in property [${property}] value in [${sourceFile}] to migrate" + fi +} + +migrateReplicator () { + local property="$1" + local oldPropertyValue="$2" + local yamlPath="$3" + + setSystemValue "${yamlPath}" "${oldPropertyValue}" "${SYSTEM_YAML_PATH}" + logger "Setting [${yamlPath}] with value of the property [${property}] in system.yaml" +} + +compareJavaOptions () { + local property="$1" + local oldPropertyValue="$2" + local newPropertyValue="$3" + local yamlPath="$4" + local sourceFile="$5" + local oldJavaOption= + local newJavaOption= + local extraJavaOption= + local check=false + local success=true + local status=true + + oldJavaOption=$(echo "${oldPropertyValue}" | awk 'BEGIN{FS=OFS="\""}{for(i=2;i.+)\.{{ include "artifactory.fullname" . }} {{ include "artifactory.fullname" . }} +{{ tpl (include "artifactory.nginx.hosts" .) . }}; + +if ($http_x_forwarded_proto = '') { + set $http_x_forwarded_proto $scheme; +} +set $host_port {{ .Values.nginx.https.externalPort }}; +if ( $scheme = "http" ) { + set $host_port {{ .Values.nginx.http.externalPort }}; +} +## Application specific logs +## access_log /var/log/nginx/artifactory-access.log timing; +## error_log /var/log/nginx/artifactory-error.log; +rewrite ^/artifactory/?$ / redirect; +if ( $repo != "" ) { + rewrite ^/(v1|v2)/(.*) /artifactory/api/docker/$repo/$1/$2 break; +} +chunked_transfer_encoding on; +client_max_body_size 0; + +location / { + proxy_read_timeout 900; + proxy_pass_header Server; + proxy_cookie_path ~*^/.* /; + proxy_pass {{ include "artifactory.scheme" . }}://{{ include "artifactory.fullname" . }}:{{ .Values.artifactory.externalPort }}/; + {{- if .Values.nginx.service.ssloffload}} + {{- if .Values.nginx.service.ssloffloadForceHttps}} + proxy_set_header X-JFrog-Override-Base-Url https://$host; + proxy_set_header X-Forwarded-Proto https; + {{- else }} + proxy_set_header X-JFrog-Override-Base-Url $http_x_forwarded_proto://$host; + proxy_set_header X-Forwarded-Proto $http_x_forwarded_proto; + {{- end }} + {{- else if or (eq (int .Values.nginx.https.internalPort) 80) (eq (int .Values.nginx.https.externalPort) 443)}} + proxy_set_header X-JFrog-Override-Base-Url $http_x_forwarded_proto://$host; + proxy_set_header X-Forwarded-Port $server_port; + proxy_set_header X-Forwarded-Proto $http_x_forwarded_proto; + {{- else }} + proxy_set_header X-JFrog-Override-Base-Url $http_x_forwarded_proto://$host:$host_port; + proxy_set_header X-Forwarded-Port $server_port; + proxy_set_header X-Forwarded-Proto $http_x_forwarded_proto; + {{- end }} + proxy_set_header Host $http_host; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + {{- if .Values.nginx.disableProxyBuffering}} + proxy_http_version 1.1; + proxy_request_buffering off; + proxy_buffering off; + {{- end }} + add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always; + location /artifactory/ { + {{- if .Values.rtfs.enabled }} + if ($request_uri ~ ^/artifactory/service/rtfs/(.*)$ ) { + proxy_pass http://{{ include "artifactory.fullname" . }}:{{ .Values.artifactory.externalPort }}/artifactory/service/rtfs/$1; + break; + } + {{- end }} + if ( $request_uri ~ ^/artifactory/(.*)$ ) { + proxy_pass http://{{ include "artifactory.fullname" . }}:{{ .Values.artifactory.externalArtifactoryPort }}/artifactory/$1; + } + proxy_pass http://{{ include "artifactory.fullname" . }}:{{ .Values.artifactory.externalArtifactoryPort }}/artifactory/; + } + location /pipelines/ { + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "upgrade"; + proxy_set_header Host $http_host; + {{- if .Values.router.tlsEnabled }} + proxy_pass https://{{ include "artifactory.fullname" . }}:{{ .Values.router.internalPort }}; + {{- else }} + proxy_pass http://{{ include "artifactory.fullname" . }}:{{ .Values.router.internalPort }}; + {{- end }} + } +} +} \ No newline at end of file diff --git a/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/files/nginx-main-conf.yaml b/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/files/nginx-main-conf.yaml new file mode 100644 index 0000000000..6ee7f98f9e --- /dev/null +++ b/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/files/nginx-main-conf.yaml @@ -0,0 +1,83 @@ +# Main Nginx configuration file +worker_processes 4; + +{{- if .Values.nginx.logs.stderr }} +error_log stderr {{ .Values.nginx.logs.level }}; +{{- else -}} +error_log {{ .Values.nginx.persistence.mountPath }}/logs/error.log {{ .Values.nginx.logs.level }}; +{{- end }} +pid /var/run/nginx.pid; + +{{- if .Values.artifactory.ssh.enabled }} +## SSH Server Configuration +stream { + server { + {{- if .Values.nginx.singleStackIPv6Cluster }} + listen [::]:{{ .Values.nginx.ssh.internalPort }}; + {{- else -}} + listen {{ .Values.nginx.ssh.internalPort }}; + {{- end }} + proxy_pass {{ include "artifactory.fullname" . }}:{{ .Values.artifactory.ssh.externalPort }}; + } +} +{{- end }} + +events { + worker_connections 1024; +} + +http { + include /etc/nginx/mime.types; + default_type application/octet-stream; + + variables_hash_max_size 1024; + variables_hash_bucket_size 64; + server_names_hash_max_size 4096; + server_names_hash_bucket_size 128; + types_hash_max_size 2048; + types_hash_bucket_size 64; + proxy_read_timeout 2400s; + client_header_timeout 2400s; + client_body_timeout 2400s; + proxy_connect_timeout 75s; + proxy_send_timeout 2400s; + proxy_buffer_size 128k; + proxy_buffers 40 128k; + proxy_busy_buffers_size 128k; + proxy_temp_file_write_size 250m; + proxy_http_version 1.1; + client_body_buffer_size 128k; + + log_format main '$remote_addr - $remote_user [$time_local] "$request" ' + '$status $body_bytes_sent "$http_referer" ' + '"$http_user_agent" "$http_x_forwarded_for"'; + + log_format timing 'ip = $remote_addr ' + 'user = \"$remote_user\" ' + 'local_time = \"$time_local\" ' + 'host = $host ' + 'request = \"$request\" ' + 'status = $status ' + 'bytes = $body_bytes_sent ' + 'upstream = \"$upstream_addr\" ' + 'upstream_time = $upstream_response_time ' + 'request_time = $request_time ' + 'referer = \"$http_referer\" ' + 'UA = \"$http_user_agent\"'; + + {{- if .Values.nginx.logs.stdout }} + access_log /dev/stdout timing; + {{- else -}} + access_log {{ .Values.nginx.persistence.mountPath }}/logs/access.log timing; + {{- end }} + + sendfile on; + #tcp_nopush on; + + keepalive_timeout 65; + + #gzip on; + + include /etc/nginx/conf.d/*.conf; + +} diff --git a/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/files/system.yaml b/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/files/system.yaml new file mode 100644 index 0000000000..ac876432eb --- /dev/null +++ b/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/files/system.yaml @@ -0,0 +1,186 @@ +router: + serviceRegistry: + insecure: {{ .Values.router.serviceRegistry.insecure }} +shared: +{{- if .Values.artifactory.coldStorage.enabled }} + jfrogColdStorage: + coldInstanceEnabled: true +{{- end }} +{{- if .Values.artifactory.worker.enabled }} + featureToggler: + worker: true +{{- end }} +{{- with include "artifactory.metrics" . }} + {{- . | nindent 2 }} +{{- end }} + logging: + consoleLog: + enabled: {{ .Values.artifactory.consoleLog }} + extraJavaOpts: > + -Dartifactory.graceful.shutdown.max.request.duration.millis={{ mul .Values.artifactory.terminationGracePeriodSeconds 1000 }} + -Dartifactory.access.client.max.connections={{ .Values.access.tomcat.connector.maxThreads }} +{{- if .Values.artifactory.worker.enabled }} + -Dartifactory.workers.addon.support=true +{{- end }} + {{- with .Values.artifactory.javaOpts }} + {{- if .corePoolSize }} + -Dartifactory.async.corePoolSize={{ .corePoolSize }} + {{- end }} + {{- if .xms }} + -Xms{{ .xms }} + {{- end }} + {{- if .xmx }} + -Xmx{{ .xmx }} + {{- end }} + {{- if .jmx.enabled }} + -Dcom.sun.management.jmxremote + -Dcom.sun.management.jmxremote.port={{ .jmx.port }} + -Dcom.sun.management.jmxremote.rmi.port={{ .jmx.port }} + -Dcom.sun.management.jmxremote.ssl={{ .jmx.ssl }} + {{- if .jmx.host }} + -Djava.rmi.server.hostname={{ tpl .jmx.host $ }} + {{- else }} + -Djava.rmi.server.hostname={{ template "artifactory.fullname" $ }} + {{- end }} + {{- if .jmx.authenticate }} + -Dcom.sun.management.jmxremote.authenticate=true + -Dcom.sun.management.jmxremote.access.file={{ .jmx.accessFile }} + -Dcom.sun.management.jmxremote.password.file={{ .jmx.passwordFile }} + {{- else }} + -Dcom.sun.management.jmxremote.authenticate=false + {{- end }} + {{- end }} + {{- if .other }} + {{ .other }} + {{- end }} + {{- end }} + {{- if or .Values.database.type .Values.postgresql.enabled }} + database: + allowNonPostgresql: {{ .Values.database.allowNonPostgresql }} + {{- if .Values.postgresql.enabled }} + type: postgresql + url: "jdbc:postgresql://{{ .Release.Name }}-postgresql:{{ .Values.postgresql.service.port }}/{{ .Values.postgresql.postgresqlDatabase }}" + driver: org.postgresql.Driver + username: "{{ .Values.postgresql.postgresqlUsername }}" + {{- else }} + type: "{{ .Values.database.type }}" + driver: "{{ .Values.database.driver }}" + {{- end }} + {{- end }} +artifactory: +{{- if or .Values.artifactory.haDataDir.enabled .Values.artifactory.haBackupDir.enabled }} + node: + {{- if .Values.artifactory.haDataDir.path }} + haDataDir: {{ .Values.artifactory.haDataDir.path }} + {{- end }} + {{- if .Values.artifactory.haBackupDir.path }} + haBackupDir: {{ .Values.artifactory.haBackupDir.path }} + {{- end }} +{{- end }} + database: + maxOpenConnections: {{ .Values.artifactory.database.maxOpenConnections }} + tomcat: + maintenanceConnector: + port: {{ .Values.artifactory.tomcat.maintenanceConnector.port }} + connector: + maxThreads: {{ .Values.artifactory.tomcat.connector.maxThreads }} + sendReasonPhrase: {{ .Values.artifactory.tomcat.connector.sendReasonPhrase }} + extraConfig: {{ .Values.artifactory.tomcat.connector.extraConfig }} +frontend: + session: + timeMinutes: {{ .Values.frontend.session.timeoutMinutes | quote }} +access: + runOnArtifactoryTomcat: {{ .Values.access.runOnArtifactoryTomcat | default false }} + database: + maxOpenConnections: {{ .Values.access.database.maxOpenConnections }} + {{- if not (.Values.access.runOnArtifactoryTomcat | default false) }} + extraJavaOpts: > + {{- if .Values.splitServicesToContainers }} + -XX:InitialRAMPercentage=20 + -XX:MaxRAMPercentage=70 + {{- end }} + {{- with .Values.access.javaOpts }} + {{- if .other }} + {{ .other }} + {{- end }} + {{- end }} + {{- end }} + tomcat: + connector: + maxThreads: {{ .Values.access.tomcat.connector.maxThreads }} + sendReasonPhrase: {{ .Values.access.tomcat.connector.sendReasonPhrase }} + extraConfig: {{ .Values.access.tomcat.connector.extraConfig }} +{{- if .Values.artifactory.worker.enabled }} + worker: + enabled: true +{{- end }} +{{- if .Values.mc.enabled }} +mc: + enabled: true + database: + maxOpenConnections: {{ .Values.mc.database.maxOpenConnections }} + idgenerator: + maxOpenConnections: {{ .Values.mc.idgenerator.maxOpenConnections }} + tomcat: + connector: + maxThreads: {{ .Values.mc.tomcat.connector.maxThreads }} + sendReasonPhrase: {{ .Values.mc.tomcat.connector.sendReasonPhrase }} + extraConfig: {{ .Values.mc.tomcat.connector.extraConfig }} +{{- end }} +metadata: + database: + maxOpenConnections: {{ .Values.metadata.database.maxOpenConnections }} +{{- if and .Values.jfconnect.enabled (not (regexMatch "^.*(oss|cpp-ce|jcr).*$" .Values.artifactory.image.repository)) }} +jfconnect: + enabled: true +{{- else }} +jfconnect: + enabled: false +jfconnect_service: + enabled: false +{{- end }} + +onemodel: + enabled: {{ .Values.onemodel.enabled }} + {{- if .Values.onemodel.apolloYaml }} + apolloYaml: +{{ toYaml .Values.onemodel.apolloYaml | nindent 6 }} + {{- end}} +{{- if and .Values.rtfs.enabled (not (regexMatch "^.*(oss|cpp-ce|jcr).*$" .Values.artifactory.image.repository)) }} +rtfs: + enabled: true +{{- else }} +rtfs: + enabled: false +{{- end }} +{{- if .Values.topology.enabled }} +topology: + enabled: {{ .Values.topology.enabled }} + database: + maxOpenConnections: {{ .Values.topology.database.maxOpenConnections }} + port: {{ .Values.topology.internalPort }} + extraJavaOpts: > + {{- if .Values.splitServicesToContainers }} + -XX:InitialRAMPercentage=20 + -XX:MaxRAMPercentage=70 + {{- end }} + {{- with .Values.topology.javaOpts }} + {{- if .other }} + {{ .other }} + {{- end }} + {{- end }} +{{- else }} +topology: + enabled: {{ .Values.topology.enabled }} +{{- end }} +{{- if .Values.event.webhooks }} +event: + webhooks: {{ toYaml .Values.event.webhooks | nindent 6 }} +{{- end }} +{{- if .Values.evidence.enabled }} +evidence: + enabled: true +{{- else }} +evidence: + enabled: false +{{- end }} \ No newline at end of file diff --git a/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/logo/artifactory-logo.png b/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/logo/artifactory-logo.png new file mode 100644 index 0000000000000000000000000000000000000000..fe6c23c5a7f87edaf49f883ffa99d875073e2285 GIT binary patch literal 82419 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 z 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 GZv9`RA2Y%L literal 0 HcmV?d00001 diff --git a/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/sizing/artifactory-2xlarge.yaml b/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/sizing/artifactory-2xlarge.yaml new file mode 100644 index 0000000000..9fdf63e541 --- /dev/null +++ b/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/sizing/artifactory-2xlarge.yaml @@ -0,0 +1,182 @@ +############################################################################################## +# The 2xlarge sizing +# This size is intended for very large organizations. It can be increased with adding replicas +# [WARNING] Some of the configuration mentioned in this file are taken inside system.yaml +# hence this configuration will be overridden when enabling systemYamlOverride +############################################################################################## +splitServicesToContainers: true +artifactory: + # Enterprise and above licenses are required for setting replicaCount greater than 1. + # Count should be equal or above the total number of licenses available for artifactory. + replicaCount: 6 + + # Require multiple Artifactory pods to run on separate nodes + podAntiAffinity: + type: "hard" + + resources: + requests: + cpu: "4" + memory: 20Gi + limits: + # cpu: "20" + memory: 24Gi + + extraEnvironmentVariables: + - name: MALLOC_ARENA_MAX + value: "16" + - name: SKIP_WAIT_FOR_EXTERNAL_DB + value: "true" + + javaOpts: + other: > + -XX:InitialRAMPercentage=40 + -XX:MaxRAMPercentage=70 + -Dartifactory.async.corePoolSize=200 + -Dartifactory.async.poolMaxQueueSize=100000 + -Dartifactory.http.client.max.total.connections=150 + -Dartifactory.http.client.max.connections.per.route=150 + -Dartifactory.access.client.max.connections=200 + -Dartifactory.metadata.event.operator.threads=5 + -XX:MaxMetaspaceSize=512m + -Djdk.nio.maxCachedBufferSize=1048576 + -XX:MaxDirectMemorySize=1024m + tomcat: + connector: + maxThreads: 800 + extraConfig: 'acceptCount="1200" acceptorThreadCount="2" compression="off" connectionLinger="-1" connectionTimeout="120000" enableLookups="false"' + + database: + maxOpenConnections: 200 + +access: + tomcat: + connector: + maxThreads: 200 + javaOpts: + other: > + -XX:InitialRAMPercentage=20 + -XX:MaxRAMPercentage=60 + database: + maxOpenConnections: 200 + resources: + requests: + cpu: 1 + memory: 2Gi + limits: + # cpu: 2 + memory: 4Gi + extraEnvironmentVariables: + - name: MALLOC_ARENA_MAX + value: "16" + - name: SKIP_WAIT_FOR_EXTERNAL_DB + value: "true" + +router: + resources: + requests: + cpu: "2" + memory: 2Gi + limits: + # cpu: "12" + memory: 4Gi + +frontend: + resources: + requests: + cpu: "1" + memory: 500Mi + limits: + # cpu: "5" + memory: 1Gi + +metadata: + database: + maxOpenConnections: 200 + resources: + requests: + cpu: "1" + memory: 500Mi + limits: + # cpu: "5" + memory: 2Gi + +event: + resources: + requests: + cpu: 200m + memory: 100Mi + limits: + # cpu: "1" + memory: 500Mi + +observability: + resources: + requests: + cpu: 200m + memory: 100Mi + limits: + # cpu: "1" + memory: 500Mi + +jfconnect: + resources: + requests: + cpu: 100m + memory: 100Mi + limits: + # cpu: "1" + memory: 250Mi + +topology: + resources: + requests: + cpu: 100m + memory: 1000Mi + limits: + memory: 1500Mi + extraEnvironmentVariables: + - name: MALLOC_ARENA_MAX + value: "8" +onemodel: + resources: + requests: + cpu: 225m + memory: 180Mi + limits: + # cpu: "1" + memory: 250Mi + +nginx: + replicaCount: 3 + disableProxyBuffering: true + resources: + requests: + cpu: "4" + memory: "6Gi" + limits: + # cpu: "14" + memory: "8Gi" + +postgresql: + postgresqlExtendedConf: + maxConnections: "5000" + primary: + affinity: + # Require PostgreSQL pod to run on a different node than Artifactory pods + podAntiAffinity: + requiredDuringSchedulingIgnoredDuringExecution: + - labelSelector: + matchExpressions: + - key: app + operator: In + values: + - artifactory + topologyKey: kubernetes.io/hostname + resources: + requests: + memory: 256Gi + cpu: "64" + limits: + memory: 256Gi + # cpu: "128" diff --git a/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/sizing/artifactory-large.yaml b/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/sizing/artifactory-large.yaml new file mode 100644 index 0000000000..ee7a1146ff --- /dev/null +++ b/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/sizing/artifactory-large.yaml @@ -0,0 +1,184 @@ +############################################################################################## +# The large sizing +# This size is intended for large organizations. It can be increased with adding replicas or moving to the xlarge sizing +# [WARNING] Some of the configuration mentioned in this file are taken inside system.yaml +# hence this configuration will be overridden when enabling systemYamlOverride +############################################################################################## +splitServicesToContainers: true +artifactory: + # Enterprise and above licenses are required for setting replicaCount greater than 1. + # Count should be equal or above the total number of licenses available for artifactory. + replicaCount: 3 + + # Require multiple Artifactory pods to run on separate nodes + podAntiAffinity: + type: "hard" + + resources: + requests: + cpu: "2" + memory: 10Gi + limits: + # cpu: "14" + memory: 12Gi + + javaOpts: + other: > + -XX:InitialRAMPercentage=40 + -XX:MaxRAMPercentage=65 + -Dartifactory.async.corePoolSize=80 + -Dartifactory.async.poolMaxQueueSize=20000 + -Dartifactory.http.client.max.total.connections=100 + -Dartifactory.http.client.max.connections.per.route=100 + -Dartifactory.access.client.max.connections=125 + -Dartifactory.metadata.event.operator.threads=4 + -XX:MaxMetaspaceSize=512m + -Djdk.nio.maxCachedBufferSize=524288 + -XX:MaxDirectMemorySize=512m + tomcat: + connector: + maxThreads: 500 + extraConfig: 'acceptCount="800" acceptorThreadCount="2" compression="off" connectionLinger="-1" connectionTimeout="120000" enableLookups="false"' + + database: + maxOpenConnections: 100 + + extraEnvironmentVariables: + - name: MALLOC_ARENA_MAX + value: "8" + - name: SKIP_WAIT_FOR_EXTERNAL_DB + value: "true" + +access: + tomcat: + connector: + maxThreads: 125 + javaOpts: + other: > + -XX:InitialRAMPercentage=20 + -XX:MaxRAMPercentage=60 + database: + maxOpenConnections: 100 + resources: + requests: + cpu: 1 + memory: 1.5Gi + limits: + # cpu: 1 + memory: 2Gi + extraEnvironmentVariables: + - name: MALLOC_ARENA_MAX + value: "8" + - name: SKIP_WAIT_FOR_EXTERNAL_DB + value: "true" + + +router: + resources: + requests: + cpu: 400m + memory: 800Mi + limits: + # cpu: "8" + memory: 2Gi + +frontend: + resources: + requests: + cpu: 200m + memory: 300Mi + limits: + # cpu: "3" + memory: 1Gi + +metadata: + database: + maxOpenConnections: 100 + resources: + requests: + cpu: 200m + memory: 200Mi + limits: + # cpu: "4" + memory: 1Gi + +onemodel: + resources: + requests: + cpu: 200m + memory: 160Mi + limits: + # cpu: "1" + memory: 250Mi + +event: + resources: + requests: + cpu: 100m + memory: 50Mi + limits: + # cpu: 500m + memory: 250Mi + +observability: + resources: + requests: + cpu: 100m + memory: 50Mi + limits: + # cpu: 500m + memory: 250Mi + +jfconnect: + resources: + requests: + cpu: 50m + memory: 100Mi + limits: + # cpu: 500m + memory: 250Mi + +topology: + resources: + requests: + cpu: 100m + memory: 1000Mi + limits: + memory: 1500Mi + extraEnvironmentVariables: + - name: MALLOC_ARENA_MAX + value: "8" + +nginx: + replicaCount: 2 + disableProxyBuffering: true + resources: + requests: + cpu: "1" + memory: "500Mi" + limits: + # cpu: "4" + memory: "1Gi" + +postgresql: + postgresqlExtendedConf: + maxConnections: "600" + primary: + affinity: + # Require PostgreSQL pod to run on a different node than Artifactory pods + podAntiAffinity: + requiredDuringSchedulingIgnoredDuringExecution: + - labelSelector: + matchExpressions: + - key: app + operator: In + values: + - artifactory + topologyKey: kubernetes.io/hostname + resources: + requests: + memory: 64Gi + cpu: "16" + limits: + memory: 64Gi + # cpu: "32" diff --git a/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/sizing/artifactory-medium.yaml b/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/sizing/artifactory-medium.yaml new file mode 100644 index 0000000000..686ac92608 --- /dev/null +++ b/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/sizing/artifactory-medium.yaml @@ -0,0 +1,183 @@ +############################################################################################## +# The medium sizing +# This size is just 2 replicas of the small size. Vertical sizing of all services is not changed +# [WARNING] Some of the configuration mentioned in this file are taken inside system.yaml +# hence this configuration will be overridden when enabling systemYamlOverride +############################################################################################## +splitServicesToContainers: true +artifactory: + # Enterprise and above licenses are required for setting replicaCount greater than 1. + # Count should be equal or above the total number of licenses available for artifactory. + replicaCount: 2 + + # Require multiple Artifactory pods to run on separate nodes + podAntiAffinity: + type: "hard" + + resources: + requests: + cpu: "1" + memory: 4Gi + limits: + # cpu: "10" + memory: 5Gi + + javaOpts: + other: > + -XX:InitialRAMPercentage=40 + -XX:MaxRAMPercentage=70 + -Dartifactory.async.corePoolSize=40 + -Dartifactory.async.poolMaxQueueSize=10000 + -Dartifactory.http.client.max.total.connections=50 + -Dartifactory.http.client.max.connections.per.route=50 + -Dartifactory.access.client.max.connections=75 + -Dartifactory.metadata.event.operator.threads=3 + -XX:MaxMetaspaceSize=512m + -Djdk.nio.maxCachedBufferSize=262144 + -XX:MaxDirectMemorySize=256m + tomcat: + connector: + maxThreads: 300 + extraConfig: 'acceptCount="600" acceptorThreadCount="2" compression="off" connectionLinger="-1" connectionTimeout="120000" enableLookups="false"' + + database: + maxOpenConnections: 50 + + extraEnvironmentVariables: + - name: MALLOC_ARENA_MAX + value: "2" + - name: SKIP_WAIT_FOR_EXTERNAL_DB + value: "true" + +access: + tomcat: + connector: + maxThreads: 75 + javaOpts: + other: > + -XX:InitialRAMPercentage=20 + -XX:MaxRAMPercentage=60 + database: + maxOpenConnections: 50 + resources: + requests: + cpu: 500m + memory: 1.5Gi + limits: + # cpu: 1 + memory: 2Gi + extraEnvironmentVariables: + - name: MALLOC_ARENA_MAX + value: "2" + - name: SKIP_WAIT_FOR_EXTERNAL_DB + value: "true" + +router: + resources: + requests: + cpu: 200m + memory: 500Mi + limits: + # cpu: "2" + memory: 1Gi + +frontend: + resources: + requests: + cpu: 100m + memory: 150Mi + limits: + # cpu: "2" + memory: 250Mi + +metadata: + database: + maxOpenConnections: 50 + resources: + requests: + cpu: 100m + memory: 200Mi + limits: + # cpu: "2" + memory: 1Gi + +onemodel: + resources: + requests: + cpu: 175m + memory: 140Mi + limits: + # cpu: "2" + memory: 1Gi + +event: + resources: + requests: + cpu: 100m + memory: 50Mi + limits: + # cpu: 500m + memory: 250Mi + +observability: + resources: + requests: + cpu: 100m + memory: 50Mi + limits: + # cpu: 500m + memory: 250Mi + +jfconnect: + resources: + requests: + cpu: 50m + memory: 50Mi + limits: + # cpu: 500m + memory: 250Mi + +topology: + resources: + requests: + cpu: 100m + memory: 600Mi + limits: + memory: 900Mi + extraEnvironmentVariables: + - name: MALLOC_ARENA_MAX + value: "2" + +nginx: + replicaCount: 2 + disableProxyBuffering: true + resources: + requests: + cpu: "100m" + memory: "100Mi" + limits: + # cpu: "2" + memory: "500Mi" + +postgresql: + postgresqlExtendedConf: + maxConnections: "200" + primary: + affinity: + # Require PostgreSQL pod to run on a different node than Artifactory pods + podAntiAffinity: + requiredDuringSchedulingIgnoredDuringExecution: + - labelSelector: + matchExpressions: + - key: app + operator: In + values: + - artifactory + topologyKey: kubernetes.io/hostname + resources: + requests: + memory: 32Gi + cpu: "8" + limits: + memory: 32Gi + # cpu: "16" \ No newline at end of file diff --git a/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/sizing/artifactory-small.yaml b/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/sizing/artifactory-small.yaml new file mode 100644 index 0000000000..6d681f602a --- /dev/null +++ b/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/sizing/artifactory-small.yaml @@ -0,0 +1,183 @@ +############################################################################################## +# The small sizing +# This is the size recommended for running Artifactory for small teams +# [WARNING] Some of the configuration mentioned in this file are taken inside system.yaml +# hence this configuration will be overridden when enabling systemYamlOverride +############################################################################################## +splitServicesToContainers: true +artifactory: + # Enterprise and above licenses are required for setting replicaCount greater than 1. + # Count should be equal or above the total number of licenses available for artifactory. + replicaCount: 1 + + # Require multiple Artifactory pods to run on separate nodes + podAntiAffinity: + type: "hard" + + resources: + requests: + cpu: "1" + memory: 4Gi + limits: + # cpu: "10" + memory: 5Gi + + javaOpts: + other: > + -XX:InitialRAMPercentage=40 + -XX:MaxRAMPercentage=70 + -Dartifactory.async.corePoolSize=40 + -Dartifactory.async.poolMaxQueueSize=10000 + -Dartifactory.http.client.max.total.connections=50 + -Dartifactory.http.client.max.connections.per.route=50 + -Dartifactory.access.client.max.connections=75 + -Dartifactory.metadata.event.operator.threads=3 + -XX:MaxMetaspaceSize=512m + -Djdk.nio.maxCachedBufferSize=262144 + -XX:MaxDirectMemorySize=256m + tomcat: + connector: + maxThreads: 300 + extraConfig: 'acceptCount="600" acceptorThreadCount="2" compression="off" connectionLinger="-1" connectionTimeout="120000" enableLookups="false"' + + database: + maxOpenConnections: 50 + + extraEnvironmentVariables: + - name: MALLOC_ARENA_MAX + value: "2" + - name: SKIP_WAIT_FOR_EXTERNAL_DB + value: "true" + +access: + tomcat: + connector: + maxThreads: 75 + javaOpts: + other: > + -XX:InitialRAMPercentage=20 + -XX:MaxRAMPercentage=60 + database: + maxOpenConnections: 50 + resources: + requests: + cpu: 500m + memory: 1.5Gi + limits: + # cpu: 1 + memory: 2Gi + extraEnvironmentVariables: + - name: MALLOC_ARENA_MAX + value: "2" + - name: SKIP_WAIT_FOR_EXTERNAL_DB + value: "true" + +router: + resources: + requests: + cpu: 200m + memory: 500Mi + limits: + # cpu: "2" + memory: 1Gi + +frontend: + resources: + requests: + cpu: 100m + memory: 150Mi + limits: + # cpu: "2" + memory: 250Mi + +metadata: + database: + maxOpenConnections: 50 + resources: + requests: + cpu: 100m + memory: 200Mi + limits: + # cpu: "2" + memory: 1Gi + +onemodel: + resources: + requests: + cpu: 125m + memory: 120Mi + limits: + # cpu: "2" + memory: 1Gi + +event: + resources: + requests: + cpu: 100m + memory: 50Mi + limits: + # cpu: 500m + memory: 250Mi + +observability: + resources: + requests: + cpu: 100m + memory: 50Mi + limits: + # cpu: 500m + memory: 250Mi + +jfconnect: + resources: + requests: + cpu: 50m + memory: 50Mi + limits: + # cpu: 500m + memory: 250Mi + +topology: + resources: + requests: + cpu: 50m + memory: 600Mi + limits: + memory: 900Mi + extraEnvironmentVariables: + - name: MALLOC_ARENA_MAX + value: "2" + +nginx: + replicaCount: 1 + disableProxyBuffering: true + resources: + requests: + cpu: "100m" + memory: "100Mi" + limits: + # cpu: "2" + memory: "500Mi" + +postgresql: + postgresqlExtendedConf: + maxConnections: "100" + primary: + affinity: + # Require PostgreSQL pod to run on a different node than Artifactory pods + podAntiAffinity: + requiredDuringSchedulingIgnoredDuringExecution: + - labelSelector: + matchExpressions: + - key: app + operator: In + values: + - artifactory + topologyKey: kubernetes.io/hostname + resources: + requests: + memory: 16Gi + cpu: "4" + limits: + memory: 16Gi + # cpu: "10" \ No newline at end of file diff --git a/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/sizing/artifactory-xlarge.yaml b/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/sizing/artifactory-xlarge.yaml new file mode 100644 index 0000000000..7fd2601e29 --- /dev/null +++ b/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/sizing/artifactory-xlarge.yaml @@ -0,0 +1,184 @@ +############################################################################################## +# The xlarge sizing +# This size is intended for very large organizations. It can be increased with adding replicas +# [WARNING] Some of the configuration mentioned in this file are taken inside system.yaml +# hence this configuration will be overridden when enabling systemYamlOverride +############################################################################################## +splitServicesToContainers: true +artifactory: + # Enterprise and above licenses are required for setting replicaCount greater than 1. + # Count should be equal or above the total number of licenses available for artifactory. + replicaCount: 4 + + # Require multiple Artifactory pods to run on separate nodes + podAntiAffinity: + type: "hard" + + resources: + requests: + cpu: "2" + memory: 14Gi + limits: + # cpu: "14" + memory: 16Gi + + javaOpts: + other: > + -XX:InitialRAMPercentage=40 + -XX:MaxRAMPercentage=65 + -Dartifactory.async.corePoolSize=160 + -Dartifactory.async.poolMaxQueueSize=50000 + -Dartifactory.http.client.max.total.connections=150 + -Dartifactory.http.client.max.connections.per.route=150 + -Dartifactory.access.client.max.connections=150 + -Dartifactory.metadata.event.operator.threads=5 + -XX:MaxMetaspaceSize=512m + -Djdk.nio.maxCachedBufferSize=1048576 + -XX:MaxDirectMemorySize=1024m + tomcat: + connector: + maxThreads: 600 + extraConfig: 'acceptCount="1200" acceptorThreadCount="2" compression="off" connectionLinger="-1" connectionTimeout="120000" enableLookups="false"' + + database: + maxOpenConnections: 150 + + extraEnvironmentVariables: + - name: MALLOC_ARENA_MAX + value: "16" + - name: SKIP_WAIT_FOR_EXTERNAL_DB + value: "true" + +access: + tomcat: + connector: + maxThreads: 150 + javaOpts: + other: > + -XX:InitialRAMPercentage=20 + -XX:MaxRAMPercentage=60 + database: + maxOpenConnections: 150 + resources: + requests: + cpu: 500m + memory: 2Gi + limits: + # cpu: 1 + memory: 3Gi + extraEnvironmentVariables: + - name: MALLOC_ARENA_MAX + value: "16" + - name: SKIP_WAIT_FOR_EXTERNAL_DB + value: "true" + + +router: + resources: + requests: + cpu: 400m + memory: 1Gi + limits: + # cpu: "8" + memory: 2Gi + +frontend: + resources: + requests: + cpu: 200m + memory: 300Mi + limits: + # cpu: "3" + memory: 1Gi + +metadata: + database: + maxOpenConnections: 150 + resources: + requests: + cpu: 200m + memory: 200Mi + limits: + # cpu: "4" + memory: 1Gi + +onemodel: + resources: + requests: + cpu: 250m + memory: 200Mi + limits: + # cpu: "4" + memory: 1Gi + +event: + resources: + requests: + cpu: 100m + memory: 50Mi + limits: + # cpu: 500m + memory: 250Mi + +observability: + resources: + requests: + cpu: 100m + memory: 50Mi + limits: + # cpu: 500m + memory: 250Mi + +jfconnect: + resources: + requests: + cpu: 50m + memory: 100Mi + limits: + # cpu: 500m + memory: 250Mi + +topology: + resources: + requests: + cpu: 100m + memory: 1000Mi + limits: + memory: 1500Mi + extraEnvironmentVariables: + - name: MALLOC_ARENA_MAX + value: "8" + +nginx: + replicaCount: 2 + disableProxyBuffering: true + resources: + requests: + cpu: "4" + memory: "4Gi" + limits: + # cpu: "12" + memory: "8Gi" + +postgresql: + postgresqlExtendedConf: + maxConnections: "2000" + primary: + affinity: + # Require PostgreSQL pod to run on a different node than Artifactory pods + podAntiAffinity: + requiredDuringSchedulingIgnoredDuringExecution: + - labelSelector: + matchExpressions: + - key: app + operator: In + values: + - artifactory + topologyKey: kubernetes.io/hostname + resources: + requests: + memory: 128Gi + cpu: "32" + limits: + memory: 128Gi + # cpu: "64" \ No newline at end of file diff --git a/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/sizing/artifactory-xsmall.yaml b/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/sizing/artifactory-xsmall.yaml new file mode 100644 index 0000000000..640c06da62 --- /dev/null +++ b/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/sizing/artifactory-xsmall.yaml @@ -0,0 +1,185 @@ +############################################################################################## +# The xsmall sizing +# This is the minimum size recommended for running Artifactory +# [WARNING] Some of the configuration mentioned in this file are taken inside system.yaml +# hence this configuration will be overridden when enabling systemYamlOverride +############################################################################################## +splitServicesToContainers: true +artifactory: + # Enterprise and above licenses are required for setting replicaCount greater than 1. + # Count should be equal or above the total number of licenses available for artifactory. + replicaCount: 1 + + # Require multiple Artifactory pods to run on separate nodes + podAntiAffinity: + type: "hard" + + resources: + requests: + cpu: "1" + memory: 3Gi + limits: + # cpu: "10" + memory: 4Gi + + javaOpts: + other: > + -XX:InitialRAMPercentage=40 + -XX:MaxRAMPercentage=70 + -Dartifactory.async.corePoolSize=10 + -Dartifactory.async.poolMaxQueueSize=2000 + -Dartifactory.http.client.max.total.connections=20 + -Dartifactory.http.client.max.connections.per.route=20 + -Dartifactory.access.client.max.connections=15 + -Dartifactory.metadata.event.operator.threads=2 + -XX:MaxMetaspaceSize=400m + -XX:CompressedClassSpaceSize=96m + -Djdk.nio.maxCachedBufferSize=131072 + -XX:MaxDirectMemorySize=128m + tomcat: + connector: + maxThreads: 50 + extraConfig: 'acceptCount="200" acceptorThreadCount="2" compression="off" connectionLinger="-1" connectionTimeout="120000" enableLookups="false"' + + database: + maxOpenConnections: 15 + + extraEnvironmentVariables: + - name: MALLOC_ARENA_MAX + value: "2" + - name: SKIP_WAIT_FOR_EXTERNAL_DB + value: "true" + +access: + tomcat: + connector: + maxThreads: 15 + javaOpts: + other: > + -XX:InitialRAMPercentage=20 + -XX:MaxRAMPercentage=60 + database: + maxOpenConnections: 15 + resources: + requests: + cpu: 500m + memory: 1.5Gi + limits: + # cpu: 1 + memory: 2Gi + extraEnvironmentVariables: + - name: MALLOC_ARENA_MAX + value: "2" + - name: SKIP_WAIT_FOR_EXTERNAL_DB + value: "true" + +router: + resources: + requests: + cpu: 100m + memory: 200Mi + limits: + # cpu: "2" + memory: 1Gi + +frontend: + resources: + requests: + cpu: 50m + memory: 150Mi + limits: + # cpu: "2" + memory: 250Mi + +metadata: + database: + maxOpenConnections: 15 + resources: + requests: + cpu: 50m + memory: 100Mi + limits: + # cpu: "2" + memory: 1Gi + +event: + resources: + requests: + cpu: 50m + memory: 50Mi + limits: + # cpu: 500m + memory: 250Mi + +onemodel: + resources: + requests: + cpu: 125m + memory: 100Mi + limits: + # cpu: "2" + memory: 500Mi + +observability: + resources: + requests: + cpu: 50m + memory: 50Mi + limits: + # cpu: 500m + memory: 250Mi + +jfconnect: + resources: + requests: + cpu: 50m + memory: 50Mi + limits: + # cpu: 500m + memory: 250Mi + +topology: + resources: + requests: + cpu: 50m + memory: 500Mi + limits: + memory: 800Mi + extraEnvironmentVariables: + - name: MALLOC_ARENA_MAX + value: "2" + +nginx: + replicaCount: 1 + disableProxyBuffering: true + resources: + requests: + cpu: "50m" + memory: "50Mi" + limits: + # cpu: "1" + memory: "250Mi" + +postgresql: + postgresqlExtendedConf: + maxConnections: "50" + primary: + affinity: + # Require PostgreSQL pod to run on a different node than Artifactory pods + podAntiAffinity: + requiredDuringSchedulingIgnoredDuringExecution: + - labelSelector: + matchExpressions: + - key: app + operator: In + values: + - artifactory + topologyKey: kubernetes.io/hostname + resources: + requests: + memory: 8Gi + cpu: "2" + limits: + memory: 8Gi + # cpu: "8" + diff --git a/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/templates/NOTES.txt b/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/templates/NOTES.txt new file mode 100644 index 0000000000..8ebf821a96 --- /dev/null +++ b/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/templates/NOTES.txt @@ -0,0 +1,106 @@ +Congratulations. You have just deployed JFrog Artifactory! +{{- if .Values.artifactory.masterKey }} +{{- if and (not .Values.artifactory.masterKeySecretName) (eq .Values.artifactory.masterKey "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF") }} + + +***************************************** WARNING ****************************************** +* Your Artifactory master key is still set to the provided example: * +* artifactory.masterKey=FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF * +* * +* You should change this to your own generated key: * +* $ export MASTER_KEY=$(openssl rand -hex 32) * +* $ echo ${MASTER_KEY} * +* * +* Pass the created master key to helm with '--set artifactory.masterKey=${MASTER_KEY}' * +* * +* Alternatively, you can use a pre-existing secret with a key called master-key with * +* '--set artifactory.masterKeySecretName=${SECRET_NAME}' * +******************************************************************************************** +{{- end }} +{{- end }} + +{{- if .Values.artifactory.joinKey }} +{{- if eq .Values.artifactory.joinKey "EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE" }} + + +***************************************** WARNING ****************************************** +* Your Artifactory join key is still set to the provided example: * +* artifactory.joinKey=EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE * +* * +* You should change this to your own generated key: * +* $ export JOIN_KEY=$(openssl rand -hex 32) * +* $ echo ${JOIN_KEY} * +* * +* Pass the created master key to helm with '--set artifactory.joinKey=${JOIN_KEY}' * +* * +******************************************************************************************** +{{- end }} +{{- end }} + +{{- if .Values.artifactory.setSecurityContext }} +****************************************** WARNING ********************************************** +* From chart version 107.84.x, `setSecurityContext` has been renamed to `podSecurityContext`, * + please change your values.yaml before upgrade , For more Info , refer to 107.84.x changelog * +************************************************************************************************* +{{- end }} + +{{- if and (or (or (or (or (or ( or ( or ( or (or (or ( or (or .Values.artifactory.masterKeySecretName .Values.global.masterKeySecretName) .Values.systemYamlOverride.existingSecret) (or .Values.artifactory.customCertificates.enabled .Values.global.customCertificates.enabled)) .Values.aws.licenseConfigSecretName) .Values.artifactory.persistence.customBinarystoreXmlSecret) .Values.access.customCertificatesSecretName) .Values.systemYamlOverride.existingSecret) .Values.artifactory.license.secret) .Values.artifactory.userPluginSecrets) (and .Values.artifactory.admin.secret .Values.artifactory.admin.dataKey)) (and .Values.artifactory.persistence.googleStorage.gcpServiceAccount.enabled .Values.artifactory.persistence.googleStorage.gcpServiceAccount.customSecretName)) (or .Values.artifactory.joinKeySecretName .Values.global.joinKeySecretName)) .Values.artifactory.unifiedSecretInstallation }} +****************************************** WARNING ************************************************************************************************** +* The unifiedSecretInstallation flag is currently enabled, which creates the unified secret. The existing secrets will continue as separate secrets.* +* Update the values.yaml with the existing secrets to add them to the unified secret. * +***************************************************************************************************************************************************** +{{- end }} + +1. Get the Artifactory URL by running these commands: + + {{- if .Values.ingress.enabled }} + {{- range .Values.ingress.hosts }} + http://{{ . }} + {{- end }} + + {{- else if contains "NodePort" .Values.nginx.service.type }} + export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ template "artifactory.nginx.fullname" . }}) + export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}") + echo http://$NODE_IP:$NODE_PORT/ + + {{- else if contains "LoadBalancer" .Values.nginx.service.type }} + + NOTE: It may take a few minutes for the LoadBalancer IP to be available. + You can watch the status of the service by running 'kubectl get svc --namespace {{ .Release.Namespace }} -w {{ template "artifactory.nginx.fullname" . }}' + export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ template "artifactory.nginx.fullname" . }} -o jsonpath='{.status.loadBalancer.ingress[0].ip}') + echo http://$SERVICE_IP/ + + {{- else if contains "ClusterIP" .Values.nginx.service.type }} + export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "component={{ .Values.nginx.name }}" -o jsonpath="{.items[0].metadata.name}") + echo http://127.0.0.1:{{ .Values.nginx.externalPortHttp }} + kubectl port-forward --namespace {{ .Release.Namespace }} $POD_NAME {{ .Values.nginx.externalPortHttp }}:{{ .Values.nginx.internalPortHttp }} + + {{- end }} + +2. Open Artifactory in your browser + Default credential for Artifactory: + user: admin + password: password + +{{ if .Values.artifactory.javaOpts.jmx.enabled }} +JMX configuration: +{{- if not (contains "LoadBalancer" .Values.artifactory.service.type) }} +If you want to access JMX from you computer with jconsole, you should set ".Values.artifactory.service.type=LoadBalancer" !!! +{{ end }} + +1. Get the Artifactory service IP: +export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ template "artifactory.fullname" . }} -o jsonpath='{.status.loadBalancer.ingress[0].ip}') + +2. Map the service name to the service IP in /etc/hosts: +sudo sh -c "echo \"${SERVICE_IP} {{ template "artifactory.fullname" . }}\" >> /etc/hosts" + +3. Launch jconsole: +jconsole {{ template "artifactory.fullname" . }}:{{ .Values.artifactory.javaOpts.jmx.port }} +{{- end }} + +{{- if and .Values.nginx.enabled .Values.ingress.hosts }} +***************************************** WARNING ***************************************************************************** +* when nginx is enabled , .Values.ingress.hosts will be deprecated in upcoming releases * +* It is recommended to use nginx.hosts instead ingress.hosts +******************************************************************************************************************************* +{{- end }} diff --git a/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/templates/_helpers.tpl b/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/templates/_helpers.tpl new file mode 100644 index 0000000000..deee308e94 --- /dev/null +++ b/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/templates/_helpers.tpl @@ -0,0 +1,647 @@ +{{/* vim: set filetype=mustache: */}} +{{/* +Expand the name of the chart. +*/}} +{{- define "artifactory.name" -}} +{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}} +{{- end -}} + +{{/* +Expand the name nginx service. +*/}} +{{- define "artifactory.nginx.name" -}} +{{- default .Chart.Name .Values.nginx.name .Values.nameOverride | trunc 63 | trimSuffix "-" -}} +{{- end -}} + +{{/* +Create a default fully qualified app name. +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +*/}} +{{- define "artifactory.fullname" -}} +{{- if .Values.fullnameOverride -}} +{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" -}} +{{- else -}} +{{- $name := default .Chart.Name .Values.nameOverride -}} +{{- if contains $name .Release.Name -}} +{{- .Release.Name | trunc 63 | trimSuffix "-" -}} +{{- else -}} +{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}} +{{- end -}} +{{- end -}} +{{- end -}} + +{{/* +Create a default fully qualified nginx name. +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +*/}} +{{- define "artifactory.nginx.fullname" -}} +{{- $name := default .Chart.Name .Values.nameOverride -}} +{{- printf "%s-%s-%s" .Release.Name $name .Values.nginx.name | trunc 63 | trimSuffix "-" -}} +{{- end -}} + +{{/* +Create the name of the service account to use +*/}} +{{- define "artifactory.serviceAccountName" -}} +{{- if .Values.serviceAccount.create -}} +{{ default (include "artifactory.fullname" .) .Values.serviceAccount.name }} +{{- else -}} +{{ default "default" .Values.serviceAccount.name }} +{{- end -}} +{{- end -}} + +{{/* +Create chart name and version as used by the chart label. +*/}} +{{- define "artifactory.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}} +{{- end -}} + +{{/* +Generate SSL certificates +*/}} +{{- define "artifactory.gen-certs" -}} +{{- $altNames := list ( printf "%s.%s" (include "artifactory.fullname" .) .Release.Namespace ) ( printf "%s.%s.svc" (include "artifactory.fullname" .) .Release.Namespace ) -}} +{{- $ca := genCA "artifactory-ca" 365 -}} +{{- $cert := genSignedCert ( include "artifactory.fullname" . ) nil $altNames 365 $ca -}} +tls.crt: {{ $cert.Cert | b64enc }} +tls.key: {{ $cert.Key | b64enc }} +{{- end -}} + +{{/* +Scheme (http/https) based on Access or Router TLS enabled/disabled +*/}} +{{- define "artifactory.scheme" -}} +{{- if or .Values.access.accessConfig.security.tls .Values.router.tlsEnabled -}} +{{- printf "%s" "https" -}} +{{- else -}} +{{- printf "%s" "http" -}} +{{- end -}} +{{- end -}} + +{{/* +Resolve joinKey value +*/}} +{{- define "artifactory.joinKey" -}} +{{- if .Values.global.joinKey -}} +{{- .Values.global.joinKey -}} +{{- else if .Values.artifactory.joinKey -}} +{{- .Values.artifactory.joinKey -}} +{{- end -}} +{{- end -}} + +{{/* +Resolve jfConnectToken value +*/}} +{{- define "artifactory.jfConnectToken" -}} +{{- .Values.artifactory.jfConnectToken -}} +{{- end -}} + +{{/* +Resolve masterKey value +*/}} +{{- define "artifactory.masterKey" -}} +{{- if .Values.global.masterKey -}} +{{- .Values.global.masterKey -}} +{{- else if .Values.artifactory.masterKey -}} +{{- .Values.artifactory.masterKey -}} +{{- end -}} +{{- end -}} + +{{/* +Resolve joinKeySecretName value +*/}} +{{- define "artifactory.joinKeySecretName" -}} +{{- if .Values.global.joinKeySecretName -}} +{{- .Values.global.joinKeySecretName -}} +{{- else if .Values.artifactory.joinKeySecretName -}} +{{- .Values.artifactory.joinKeySecretName -}} +{{- else -}} +{{ include "artifactory.fullname" . }} +{{- end -}} +{{- end -}} + +{{/* +Resolve jfConnectTokenSecretName value +*/}} +{{- define "artifactory.jfConnectTokenSecretName" -}} +{{- if .Values.artifactory.jfConnectTokenSecretName -}} +{{- .Values.artifactory.jfConnectTokenSecretName -}} +{{- else -}} +{{ include "artifactory.fullname" . }} +{{- end -}} +{{- end -}} + +{{/* +Resolve masterKeySecretName value +*/}} +{{- define "artifactory.masterKeySecretName" -}} +{{- if .Values.global.masterKeySecretName -}} +{{- .Values.global.masterKeySecretName -}} +{{- else if .Values.artifactory.masterKeySecretName -}} +{{- .Values.artifactory.masterKeySecretName -}} +{{- else -}} +{{ include "artifactory.fullname" . }} +{{- end -}} +{{- end -}} + +{{/* +Resolve imagePullSecrets value +*/}} +{{- define "artifactory.imagePullSecrets" -}} +{{- if .Values.global.imagePullSecrets }} +imagePullSecrets: +{{- range .Values.global.imagePullSecrets }} + - name: {{ . }} +{{- end }} +{{- else if .Values.imagePullSecrets }} +imagePullSecrets: +{{- range .Values.imagePullSecrets }} + - name: {{ . }} +{{- end }} +{{- end -}} +{{- end -}} + +{{/* +Resolve customInitContainersBegin value +*/}} +{{- define "artifactory.customInitContainersBegin" -}} +{{- if .Values.global.customInitContainersBegin -}} +{{- .Values.global.customInitContainersBegin -}} +{{- end -}} +{{- if .Values.artifactory.customInitContainersBegin -}} +{{- .Values.artifactory.customInitContainersBegin -}} +{{- end -}} +{{- end -}} + +{{/* +Resolve customInitContainers value +*/}} +{{- define "artifactory.customInitContainers" -}} +{{- if .Values.global.customInitContainers -}} +{{- .Values.global.customInitContainers -}} +{{- end -}} +{{- if .Values.artifactory.customInitContainers -}} +{{- .Values.artifactory.customInitContainers -}} +{{- end -}} +{{- end -}} + +{{/* +Resolve customVolumes value +*/}} +{{- define "artifactory.customVolumes" -}} +{{- if .Values.global.customVolumes -}} +{{- .Values.global.customVolumes -}} +{{- end -}} +{{- if .Values.artifactory.customVolumes -}} +{{- .Values.artifactory.customVolumes -}} +{{- end -}} +{{- end -}} + +{{/* +Resolve customVolumeMounts value +*/}} +{{- define "artifactory.customVolumeMounts" -}} +{{- if .Values.global.customVolumeMounts -}} +{{- .Values.global.customVolumeMounts -}} +{{- end -}} +{{- if .Values.artifactory.customVolumeMounts -}} +{{- .Values.artifactory.customVolumeMounts -}} +{{- end -}} +{{- end -}} + +{{/* +Resolve customSidecarContainers value +*/}} +{{- define "artifactory.customSidecarContainers" -}} +{{- if .Values.global.customSidecarContainers -}} +{{- .Values.global.customSidecarContainers -}} +{{- end -}} +{{- if .Values.artifactory.customSidecarContainers -}} +{{- .Values.artifactory.customSidecarContainers -}} +{{- end -}} +{{- end -}} + +{{/* +Return the proper artifactory chart image names +*/}} +{{- define "artifactory.getImageInfoByValue" -}} +{{- $dot := index . 0 }} +{{- $indexReference := index . 1 }} +{{- $registryName := index $dot.Values $indexReference "image" "registry" -}} +{{- $repositoryName := index $dot.Values $indexReference "image" "repository" -}} +{{- $tag := default $dot.Chart.AppVersion (index $dot.Values $indexReference "image" "tag") | toString -}} +{{- if $dot.Values.global }} + {{- if and $dot.Values.splitServicesToContainers $dot.Values.global.versions.router (eq $indexReference "router") }} + {{- $tag = $dot.Values.global.versions.router | toString -}} + {{- end -}} + {{- if and $dot.Values.global.versions.initContainers (eq $indexReference "initContainers") }} + {{- $tag = $dot.Values.global.versions.initContainers | toString -}} + {{- end -}} + {{- if and $dot.Values.global.versions.artifactory (or (eq $indexReference "artifactory") (eq $indexReference "nginx") ) }} + {{- $tag = $dot.Values.global.versions.artifactory | toString -}} + {{- end -}} + {{- if and $dot.Values.global.versions.rtfs (eq $indexReference "rtfs") }} + {{- $tag = $dot.Values.global.versions.rtfs | toString -}} + {{- end -}} + {{- if $dot.Values.global.imageRegistry }} + {{- printf "%s/%s:%s" $dot.Values.global.imageRegistry $repositoryName $tag -}} + {{- else -}} + {{- printf "%s/%s:%s" $registryName $repositoryName $tag -}} + {{- end -}} +{{- else -}} + {{- printf "%s/%s:%s" $registryName $repositoryName $tag -}} +{{- end -}} +{{- end -}} + +{{/* +Return the proper artifactory app version +*/}} +{{- define "artifactory.app.version" -}} +{{- $tag := (splitList ":" ((include "artifactory.getImageInfoByValue" (list . "artifactory" )))) | last | toString -}} +{{- printf "%s" $tag -}} +{{- end -}} + +{{/* +Custom certificate copy command +*/}} +{{- define "artifactory.copyCustomCerts" -}} +echo "Copy custom certificates to {{ .Values.artifactory.persistence.mountPath }}/etc/security/keys/trusted"; +mkdir -p {{ .Values.artifactory.persistence.mountPath }}/etc/security/keys/trusted; +for file in $(ls -1 /tmp/certs/* | grep -v .key | grep -v ":" | grep -v grep); do if [ -f "${file}" ]; then cp -v ${file} {{ .Values.artifactory.persistence.mountPath }}/etc/security/keys/trusted; fi done; +if [ -f {{ .Values.artifactory.persistence.mountPath }}/etc/security/keys/trusted/tls.crt ]; then mv -v {{ .Values.artifactory.persistence.mountPath }}/etc/security/keys/trusted/tls.crt {{ .Values.artifactory.persistence.mountPath }}/etc/security/keys/trusted/ca.crt; fi; +{{- end -}} + +{{/* +Circle of trust certificates copy command +*/}} +{{- define "artifactory.copyCircleOfTrustCertsCerts" -}} +echo "Copy circle of trust certificates to {{ .Values.artifactory.persistence.mountPath }}/etc/access/keys/trusted"; +mkdir -p {{ .Values.artifactory.persistence.mountPath }}/etc/access/keys/trusted; +for file in $(ls -1 /tmp/circleoftrustcerts/* | grep -v .key | grep -v ":" | grep -v grep); do if [ -f "${file}" ]; then cp -v ${file} {{ .Values.artifactory.persistence.mountPath }}/etc/access/keys/trusted; fi done; +{{- end -}} + +{{/* +Resolve requiredServiceTypes value +*/}} +{{- define "artifactory.router.requiredServiceTypes" -}} +{{- $requiredTypes := "jfrt,jfac" -}} +{{- if not .Values.access.enabled -}} + {{- $requiredTypes = "jfrt" -}} +{{- end -}} +{{- if .Values.observability.enabled -}} + {{- $requiredTypes = printf "%s,%s" $requiredTypes "jfob" -}} +{{- end -}} +{{- if .Values.metadata.enabled -}} + {{- $requiredTypes = printf "%s,%s" $requiredTypes "jfmd" -}} +{{- end -}} +{{- if .Values.event.enabled -}} + {{- $requiredTypes = printf "%s,%s" $requiredTypes "jfevt" -}} +{{- end -}} +{{- if .Values.frontend.enabled -}} + {{- $requiredTypes = printf "%s,%s" $requiredTypes "jffe" -}} +{{- end -}} +{{- if .Values.jfconnect.enabled -}} + {{- $requiredTypes = printf "%s,%s" $requiredTypes "jfcon" -}} +{{- end -}} +{{- if .Values.evidence.enabled -}} + {{- $requiredTypes = printf "%s,%s" $requiredTypes "jfevd" -}} +{{- end -}} +{{- if .Values.topology.enabled -}} + {{- $requiredTypes = printf "%s,%s" $requiredTypes "jftpl" -}} +{{- end -}} +{{- if .Values.mc.enabled -}} + {{- $requiredTypes = printf "%s,%s" $requiredTypes "jfmc" -}} +{{- end -}} +{{- if .Values.onemodel.enabled -}} + {{- $requiredTypes = printf "%s,%s" $requiredTypes "jfomr" -}} +{{- end -}} +{{- $requiredTypes -}} +{{- end -}} + +{{/* +Check if the image is artifactory pro or not +*/}} +{{- define "artifactory.isImageProType" -}} +{{- if not (regexMatch "^.*(oss|cpp-ce|jcr).*$" .Values.artifactory.image.repository) -}} +{{ true }} +{{- else -}} +{{ false }} +{{- end -}} +{{- end -}} + +{{/* +Check if the artifactory is using derby database +*/}} +{{- define "artifactory.isUsingDerby" -}} +{{- if and (eq (default "derby" .Values.database.type) "derby") (not .Values.postgresql.enabled) -}} +{{ true }} +{{- else -}} +{{ false }} +{{- end -}} +{{- end -}} + +{{/* +nginx scheme (http/https) +*/}} +{{- define "nginx.scheme" -}} +{{- if .Values.nginx.http.enabled -}} +{{- printf "%s" "http" -}} +{{- else -}} +{{- printf "%s" "https" -}} +{{- end -}} +{{- end -}} + +{{/* +nginx command +*/}} +{{- define "nginx.command" -}} +{{- if .Values.nginx.customCommand }} +{{ toYaml .Values.nginx.customCommand }} +{{- end }} +{{- end -}} + +{{/* +nginx port (8080/8443) based on http/https enabled +*/}} +{{- define "nginx.port" -}} +{{- if .Values.nginx.http.enabled -}} +{{- .Values.nginx.http.internalPort -}} +{{- else -}} +{{- .Values.nginx.https.internalPort -}} +{{- end -}} +{{- end -}} + +{{/* +Resolve customInitContainers value +*/}} +{{- define "artifactory.nginx.customInitContainers" -}} +{{- if .Values.nginx.customInitContainers -}} +{{- .Values.nginx.customInitContainers -}} +{{- end -}} +{{- end -}} + +{{/* +Resolve customVolumes value +*/}} +{{- define "artifactory.nginx.customVolumes" -}} +{{- if .Values.nginx.customVolumes -}} +{{- .Values.nginx.customVolumes -}} +{{- end -}} +{{- end -}} + + +{{/* +Resolve customVolumeMounts nginx value +*/}} +{{- define "artifactory.nginx.customVolumeMounts" -}} +{{- if .Values.nginx.customVolumeMounts -}} +{{- .Values.nginx.customVolumeMounts -}} +{{- end -}} +{{- end -}} + + +{{/* +Resolve customSidecarContainers value +*/}} +{{- define "artifactory.nginx.customSidecarContainers" -}} +{{- if .Values.nginx.customSidecarContainers -}} +{{- .Values.nginx.customSidecarContainers -}} +{{- end -}} +{{- end -}} + +{{/* +Resolve Artifactory pod node selector value +*/}} +{{- define "artifactory.nodeSelector" -}} +nodeSelector: +{{- if .Values.global.nodeSelector }} +{{ toYaml .Values.global.nodeSelector | indent 2 }} +{{- else if .Values.artifactory.nodeSelector }} +{{ toYaml .Values.artifactory.nodeSelector | indent 2 }} +{{- end -}} +{{- end -}} + +{{/* +Resolve Nginx pods node selector value +*/}} +{{- define "nginx.nodeSelector" -}} +nodeSelector: +{{- if .Values.global.nodeSelector }} +{{ toYaml .Values.global.nodeSelector | indent 2 }} +{{- else if .Values.nginx.nodeSelector }} +{{ toYaml .Values.nginx.nodeSelector | indent 2 }} +{{- end -}} +{{- end -}} + +{{/* +Resolve unifiedCustomSecretVolumeName value +*/}} +{{- define "artifactory.unifiedCustomSecretVolumeName" -}} +{{- printf "%s-%s" (include "artifactory.name" .) ("unified-secret-volume") | trunc 63 -}} +{{- end -}} + +{{/* +Check the Duplication of volume names for secrets. If unifiedSecretInstallation is enabled then the method is checking for volume names, +if the volume exists in customVolume then an extra volume with the same name will not be getting added in unifiedSecretInstallation case. +*/}} +{{- define "artifactory.checkDuplicateUnifiedCustomVolume" -}} +{{- if or .Values.global.customVolumes .Values.artifactory.customVolumes -}} +{{- $val := (tpl (include "artifactory.customVolumes" .) .) | toJson -}} +{{- contains (include "artifactory.unifiedCustomSecretVolumeName" .) $val | toString -}} +{{- else -}} +{{- printf "%s" "false" -}} +{{- end -}} +{{- end -}} + +{{/* +Calculate the systemYaml from structured and unstructured text input +*/}} +{{- define "artifactory.finalSystemYaml" -}} +{{ tpl (mergeOverwrite (include "artifactory.systemYaml" . | fromYaml) .Values.artifactory.extraSystemYaml | toYaml) . }} +{{- end -}} + +{{/* +Calculate the systemYaml from the unstructured text input +*/}} +{{- define "artifactory.systemYaml" -}} +{{ include (print $.Template.BasePath "/_system-yaml-render.tpl") . }} +{{- end -}} + +{{/* +Metrics enabled +*/}} +{{- define "metrics.enabled" -}} + metrics: + enabled: true +{{- end }} + +{{/* +Resolve unified secret prepend release name +*/}} +{{- define "artifactory.unifiedSecretPrependReleaseName" -}} +{{- if .Values.artifactory.unifiedSecretPrependReleaseName }} +{{- printf "%s" (include "artifactory.fullname" .) -}} +{{- else }} +{{- printf "%s" (include "artifactory.name" .) -}} +{{- end }} +{{- end }} + +{{/* +Resolve Service prepend release name +*/}} +{{- define "artifactory.servicePrependReleaseName" -}} +{{- if .Values.artifactory.servicePrependReleaseName }} +{{- printf "%s" (include "artifactory.fullname" .) -}} +{{- else }} +{{- printf "%s" (include "artifactory.name" .) -}} +{{- end }} +{{- end }} + +{{/* +Resolve artifactory metrics +*/}} +{{- define "artifactory.metrics" -}} +{{- if .Values.artifactory.openMetrics -}} +{{- if .Values.artifactory.openMetrics.enabled -}} +{{ include "metrics.enabled" . }} +{{- if .Values.artifactory.openMetrics.filebeat }} +{{- if .Values.artifactory.openMetrics.filebeat.enabled }} +{{ include "metrics.enabled" . }} + filebeat: +{{ tpl (.Values.artifactory.openMetrics.filebeat | toYaml) . | indent 6 }} +{{- end -}} +{{- end -}} +{{- end -}} +{{- else if .Values.artifactory.metrics -}} +{{- if .Values.artifactory.metrics.enabled -}} +{{ include "metrics.enabled" . }} +{{- if .Values.artifactory.metrics.filebeat }} +{{- if .Values.artifactory.metrics.filebeat.enabled }} +{{ include "metrics.enabled" . }} + filebeat: +{{ tpl (.Values.artifactory.metrics.filebeat | toYaml) . | indent 6 }} +{{- end -}} +{{- end -}} +{{- end -}} +{{- end -}} +{{- end -}} + +{{/* +Resolve nginx hosts value +*/}} +{{- define "artifactory.nginx.hosts" -}} +{{- if .Values.ingress.hosts }} +{{- range .Values.ingress.hosts -}} + {{- if contains "." . -}} + {{ "" | indent 0 }} ~(?.+)\.{{ . }} + {{- end -}} +{{- end -}} +{{- else if .Values.nginx.hosts }} +{{- range .Values.nginx.hosts -}} + {{- if contains "." . -}} + {{ "" | indent 0 }} ~(?.+)\.{{ . }} + {{- end -}} +{{- end -}} +{{- end -}} +{{- end -}} + +{{/* +Create a default fully qualified grpc ingress name. +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +*/}} +{{- define "artifactory.ingressGrpc.fullname" -}} +{{- printf "%s-%s" (include "artifactory.fullname" .) .Values.ingressGrpc.name | trunc 63 | trimSuffix "-" -}} +{{- end -}} + +{{/* +Create a default fully qualified grpc service name. +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +*/}} +{{- define "artifactory.serviceGrpc.fullname" -}} +{{- printf "%s-%s" (include "artifactory.fullname" .) .Values.artifactory.serviceGrpc.name | trunc 63 | trimSuffix "-" -}} +{{- end -}} + +{{/* +Create a default fully qualified app name. +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +*/}} +{{- define "rtfs.fullname" -}} +{{- if .Values.fullnameOverride -}} +{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" -}} +{{- else -}} +{{- $name := default .Chart.Name .Values.nameOverride -}} +{{- if contains $name .Release.Name -}} +{{- printf "%s-%s" (.Release.Name | trunc 63 | trimSuffix "-") .Values.rtfs.name -}} +{{- else -}} +{{- printf "%s-%s-%s" (.Release.Name | trunc 63 | trimSuffix "-") $name .Values.rtfs.name -}} +{{- end -}} +{{- end -}} +{{- end -}} + +{{/* +Resolve customVolumes value +*/}} +{{- define "artifactory.rtfs.customVolumes" -}} +{{- if .Values.rtfs.customVolumes -}} +{{- .Values.rtfs.customVolumes -}} +{{- end -}} +{{- end -}} + +{{/* +Rtfs command +*/}} +{{- define "rtfs.command" -}} +{{- if .Values.rtfs.customCommand }} +{{ toYaml .Values.rtfs.customCommand }} +{{- end }} +{{- end -}} + +{{/* + Resolve jfrogUrl value +*/}} +{{- define "rtfs.jfrogUrl" -}} +{{- if .Values.global.jfrogUrl -}} +{{- .Values.global.jfrogUrl -}} +{{- else if .Values.rtfs.jfrogUrl -}} +{{- .Values.rtfs.jfrogUrl -}} +{{- else -}} +{{- printf "http://%s:8082" (include "artifactory.fullname" .) -}} +{{- end -}} +{{- end -}} + +{{/* +Resolve customVolumeMounts rtfs value +*/}} +{{- define "artifactory.rtfs.customVolumeMounts" -}} +{{- if .Values.rtfs.customVolumeMounts -}} +{{- .Values.rtfs.customVolumeMounts -}} +{{- end -}} +{{- end -}} + +{{/* +Resolve RTFS customSidecarContainers value +*/}} +{{- define "artifactory.rtfs.customSidecarContainers" -}} +{{- if .Values.rtfs.customSidecarContainers -}} +{{- .Values.rtfs.customSidecarContainers -}} +{{- end -}} +{{- end -}} + +{{/* +Resolve RTFS customInitContainers value +*/}} +{{- define "artifactory.rtfs.customInitContainers" -}} +{{- if .Values.rtfs.customInitContainers -}} +{{- .Values.rtfs.customInitContainers -}} +{{- end -}} +{{- end -}} + +{{/* +Resolve RTFS autoscalling metrics +*/}} +{{- define "rtfs.metrics" -}} +{{- if .Values.rtfs.autoscaling.metrics -}} +{{- .Values.rtfs.autoscaling.metrics -}} +{{- end -}} +{{- end -}} \ No newline at end of file diff --git a/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/templates/_system-yaml-render.tpl b/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/templates/_system-yaml-render.tpl new file mode 100644 index 0000000000..deaa773ea9 --- /dev/null +++ b/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/templates/_system-yaml-render.tpl @@ -0,0 +1,5 @@ +{{- if .Values.artifactory.systemYaml -}} +{{- tpl .Values.artifactory.systemYaml . -}} +{{- else -}} +{{ (tpl ( $.Files.Get "files/system.yaml" ) .) }} +{{- end -}} \ No newline at end of file diff --git a/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/templates/additional-resources.yaml b/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/templates/additional-resources.yaml new file mode 100644 index 0000000000..c4d06f08ad --- /dev/null +++ b/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/templates/additional-resources.yaml @@ -0,0 +1,3 @@ +{{ if .Values.additionalResources }} +{{ tpl .Values.additionalResources . }} +{{- end -}} diff --git a/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/templates/admin-bootstrap-creds.yaml b/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/templates/admin-bootstrap-creds.yaml new file mode 100644 index 0000000000..eb2d613c6c --- /dev/null +++ b/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/templates/admin-bootstrap-creds.yaml @@ -0,0 +1,15 @@ +{{- if not (and .Values.artifactory.admin.secret .Values.artifactory.admin.dataKey) }} +{{- if and .Values.artifactory.admin.password (not .Values.artifactory.unifiedSecretInstallation) }} +kind: Secret +apiVersion: v1 +metadata: + name: {{ template "artifactory.fullname" . }}-bootstrap-creds + labels: + app: {{ template "artifactory.name" . }} + chart: {{ template "artifactory.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} +data: + bootstrap.creds: {{ (printf "%s@%s=%s" .Values.artifactory.admin.username .Values.artifactory.admin.ip .Values.artifactory.admin.password) | b64enc }} +{{- end }} +{{- end }} diff --git a/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/templates/artifactory-access-config.yaml b/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/templates/artifactory-access-config.yaml new file mode 100644 index 0000000000..4fcf85d94c --- /dev/null +++ b/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/templates/artifactory-access-config.yaml @@ -0,0 +1,15 @@ +{{- if and .Values.access.accessConfig (not .Values.artifactory.unifiedSecretInstallation) }} +apiVersion: v1 +kind: Secret +metadata: + name: {{ template "artifactory.fullname" . }}-access-config + labels: + app: {{ template "artifactory.name" . }} + chart: {{ template "artifactory.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} +type: Opaque +stringData: + access.config.patch.yml: | +{{ tpl (toYaml .Values.access.accessConfig) . | indent 4 }} +{{- end }} diff --git a/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/templates/artifactory-binarystore-secret.yaml b/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/templates/artifactory-binarystore-secret.yaml new file mode 100644 index 0000000000..6b721dd4c7 --- /dev/null +++ b/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/templates/artifactory-binarystore-secret.yaml @@ -0,0 +1,18 @@ +{{- if and (not .Values.artifactory.persistence.customBinarystoreXmlSecret) (not .Values.artifactory.unifiedSecretInstallation) }} +kind: Secret +apiVersion: v1 +metadata: + name: {{ template "artifactory.fullname" . }}-binarystore + labels: + app: {{ template "artifactory.name" . }} + chart: {{ template "artifactory.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} +stringData: + binarystore.xml: |- +{{- if .Values.artifactory.persistence.binarystoreXml }} +{{ tpl .Values.artifactory.persistence.binarystoreXml . | indent 4 }} +{{- else }} +{{ tpl ( .Files.Get "files/binarystore.xml" ) . | indent 4 }} +{{- end }} +{{- end }} diff --git a/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/templates/artifactory-configmaps.yaml b/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/templates/artifactory-configmaps.yaml new file mode 100644 index 0000000000..359fa07d25 --- /dev/null +++ b/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/templates/artifactory-configmaps.yaml @@ -0,0 +1,13 @@ +{{ if .Values.artifactory.configMaps }} +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ template "artifactory.fullname" . }}-configmaps + labels: + app: {{ template "artifactory.fullname" . }} + chart: {{ .Chart.Name }}-{{ .Chart.Version }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} +data: +{{ tpl .Values.artifactory.configMaps . | indent 2 }} +{{ end -}} \ No newline at end of file diff --git a/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/templates/artifactory-custom-secrets.yaml b/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/templates/artifactory-custom-secrets.yaml new file mode 100644 index 0000000000..4b73e79fc3 --- /dev/null +++ b/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/templates/artifactory-custom-secrets.yaml @@ -0,0 +1,19 @@ +{{- if and .Values.artifactory.customSecrets (not .Values.artifactory.unifiedSecretInstallation) }} +{{- range .Values.artifactory.customSecrets }} +--- +apiVersion: v1 +kind: Secret +metadata: + name: {{ template "artifactory.fullname" $ }}-{{ .name }} + labels: + app: "{{ template "artifactory.name" $ }}" + chart: "{{ template "artifactory.chart" $ }}" + component: "{{ $.Values.artifactory.name }}" + heritage: {{ $.Release.Service | quote }} + release: {{ $.Release.Name | quote }} +type: Opaque +stringData: + {{ .key }}: | +{{ .data | indent 4 -}} +{{- end -}} +{{- end -}} diff --git a/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/templates/artifactory-database-secrets.yaml b/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/templates/artifactory-database-secrets.yaml new file mode 100644 index 0000000000..f98d422e95 --- /dev/null +++ b/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/templates/artifactory-database-secrets.yaml @@ -0,0 +1,24 @@ +{{- if and (not .Values.database.secrets) (not .Values.postgresql.enabled) (not .Values.artifactory.unifiedSecretInstallation) }} +{{- if or .Values.database.url .Values.database.user .Values.database.password }} +apiVersion: v1 +kind: Secret +metadata: + name: {{ template "artifactory.fullname" . }}-database-creds + labels: + app: {{ template "artifactory.name" . }} + chart: {{ template "artifactory.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} +type: Opaque +data: + {{- with .Values.database.url }} + db-url: {{ tpl . $ | b64enc | quote }} + {{- end }} + {{- with .Values.database.user }} + db-user: {{ tpl . $ | b64enc | quote }} + {{- end }} + {{- with .Values.database.password }} + db-password: {{ tpl . $ | b64enc | quote }} + {{- end }} +{{- end }} +{{- end }} diff --git a/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/templates/artifactory-gcp-credentials-secret.yaml b/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/templates/artifactory-gcp-credentials-secret.yaml new file mode 100644 index 0000000000..72dee6bb84 --- /dev/null +++ b/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/templates/artifactory-gcp-credentials-secret.yaml @@ -0,0 +1,16 @@ +{{- if not .Values.artifactory.persistence.googleStorage.gcpServiceAccount.customSecretName }} +{{- if and (.Values.artifactory.persistence.googleStorage.gcpServiceAccount.enabled) (not .Values.artifactory.unifiedSecretInstallation) }} +kind: Secret +apiVersion: v1 +metadata: + name: {{ template "artifactory.fullname" . }}-gcpcreds + labels: + app: {{ template "artifactory.name" . }} + chart: {{ template "artifactory.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} +stringData: + gcp.credentials.json: |- +{{ tpl .Values.artifactory.persistence.googleStorage.gcpServiceAccount.config . | indent 4 }} +{{- end }} +{{- end }} \ No newline at end of file diff --git a/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/templates/artifactory-hpa.yaml b/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/templates/artifactory-hpa.yaml new file mode 100644 index 0000000000..01f8a9fb70 --- /dev/null +++ b/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/templates/artifactory-hpa.yaml @@ -0,0 +1,29 @@ +{{- if .Values.autoscaling.enabled }} + {{- if semverCompare ">=v1.23.0-0" .Capabilities.KubeVersion.Version }} +apiVersion: autoscaling/v2 + {{- else }} +apiVersion: autoscaling/v2beta2 + {{- end }} +kind: HorizontalPodAutoscaler +metadata: + labels: + app: {{ template "artifactory.name" . }} + chart: {{ template "artifactory.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + name: {{ template "artifactory.fullname" . }} +spec: + scaleTargetRef: + apiVersion: apps/v1 + kind: StatefulSet + name: {{ template "artifactory.fullname" . }} + minReplicas: {{ .Values.autoscaling.minReplicas }} + maxReplicas: {{ .Values.autoscaling.maxReplicas }} + metrics: + - type: Resource + resource: + name: cpu + target: + type: Utilization + averageUtilization: {{ .Values.autoscaling.targetCPUUtilizationPercentage }} +{{- end }} diff --git a/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/templates/artifactory-installer-info.yaml b/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/templates/artifactory-installer-info.yaml new file mode 100644 index 0000000000..cfb95b67d3 --- /dev/null +++ b/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/templates/artifactory-installer-info.yaml @@ -0,0 +1,16 @@ +kind: ConfigMap +apiVersion: v1 +metadata: + name: {{ template "artifactory.fullname" . }}-installer-info + labels: + app: {{ template "artifactory.name" . }} + chart: {{ template "artifactory.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} +data: + installer-info.json: | +{{- if .Values.installerInfo -}} +{{- tpl .Values.installerInfo . | nindent 4 -}} +{{- else -}} +{{ (tpl ( .Files.Get "files/installer-info.json" | nindent 4 ) .) }} +{{- end -}} diff --git a/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/templates/artifactory-license-secret.yaml b/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/templates/artifactory-license-secret.yaml new file mode 100644 index 0000000000..dda7340338 --- /dev/null +++ b/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/templates/artifactory-license-secret.yaml @@ -0,0 +1,16 @@ +{{ if and (not .Values.artifactory.unifiedSecretInstallation) (not .Values.artifactory.license.secret) }} +{{- with .Values.artifactory.license.licenseKey }} +apiVersion: v1 +kind: Secret +metadata: + name: {{ template "artifactory.fullname" $ }}-license + labels: + app: {{ template "artifactory.name" $ }} + chart: {{ template "artifactory.chart" $ }} + heritage: {{ $.Release.Service }} + release: {{ $.Release.Name }} +type: Opaque +data: + artifactory.lic: {{ . | b64enc | quote }} +{{- end }} +{{- end }} \ No newline at end of file diff --git a/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/templates/artifactory-migration-scripts.yaml b/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/templates/artifactory-migration-scripts.yaml new file mode 100644 index 0000000000..4b1ba40276 --- /dev/null +++ b/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/templates/artifactory-migration-scripts.yaml @@ -0,0 +1,18 @@ +{{- if .Values.artifactory.migration.enabled }} +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ template "artifactory.fullname" . }}-migration-scripts + labels: + app: {{ template "artifactory.name" . }} + chart: {{ template "artifactory.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} +data: + migrate.sh: | +{{ .Files.Get "files/migrate.sh" | indent 4 }} + migrationHelmInfo.yaml: | +{{ .Files.Get "files/migrationHelmInfo.yaml" | indent 4 }} + migrationStatus.sh: | +{{ .Files.Get "files/migrationStatus.sh" | indent 4 }} +{{- end }} \ No newline at end of file diff --git a/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/templates/artifactory-networkpolicy.yaml b/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/templates/artifactory-networkpolicy.yaml new file mode 100644 index 0000000000..d24203dc99 --- /dev/null +++ b/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/templates/artifactory-networkpolicy.yaml @@ -0,0 +1,34 @@ +{{- range .Values.networkpolicy }} +apiVersion: networking.k8s.io/v1 +kind: NetworkPolicy +metadata: + name: {{ template "artifactory.fullname" $ }}-{{ .name }}-networkpolicy + labels: + app: {{ template "artifactory.name" $ }} + chart: {{ template "artifactory.chart" $ }} + release: {{ $.Release.Name }} + heritage: {{ $.Release.Service }} +spec: +{{- if .podSelector }} + podSelector: +{{ .podSelector | toYaml | trimSuffix "\n" | indent 4 -}} +{{ else }} + podSelector: {} +{{- end }} + policyTypes: + {{- if .ingress }} + - Ingress + {{- end }} + {{- if .egress }} + - Egress + {{- end }} +{{- if .ingress }} + ingress: +{{ .ingress | toYaml | trimSuffix "\n" | indent 2 -}} +{{- end }} +{{- if .egress }} + egress: +{{ .egress | toYaml | trimSuffix "\n" | indent 2 -}} +{{- end }} +--- +{{- end -}} \ No newline at end of file diff --git a/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/templates/artifactory-nfs-pvc.yaml b/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/templates/artifactory-nfs-pvc.yaml new file mode 100644 index 0000000000..75d6d0c53d --- /dev/null +++ b/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/templates/artifactory-nfs-pvc.yaml @@ -0,0 +1,101 @@ +{{- if eq .Values.artifactory.persistence.type "nfs" }} +### Artifactory HA data +apiVersion: v1 +kind: PersistentVolume +metadata: + name: {{ template "artifactory.fullname" . }}-data-pv + labels: + app: {{ template "artifactory.name" . }} + chart: {{ template "artifactory.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + id: {{ template "artifactory.name" . }}-data-pv + type: nfs-volume +spec: + {{- if .Values.artifactory.persistence.nfs.mountOptions }} + mountOptions: +{{ toYaml .Values.artifactory.persistence.nfs.mountOptions | indent 4 }} + {{- end }} + capacity: + storage: {{ .Values.artifactory.persistence.nfs.capacity }} + accessModes: + - ReadWriteOnce + persistentVolumeReclaimPolicy: Retain + nfs: + server: {{ .Values.artifactory.persistence.nfs.ip }} + path: "{{ .Values.artifactory.persistence.nfs.haDataMount }}" + readOnly: false +--- +kind: PersistentVolumeClaim +apiVersion: v1 +metadata: + name: {{ template "artifactory.fullname" . }}-data-pvc + labels: + app: {{ template "artifactory.name" . }} + chart: {{ template "artifactory.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + type: nfs-volume +spec: + accessModes: + - ReadWriteOnce + storageClassName: "" + resources: + requests: + storage: {{ .Values.artifactory.persistence.nfs.capacity }} + selector: + matchLabels: + id: {{ template "artifactory.name" . }}-data-pv + app: {{ template "artifactory.name" . }} + release: {{ .Release.Name }} +--- +### Artifactory HA backup +apiVersion: v1 +kind: PersistentVolume +metadata: + name: {{ template "artifactory.fullname" . }}-backup-pv + labels: + app: {{ template "artifactory.name" . }} + chart: {{ template "artifactory.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + id: {{ template "artifactory.name" . }}-backup-pv + type: nfs-volume +spec: + {{- if .Values.artifactory.persistence.nfs.mountOptions }} + mountOptions: +{{ toYaml .Values.artifactory.persistence.nfs.mountOptions | indent 4 }} + {{- end }} + capacity: + storage: {{ .Values.artifactory.persistence.nfs.capacity }} + accessModes: + - ReadWriteOnce + persistentVolumeReclaimPolicy: Retain + nfs: + server: {{ .Values.artifactory.persistence.nfs.ip }} + path: "{{ .Values.artifactory.persistence.nfs.haBackupMount }}" + readOnly: false +--- +kind: PersistentVolumeClaim +apiVersion: v1 +metadata: + name: {{ template "artifactory.fullname" . }}-backup-pvc + labels: + app: {{ template "artifactory.name" . }} + chart: {{ template "artifactory.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + type: nfs-volume +spec: + accessModes: + - ReadWriteOnce + storageClassName: "" + resources: + requests: + storage: {{ .Values.artifactory.persistence.nfs.capacity }} + selector: + matchLabels: + id: {{ template "artifactory.name" . }}-backup-pv + app: {{ template "artifactory.name" . }} + release: {{ .Release.Name }} +{{- end }} diff --git a/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/templates/artifactory-pdb.yaml b/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/templates/artifactory-pdb.yaml new file mode 100644 index 0000000000..68876d23b0 --- /dev/null +++ b/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/templates/artifactory-pdb.yaml @@ -0,0 +1,24 @@ +{{- if .Values.artifactory.minAvailable -}} +{{- if semverCompare "= 107.79.x), just set databaseUpgradeReady=true \n" .Values.databaseUpgradeReady | quote }} +{{- end }} +{{- if and (eq (include "artifactory.isUsingDerby" .) "true") (gt (.Values.artifactory.replicaCount | int64) 1) }} + {{- fail "Derby database is not supported in HA mode" }} +{{- end }} +{{- if .Values.artifactory.postStartCommand }} + {{- fail ".Values.artifactory.postStartCommand is not supported and should be replaced with .Values.artifactory.lifecycle.postStart.exec.command" }} +{{- end }} +{{- if eq .Values.artifactory.persistence.type "aws-s3" }} + {{- fail "\nPersistence storage type 'aws-s3' is deprecated and is not supported and should be replaced with 'aws-s3-v3'" }} +{{- end }} +{{- if or .Values.artifactory.persistence.googleStorage.identity .Values.artifactory.persistence.googleStorage.credential }} + {{- fail "\nGCP Bucket Authentication with Identity and Credential is deprecated" }} +{{- end }} +{{- if (eq (.Values.artifactory.setSecurityContext | toString) "false" ) }} + {{- fail "\n You need to set security context at the pod level. .Values.artifactory.setSecurityContext is no longer supported. Replace it with .Values.artifactory.podSecurityContext" }} +{{- end }} +{{- if or .Values.artifactory.uid .Values.artifactory.gid }} +{{- if or (not (eq (.Values.artifactory.uid | toString) "1030" )) (not (eq (.Values.artifactory.gid | toString) "1030" )) }} + {{- fail "\n .Values.artifactory.uid and .Values.artifactory.gid are no longer supported. You need to set these values at the pod security context level. Replace them with .Values.artifactory.podSecurityContext.runAsUser .Values.artifactory.podSecurityContext.runAsGroup and .Values.artifactory.podSecurityContext.fsGroup" }} +{{- end }} +{{- end }} +{{- if or .Values.artifactory.fsGroupChangePolicy .Values.artifactory.seLinuxOptions }} + {{- fail "\n .Values.artifactory.fsGroupChangePolicy and .Values.artifactory.seLinuxOptions are no longer supported. You need to set these values at the pod security context level. Replace them with .Values.artifactory.podSecurityContext.fsGroupChangePolicy and .Values.artifactory.podSecurityContext.seLinuxOptions" }} +{{- end }} +{{- if .Values.initContainerImage }} + {{- fail "\n .Values.initContainerImage is no longer supported. Replace it with .Values.initContainers.image.registry .Values.initContainers.image.repository and .Values.initContainers.image.tag" }} +{{- end }} +{{- if and (eq .Values.rtfs.enabled true) (ne .Values.database.type "postgresql") (not (empty .Values.database.type))}} + {{- fail "\n RTFS supports only PostgreSQL databases and is not compatible with other databases. If you are currently using a different database, disable RTFS." }} +{{- end }} +{{- with .Values.artifactory.labels }} +{{ toYaml . | indent 4 }} +{{- end }} +{{- with .Values.artifactory.statefulset.annotations }} + annotations: +{{ toYaml . | indent 4 }} +{{- end }} +spec: + serviceName: {{ template "artifactory.servicePrependReleaseName" . }} + replicas: {{ .Values.artifactory.replicaCount }} + updateStrategy: {{- toYaml .Values.artifactory.updateStrategy | nindent 4 }} + selector: + matchLabels: + app: {{ template "artifactory.name" . }} + role: {{ template "artifactory.name" . }} + release: {{ .Release.Name }} + template: + metadata: + labels: + app: {{ template "artifactory.name" . }} + chart: {{ template "artifactory.chart" . }} + heritage: {{ .Release.Service }} + role: {{ template "artifactory.name" . }} + component: {{ .Values.artifactory.name }} + release: {{ .Release.Name }} + {{- with .Values.artifactory.labels }} +{{ toYaml . | indent 8 }} + {{- end }} + annotations: + {{- if not .Values.artifactory.unifiedSecretInstallation }} + checksum/database-secrets: {{ include (print $.Template.BasePath "/artifactory-database-secrets.yaml") . | sha256sum }} + checksum/binarystore: {{ include (print $.Template.BasePath "/artifactory-binarystore-secret.yaml") . | sha256sum }} + checksum/systemyaml: {{ include (print $.Template.BasePath "/artifactory-system-yaml.yaml") . | sha256sum }} + {{- if .Values.access.accessConfig }} + checksum/access-config: {{ include (print $.Template.BasePath "/artifactory-access-config.yaml") . | sha256sum }} + {{- end }} + {{- if .Values.artifactory.persistence.googleStorage.gcpServiceAccount.enabled }} + checksum/gcpcredentials: {{ include (print $.Template.BasePath "/artifactory-gcp-credentials-secret.yaml") . | sha256sum }} + {{- end }} + {{- if not (and .Values.artifactory.admin.secret .Values.artifactory.admin.dataKey) }} + checksum/admin-creds: {{ include (print $.Template.BasePath "/admin-bootstrap-creds.yaml") . | sha256sum }} + {{- end }} + {{- else }} + checksum/artifactory-unified-secret: {{ include (print $.Template.BasePath "/artifactory-unified-secret.yaml") . | sha256sum }} + {{- end }} + {{- with .Values.artifactory.annotations }} +{{ toYaml . | indent 8 }} + {{- end }} + spec: + {{- if .Values.artifactory.schedulerName }} + schedulerName: {{ .Values.artifactory.schedulerName | quote }} + {{- end }} + {{- if .Values.artifactory.priorityClass.existingPriorityClass }} + priorityClassName: {{ .Values.artifactory.priorityClass.existingPriorityClass }} + {{- else -}} + {{- if .Values.artifactory.priorityClass.create }} + priorityClassName: {{ default (include "artifactory.fullname" .) .Values.artifactory.priorityClass.name }} + {{- end }} + {{- end }} + serviceAccountName: {{ template "artifactory.serviceAccountName" . }} + terminationGracePeriodSeconds: {{ add .Values.artifactory.terminationGracePeriodSeconds 10 }} + {{- if or .Values.imagePullSecrets .Values.global.imagePullSecrets }} +{{- include "artifactory.imagePullSecrets" . | indent 6 }} + {{- end }} + {{- if .Values.artifactory.podSecurityContext.enabled }} + securityContext: {{- omit .Values.artifactory.podSecurityContext "enabled" | toYaml | nindent 8 }} + {{- end }} + {{- if .Values.artifactory.topologySpreadConstraints }} + topologySpreadConstraints: +{{ tpl (toYaml .Values.artifactory.topologySpreadConstraints) . | indent 8 }} + {{- end }} + initContainers: + {{- if or .Values.artifactory.customInitContainersBegin .Values.global.customInitContainersBegin }} +{{ tpl (include "artifactory.customInitContainersBegin" .) . | indent 6 }} + {{- end }} + {{- if .Values.artifactory.persistence.enabled }} + {{- if .Values.artifactory.deleteDBPropertiesOnStartup }} + - name: "delete-db-properties" + image: {{ include "artifactory.getImageInfoByValue" (list . "initContainers") }} + imagePullPolicy: {{ .Values.initContainers.image.pullPolicy }} + {{- if .Values.containerSecurityContext.enabled }} + securityContext: {{- omit .Values.containerSecurityContext "enabled" | toYaml | nindent 10 }} + {{- end }} + resources: +{{ toYaml .Values.initContainers.resources | indent 10 }} + command: + - 'bash' + - '-c' + - 'rm -fv {{ .Values.artifactory.persistence.mountPath }}/etc/db.properties' + volumeMounts: + - name: artifactory-volume + mountPath: "{{ .Values.artifactory.persistence.mountPath }}" + {{- end }} + {{- end }} + {{- if or (and .Values.artifactory.admin.secret .Values.artifactory.admin.dataKey) .Values.artifactory.admin.password }} + - name: "access-bootstrap-creds" + image: {{ include "artifactory.getImageInfoByValue" (list . "initContainers") }} + imagePullPolicy: {{ .Values.initContainers.image.pullPolicy }} + {{- if .Values.containerSecurityContext.enabled }} + securityContext: {{- omit .Values.containerSecurityContext "enabled" | toYaml | nindent 10 }} + {{- end }} + resources: +{{ toYaml .Values.initContainers.resources | indent 10 }} + command: + - 'bash' + - '-c' + - > + echo "Preparing {{ .Values.artifactory.persistence.mountPath }}/etc/access/bootstrap.creds"; + mkdir -p {{ .Values.artifactory.persistence.mountPath }}/etc/access; + cp -Lrf /tmp/access/bootstrap.creds {{ .Values.artifactory.persistence.mountPath }}/etc/access/bootstrap.creds; + chmod 600 {{ .Values.artifactory.persistence.mountPath }}/etc/access/bootstrap.creds; + volumeMounts: + - name: artifactory-volume + mountPath: "{{ .Values.artifactory.persistence.mountPath }}" + {{- if or (not .Values.artifactory.unifiedSecretInstallation) (and .Values.artifactory.admin.secret .Values.artifactory.admin.dataKey) }} + - name: access-bootstrap-creds + {{- else }} + - name: {{ include "artifactory.unifiedCustomSecretVolumeName" . }} + {{- end }} + mountPath: "/tmp/access/bootstrap.creds" + {{- if and .Values.artifactory.admin.secret .Values.artifactory.admin.dataKey }} + subPath: {{ .Values.artifactory.admin.dataKey }} + {{- else }} + subPath: "bootstrap.creds" + {{- end }} + {{- end }} + - name: 'copy-system-configurations' + image: {{ include "artifactory.getImageInfoByValue" (list . "initContainers") }} + imagePullPolicy: {{ .Values.initContainers.image.pullPolicy }} + {{- if .Values.containerSecurityContext.enabled }} + securityContext: {{- omit .Values.containerSecurityContext "enabled" | toYaml | nindent 10 }} + {{- end }} + resources: +{{ toYaml .Values.initContainers.resources | indent 10 }} + command: + - '/bin/bash' + - '-c' + - > + if [[ -e "{{ .Values.artifactory.persistence.mountPath }}/etc/filebeat.yaml" ]]; then chmod 644 {{ .Values.artifactory.persistence.mountPath }}/etc/filebeat.yaml; fi; + echo "Copy system.yaml to {{ .Values.artifactory.persistence.mountPath }}/etc"; + mkdir -p {{ .Values.artifactory.persistence.mountPath }}/etc; + mkdir -p {{ .Values.artifactory.persistence.mountPath }}/etc/access/keys/trusted; + {{- if .Values.systemYamlOverride.existingSecret }} + cp -fv /tmp/etc/{{ .Values.systemYamlOverride.dataKey }} {{ .Values.artifactory.persistence.mountPath }}/etc/system.yaml; + {{- else }} + cp -fv /tmp/etc/system.yaml {{ .Values.artifactory.persistence.mountPath }}/etc/system.yaml; + {{- end }} + echo "Copy binarystore.xml file"; + mkdir -p {{ .Values.artifactory.persistence.mountPath }}/etc/artifactory; + cp -fv /tmp/etc/artifactory/binarystore.xml {{ .Values.artifactory.persistence.mountPath }}/etc/artifactory/binarystore.xml; + {{- if .Values.access.accessConfig }} + echo "Copy access.config.patch.yml to {{ .Values.artifactory.persistence.mountPath }}/etc/access"; + mkdir -p {{ .Values.artifactory.persistence.mountPath }}/etc/access; + cp -fv /tmp/etc/access.config.patch.yml {{ .Values.artifactory.persistence.mountPath }}/etc/access/access.config.patch.yml; + {{- end }} + {{- if .Values.access.resetAccessCAKeys }} + echo "Resetting Access CA Keys"; + mkdir -p {{ .Values.artifactory.persistence.mountPath }}/bootstrap/etc/access/keys; + touch {{ .Values.artifactory.persistence.mountPath }}/bootstrap/etc/access/keys/reset_ca_keys; + {{- end }} + {{- if .Values.access.customCertificatesSecretName }} + echo "Copying custom certificates to {{ .Values.artifactory.persistence.mountPath }}/bootstrap/etc/access/keys"; + mkdir -p {{ .Values.artifactory.persistence.mountPath }}/bootstrap/etc/access/keys; + cp -fv /tmp/etc/tls.crt {{ .Values.artifactory.persistence.mountPath }}/bootstrap/etc/access/keys/ca.crt; + cp -fv /tmp/etc/tls.key {{ .Values.artifactory.persistence.mountPath }}/bootstrap/etc/access/keys/ca.private.key; + {{- end }} + {{- if .Values.jfconnect.customCertificatesSecretName }} + echo "Copying custom certificates to {{ .Values.artifactory.persistence.mountPath }}/bootstrap/jfconnect/etc/keys"; + mkdir -p {{ .Values.artifactory.persistence.mountPath }}/bootstrap/jfconnect/etc/keys; + cp -fv /tmp/etc/tls.crt {{ .Values.artifactory.persistence.mountPath }}/bootstrap/jfconnect/etc/keys/; + cp -fv /tmp/etc/tls.cer {{ .Values.artifactory.persistence.mountPath }}/bootstrap/jfconnect/etc/keys/; + cp -fv /tmp/etc/tls.pem {{ .Values.artifactory.persistence.mountPath }}/bootstrap/jfconnect/etc/keys/; + cp -fv /tmp/etc/tls.key {{ .Values.artifactory.persistence.mountPath }}/bootstrap/jfconnect/etc/keys/; + {{- end }} + {{- if or .Values.artifactory.joinKey .Values.global.joinKey .Values.artifactory.joinKeySecretName .Values.global.joinKeySecretName }} + echo "Copy joinKey to {{ .Values.artifactory.persistence.mountPath }}/bootstrap/access/etc/security"; + mkdir -p {{ .Values.artifactory.persistence.mountPath }}/bootstrap/access/etc/security; + echo -n ${ARTIFACTORY_JOIN_KEY} > {{ .Values.artifactory.persistence.mountPath }}/bootstrap/access/etc/security/join.key; + {{- end }} + {{- if or .Values.artifactory.jfConnectToken .Values.artifactory.jfConnectTokenSecretName }} + echo "Copy jfConnectToken to {{ .Values.artifactory.persistence.mountPath }}/bootstrap/jfconnect/registration_token"; + mkdir -p {{ .Values.artifactory.persistence.mountPath }}/bootstrap/jfconnect/; + echo -n ${ARTIFACTORY_JFCONNECT_TOKEN} > {{ .Values.artifactory.persistence.mountPath }}/bootstrap/jfconnect/registration_token; + {{- end }} + {{- if or .Values.artifactory.masterKey .Values.global.masterKey .Values.artifactory.masterKeySecretName .Values.global.masterKeySecretName }} + echo "Copy masterKey to {{ .Values.artifactory.persistence.mountPath }}/etc/security"; + mkdir -p {{ .Values.artifactory.persistence.mountPath }}/etc/security; + echo -n ${ARTIFACTORY_MASTER_KEY} > {{ .Values.artifactory.persistence.mountPath }}/etc/security/master.key; + {{- end }} + env: + {{- if or .Values.artifactory.joinKey .Values.global.joinKey .Values.artifactory.joinKeySecretName .Values.global.joinKeySecretName }} + - name: ARTIFACTORY_JOIN_KEY + valueFrom: + secretKeyRef: + {{- if or (not .Values.artifactory.unifiedSecretInstallation) .Values.artifactory.joinKeySecretName .Values.global.joinKeySecretName }} + name: {{ include "artifactory.joinKeySecretName" . }} + {{- else }} + name: "{{ template "artifactory.unifiedSecretPrependReleaseName" . }}-unified-secret" + {{- end }} + key: join-key + {{- end }} + {{- if or .Values.artifactory.jfConnectToken .Values.artifactory.jfConnectSecretName }} + - name: ARTIFACTORY_JFCONNECT_TOKEN + valueFrom: + secretKeyRef: + {{- if or (not .Values.artifactory.unifiedSecretInstallation) .Values.artifactory.jfConnectTokenSecretName }} + name: {{ include "artifactory.jfConnectTokenSecretName" . }} + {{- else }} + name: "{{ template "artifactory.unifiedSecretPrependReleaseName" . }}-unified-secret" + {{- end }} + key: jfconnect-token + {{- end }} + {{- if or .Values.artifactory.masterKey .Values.global.masterKey .Values.artifactory.masterKeySecretName .Values.global.masterKeySecretName }} + - name: ARTIFACTORY_MASTER_KEY + valueFrom: + secretKeyRef: + {{- if or (not .Values.artifactory.unifiedSecretInstallation) .Values.artifactory.masterKeySecretName .Values.global.masterKeySecretName }} + name: {{ include "artifactory.masterKeySecretName" . }} + {{- else }} + name: "{{ template "artifactory.unifiedSecretPrependReleaseName" . }}-unified-secret" + {{- end }} + key: master-key + {{- end }} + volumeMounts: + - name: artifactory-volume + mountPath: {{ .Values.artifactory.persistence.mountPath | quote }} + {{- if or (not .Values.artifactory.unifiedSecretInstallation) .Values.systemYamlOverride.existingSecret }} + - name: systemyaml + {{- else }} + - name: {{ include "artifactory.unifiedCustomSecretVolumeName" . }} + {{- end }} + {{- if .Values.systemYamlOverride.existingSecret }} + mountPath: "/tmp/etc/{{.Values.systemYamlOverride.dataKey}}" + subPath: {{ .Values.systemYamlOverride.dataKey }} + {{- else }} + mountPath: "/tmp/etc/system.yaml" + subPath: "system.yaml" + {{- end }} + + ######################## Binarystore ########################## + {{- if or (not .Values.artifactory.unifiedSecretInstallation) .Values.artifactory.persistence.customBinarystoreXmlSecret }} + - name: binarystore-xml + {{- else }} + - name: {{ include "artifactory.unifiedCustomSecretVolumeName" . }} + {{- end }} + mountPath: "/tmp/etc/artifactory/binarystore.xml" + subPath: binarystore.xml + + ######################## Access config ########################## + {{- if .Values.access.accessConfig }} + {{- if not .Values.artifactory.unifiedSecretInstallation }} + - name: access-config + {{- else }} + - name: {{ include "artifactory.unifiedCustomSecretVolumeName" . }} + {{- end }} + mountPath: "/tmp/etc/access.config.patch.yml" + subPath: "access.config.patch.yml" + {{- end }} + + ######################## Access certs external secret ########################## + {{- if .Values.access.customCertificatesSecretName }} + - name: access-certs + mountPath: "/tmp/etc/tls.crt" + subPath: tls.crt + - name: access-certs + mountPath: "/tmp/etc/tls.key" + subPath: tls.key + {{- end }} + + {{- if .Values.jfconnect.customCertificatesSecretName }} + - name: jfconnect-certs + mountPath: "/tmp/etc/tls.crt" + subPath: tls.crt + - name: jfconnect-certs + mountPath: "/tmp/etc/tls.pem" + subPath: tls.pem + - name: jfconnect-certs + mountPath: "/tmp/etc/tls.cer" + subPath: tls.cer + - name: jfconnect-certs + mountPath: "/tmp/etc/tls.key" + subPath: tls.key + {{- end }} + + {{- if or .Values.artifactory.customCertificates.enabled .Values.global.customCertificates.enabled }} + - name: copy-custom-certificates + image: {{ include "artifactory.getImageInfoByValue" (list . "initContainers") }} + imagePullPolicy: {{ .Values.initContainers.image.pullPolicy }} + {{- if .Values.containerSecurityContext.enabled }} + securityContext: {{- omit .Values.containerSecurityContext "enabled" | toYaml | nindent 10 }} + {{- end }} + resources: +{{ toYaml .Values.initContainers.resources | indent 10 }} + command: + - 'bash' + - '-c' + - > +{{ include "artifactory.copyCustomCerts" . | indent 10 }} + volumeMounts: + - name: artifactory-volume + mountPath: {{ .Values.artifactory.persistence.mountPath }} + - name: ca-certs + mountPath: "/tmp/certs" + {{- end }} + + {{- if .Values.artifactory.circleOfTrustCertificatesSecret }} + - name: copy-circle-of-trust-certificates + image: {{ include "artifactory.getImageInfoByValue" (list . "initContainers") }} + imagePullPolicy: {{ .Values.initContainers.image.pullPolicy }} + {{- if .Values.containerSecurityContext.enabled }} + securityContext: {{- omit .Values.containerSecurityContext "enabled" | toYaml | nindent 10 }} + {{- end }} + resources: +{{ toYaml .Values.initContainers.resources | indent 10 }} + command: + - 'bash' + - '-c' + - > +{{ include "artifactory.copyCircleOfTrustCertsCerts" . | indent 10 }} + volumeMounts: + - name: artifactory-volume + mountPath: {{ .Values.artifactory.persistence.mountPath }} + - name: circle-of-trust-certs + mountPath: "/tmp/circleoftrustcerts" + {{- end }} + + {{- if .Values.waitForDatabase }} + {{- if .Values.postgresql.enabled }} + - name: "wait-for-db" + image: {{ include "artifactory.getImageInfoByValue" (list . "initContainers") }} + imagePullPolicy: {{ .Values.initContainers.image.pullPolicy }} + {{- if .Values.containerSecurityContext.enabled }} + securityContext: {{- omit .Values.containerSecurityContext "enabled" | toYaml | nindent 10 }} + {{- end }} + resources: +{{ toYaml .Values.initContainers.resources | indent 10 }} + command: + - /bin/bash + - -c + - | + echo "Waiting for postgresql to come up" + ready=false; + while ! $ready; do echo waiting; + timeout 2s bash -c " + {{- if .Values.artifactory.migration.preStartCommand }} + echo "Running custom preStartCommand command"; + {{ tpl .Values.artifactory.migration.preStartCommand . }}; + {{- end }} + scriptsPath="/opt/jfrog/artifactory/app/bin"; + mkdir -p $scriptsPath; + echo "Copy migration scripts and Run migration"; + cp -fv /tmp/migrate.sh $scriptsPath/migrate.sh; + cp -fv /tmp/migrationHelmInfo.yaml $scriptsPath/migrationHelmInfo.yaml; + cp -fv /tmp/migrationStatus.sh $scriptsPath/migrationStatus.sh; + mkdir -p {{ .Values.artifactory.persistence.mountPath }}/log; + bash $scriptsPath/migrationStatus.sh {{ include "artifactory.app.version" . }} {{ .Values.artifactory.migration.timeoutSeconds }} > >(tee {{ .Values.artifactory.persistence.mountPath }}/log/helm-migration.log) 2>&1; + env: + {{- if and (not .Values.waitForDatabase) (not .Values.postgresql.enabled) }} + - name: SKIP_WAIT_FOR_EXTERNAL_DB + value: "true" + {{- end }} + {{- if or .Values.database.secrets.user .Values.database.user }} + - name: JF_SHARED_DATABASE_USERNAME + valueFrom: + secretKeyRef: + {{- if .Values.database.secrets.user }} + name: {{ tpl .Values.database.secrets.user.name . }} + key: {{ tpl .Values.database.secrets.user.key . }} + {{- else if .Values.database.user }} + {{- if not .Values.artifactory.unifiedSecretInstallation }} + name: {{ template "artifactory.fullname" . }}-database-creds + {{- else }} + name: "{{ template "artifactory.unifiedSecretPrependReleaseName" . }}-unified-secret" + {{- end }} + key: db-user + {{- end }} + {{- end }} + {{ if or .Values.database.secrets.password .Values.database.password .Values.postgresql.enabled }} + - name: JF_SHARED_DATABASE_PASSWORD + valueFrom: + secretKeyRef: + {{- if .Values.database.secrets.password }} + name: {{ tpl .Values.database.secrets.password.name . }} + key: {{ tpl .Values.database.secrets.password.key . }} + {{- else if .Values.database.password }} + {{- if not .Values.artifactory.unifiedSecretInstallation }} + name: {{ template "artifactory.fullname" . }}-database-creds + {{- else }} + name: "{{ template "artifactory.unifiedSecretPrependReleaseName" . }}-unified-secret" + {{- end }} + key: db-password + {{- else if .Values.postgresql.enabled }} + name: {{ .Release.Name }}-postgresql + key: postgresql-password + {{- end }} + {{- end }} + {{- if or .Values.database.secrets.url .Values.database.url }} + - name: JF_SHARED_DATABASE_URL + valueFrom: + secretKeyRef: + {{- if .Values.database.secrets.url }} + name: {{ tpl .Values.database.secrets.url.name . }} + key: {{ tpl .Values.database.secrets.url.key . }} + {{- else if .Values.database.url }} + {{- if not .Values.artifactory.unifiedSecretInstallation }} + name: {{ template "artifactory.fullname" . }}-database-creds + {{- else }} + name: "{{ template "artifactory.unifiedSecretPrependReleaseName" . }}-unified-secret" + {{- end }} + key: db-url + {{- end }} + {{- end }} +{{- with .Values.artifactory.extraEnvironmentVariables }} +{{ tpl (toYaml .) $ | indent 8 }} +{{- end }} + volumeMounts: + - name: migration-scripts + mountPath: "/tmp/migrate.sh" + subPath: migrate.sh + - name: migration-scripts + mountPath: "/tmp/migrationHelmInfo.yaml" + subPath: migrationHelmInfo.yaml + - name: migration-scripts + mountPath: "/tmp/migrationStatus.sh" + subPath: migrationStatus.sh + - name: artifactory-volume + mountPath: {{ .Values.artifactory.persistence.mountPath | quote }} + + ######################## Artifactory persistence nfs ########################## + {{- if eq .Values.artifactory.persistence.type "nfs" }} + - name: artifactory-data + mountPath: "{{ .Values.artifactory.persistence.nfs.dataDir }}" + - name: artifactory-backup + mountPath: "{{ .Values.artifactory.persistence.nfs.backupDir }}" + {{- else }} + + ######################## Artifactory persistence binarystore Xml ########################## + {{- if or (not .Values.artifactory.unifiedSecretInstallation) .Values.artifactory.persistence.customBinarystoreXmlSecret }} + - name: binarystore-xml + {{- else }} + - name: {{ include "artifactory.unifiedCustomSecretVolumeName" . }} + {{- end }} + mountPath: "/tmp/etc/artifactory/binarystore.xml" + subPath: "binarystore.xml" + + ######################## Artifactory persistence google storage ########################## + {{- if .Values.artifactory.persistence.googleStorage.gcpServiceAccount.enabled }} + {{- if or (not .Values.artifactory.unifiedSecretInstallation) .Values.artifactory.persistence.googleStorage.gcpServiceAccount.customSecretName }} + - name: gcpcreds-json + {{- else }} + - name: {{ include "artifactory.unifiedCustomSecretVolumeName" . }} + {{- end }} + mountPath: "/artifactory_bootstrap/gcp.credentials.json" + subPath: gcp.credentials.json + {{- end }} + {{- end }} + + ######################## CustomVolumeMounts ########################## + {{- if or .Values.artifactory.customVolumeMounts .Values.global.customVolumeMounts }} +{{ tpl (include "artifactory.customVolumeMounts" .) . | indent 8 }} + {{- end }} +{{- end }} + {{- if .Values.hostAliases }} + hostAliases: +{{ toYaml .Values.hostAliases | indent 6 }} + {{- end }} + containers: + {{- if .Values.splitServicesToContainers }} + - name: {{ .Values.router.name }} + image: {{ include "artifactory.getImageInfoByValue" (list . "router") }} + imagePullPolicy: {{ .Values.router.image.pullPolicy }} + {{- if .Values.containerSecurityContext.enabled }} + securityContext: {{- omit .Values.containerSecurityContext "enabled" | toYaml | nindent 10 }} + {{- end }} + command: + - '/bin/bash' + - '-c' + - > + exec /opt/jfrog/router/app/bin/entrypoint-router.sh + {{- with .Values.router.lifecycle }} + lifecycle: +{{ toYaml . | indent 10 }} + {{- end }} + env: + - name: JF_ROUTER_TOPOLOGY_LOCAL_REQUIREDSERVICETYPES + value: {{ include "artifactory.router.requiredServiceTypes" . }} +{{- with .Values.router.extraEnvironmentVariables }} +{{ tpl (toYaml .) $ | indent 8 }} +{{- end }} + volumeMounts: + - name: artifactory-volume + mountPath: {{ .Values.router.persistence.mountPath | quote }} +{{- with .Values.router.customVolumeMounts }} +{{ tpl . $ | indent 8 }} +{{- end }} + resources: +{{ toYaml .Values.router.resources | indent 10 }} + {{- if .Values.router.startupProbe.enabled }} + startupProbe: +{{ tpl .Values.router.startupProbe.config . | indent 10 }} + {{- end }} + {{- if .Values.router.readinessProbe.enabled }} + readinessProbe: +{{ tpl .Values.router.readinessProbe.config . | indent 10 }} + {{- end }} + {{- if .Values.router.livenessProbe.enabled }} + livenessProbe: +{{ tpl .Values.router.livenessProbe.config . | indent 10 }} + {{- end }} + {{- if .Values.frontend.enabled }} + - name: {{ .Values.frontend.name }} + image: {{ include "artifactory.getImageInfoByValue" (list . "artifactory") }} + imagePullPolicy: {{ .Values.artifactory.image.pullPolicy }} + {{- if .Values.containerSecurityContext.enabled }} + securityContext: {{- omit .Values.containerSecurityContext "enabled" | toYaml | nindent 10 }} + {{- end }} + command: + - '/bin/bash' + - '-c' + - > + exec /opt/jfrog/artifactory/app/third-party/node/bin/node /opt/jfrog/artifactory/app/frontend/bin/server/dist/bundle.js /opt/jfrog/artifactory/app/frontend + {{- with .Values.frontend.lifecycle }} + lifecycle: +{{ toYaml . | indent 10 }} + {{- end }} + env: + - name: JF_SHARED_NODE_ID + valueFrom: + fieldRef: + fieldPath: metadata.name + {{- if and (gt (.Values.artifactory.replicaCount | int64) 1) (eq (include "artifactory.isImageProType" .) "true") (eq (include "artifactory.isUsingDerby" .) "false") }} + - name : JF_SHARED_NODE_HAENABLED + value: "true" + {{- end }} +{{- with .Values.frontend.extraEnvironmentVariables }} +{{ tpl (toYaml .) $ | indent 8 }} +{{- end }} + volumeMounts: + {{- if or .Values.artifactory.customVolumeMounts .Values.global.customVolumeMounts }} +{{ tpl (include "artifactory.customVolumeMounts" .) . | indent 8 }} + {{- end }} + - name: artifactory-volume + mountPath: {{ .Values.artifactory.persistence.mountPath | quote }} + resources: +{{ toYaml .Values.frontend.resources | indent 10 }} + {{- if .Values.frontend.startupProbe.enabled }} + startupProbe: +{{ tpl .Values.frontend.startupProbe.config . | indent 10 }} + {{- end }} + {{- if .Values.frontend.livenessProbe.enabled }} + livenessProbe: +{{ tpl .Values.frontend.livenessProbe.config . | indent 10 }} + {{- end }} + {{- end }} + {{- if .Values.evidence.enabled }} + - name: {{ .Values.evidence.name }} + image: {{ include "artifactory.getImageInfoByValue" (list . "artifactory") }} + imagePullPolicy: {{ .Values.artifactory.image.pullPolicy }} + {{- if .Values.containerSecurityContext.enabled }} + securityContext: {{- omit .Values.containerSecurityContext "enabled" | toYaml | nindent 10 }} + {{- end }} + command: + - '/bin/bash' + - '-c' + - > + exec /opt/jfrog/artifactory/app/evidence/bin/jf-evidence start + {{- with .Values.evidence.lifecycle }} + lifecycle: +{{ toYaml . | indent 10 }} + {{- end }} + env: + - name: JF_SHARED_NODE_ID + valueFrom: + fieldRef: + fieldPath: metadata.name + {{- if or .Values.database.secrets.user .Values.database.user }} + - name: JF_SHARED_DATABASE_USERNAME + valueFrom: + secretKeyRef: + {{- if .Values.database.secrets.user }} + name: {{ tpl .Values.database.secrets.user.name . }} + key: {{ tpl .Values.database.secrets.user.key . }} + {{- else if .Values.database.user }} + {{- if not .Values.artifactory.unifiedSecretInstallation }} + name: {{ template "artifactory.fullname" . }}-database-creds + {{- else }} + name: "{{ template "artifactory.unifiedSecretPrependReleaseName" . }}-unified-secret" + {{- end }} + key: db-user + {{- end }} + {{- end }} + {{ if or .Values.database.secrets.password .Values.database.password .Values.postgresql.enabled }} + - name: JF_SHARED_DATABASE_PASSWORD + valueFrom: + secretKeyRef: + {{- if .Values.database.secrets.password }} + name: {{ tpl .Values.database.secrets.password.name . }} + key: {{ tpl .Values.database.secrets.password.key . }} + {{- else if .Values.database.password }} + {{- if not .Values.artifactory.unifiedSecretInstallation }} + name: {{ template "artifactory.fullname" . }}-database-creds + {{- else }} + name: "{{ template "artifactory.unifiedSecretPrependReleaseName" . }}-unified-secret" + {{- end }} + key: db-password + {{- else if .Values.postgresql.enabled }} + name: {{ .Release.Name }}-postgresql + key: postgresql-password + {{- end }} + {{- end }} + {{- if or .Values.database.secrets.url .Values.database.url }} + - name: JF_SHARED_DATABASE_URL + valueFrom: + secretKeyRef: + {{- if .Values.database.secrets.url }} + name: {{ tpl .Values.database.secrets.url.name . }} + key: {{ tpl .Values.database.secrets.url.key . }} + {{- else if .Values.database.url }} + {{- if not .Values.artifactory.unifiedSecretInstallation }} + name: {{ template "artifactory.fullname" . }}-database-creds + {{- else }} + name: "{{ template "artifactory.unifiedSecretPrependReleaseName" . }}-unified-secret" + {{- end }} + key: db-url + {{- end }} + {{- end }} +{{- with .Values.evidence.extraEnvironmentVariables }} +{{ tpl (toYaml .) $ | indent 8 }} +{{- end }} + ports: + - containerPort: {{ .Values.evidence.internalPort }} + name: http-evidence + - containerPort: {{ .Values.evidence.externalPort }} + name: grpc-evidence + volumeMounts: + {{- if or .Values.artifactory.customVolumeMounts .Values.global.customVolumeMounts }} +{{ tpl (include "artifactory.customVolumeMounts" .) . | indent 8 }} + {{- end }} + - name: artifactory-volume + mountPath: {{ .Values.artifactory.persistence.mountPath | quote }} + resources: +{{ toYaml .Values.evidence.resources | indent 10 }} + {{- if .Values.evidence.startupProbe.enabled }} + startupProbe: +{{ tpl .Values.evidence.startupProbe.config . | indent 10 }} + {{- end }} + {{- if .Values.evidence.livenessProbe.enabled }} + livenessProbe: +{{ tpl .Values.evidence.livenessProbe.config . | indent 10 }} + {{- end }} + {{- end }} + {{- if .Values.metadata.enabled }} + - name: {{ .Values.metadata.name }} + image: {{ include "artifactory.getImageInfoByValue" (list . "artifactory") }} + imagePullPolicy: {{ .Values.artifactory.image.pullPolicy }} + {{- if .Values.containerSecurityContext.enabled }} + securityContext: {{- omit .Values.containerSecurityContext "enabled" | toYaml | nindent 10 }} + {{- end }} + command: + - '/bin/bash' + - '-c' + - > + exec /opt/jfrog/artifactory/app/metadata/bin/jf-metadata start + {{- with .Values.metadata.lifecycle }} + lifecycle: +{{ toYaml . | indent 10 }} + {{- end }} + env: + - name: JF_SHARED_NODE_ID + valueFrom: + fieldRef: + fieldPath: metadata.name + {{- if or .Values.database.secrets.user .Values.database.user }} + - name: JF_SHARED_DATABASE_USERNAME + valueFrom: + secretKeyRef: + {{- if .Values.database.secrets.user }} + name: {{ tpl .Values.database.secrets.user.name . }} + key: {{ tpl .Values.database.secrets.user.key . }} + {{- else if .Values.database.user }} + {{- if not .Values.artifactory.unifiedSecretInstallation }} + name: {{ template "artifactory.fullname" . }}-database-creds + {{- else }} + name: "{{ template "artifactory.unifiedSecretPrependReleaseName" . }}-unified-secret" + {{- end }} + key: db-user + {{- end }} + {{- end }} + {{ if or .Values.database.secrets.password .Values.database.password .Values.postgresql.enabled }} + - name: JF_SHARED_DATABASE_PASSWORD + valueFrom: + secretKeyRef: + {{- if .Values.database.secrets.password }} + name: {{ tpl .Values.database.secrets.password.name . }} + key: {{ tpl .Values.database.secrets.password.key . }} + {{- else if .Values.database.password }} + {{- if not .Values.artifactory.unifiedSecretInstallation }} + name: {{ template "artifactory.fullname" . }}-database-creds + {{- else }} + name: "{{ template "artifactory.unifiedSecretPrependReleaseName" . }}-unified-secret" + {{- end }} + key: db-password + {{- else if .Values.postgresql.enabled }} + name: {{ .Release.Name }}-postgresql + key: postgresql-password + {{- end }} + {{- end }} + {{- if or .Values.database.secrets.url .Values.database.url }} + - name: JF_SHARED_DATABASE_URL + valueFrom: + secretKeyRef: + {{- if .Values.database.secrets.url }} + name: {{ tpl .Values.database.secrets.url.name . }} + key: {{ tpl .Values.database.secrets.url.key . }} + {{- else if .Values.database.url }} + {{- if not .Values.artifactory.unifiedSecretInstallation }} + name: {{ template "artifactory.fullname" . }}-database-creds + {{- else }} + name: "{{ template "artifactory.unifiedSecretPrependReleaseName" . }}-unified-secret" + {{- end }} + key: db-url + {{- end }} + {{- end }} +{{- with .Values.metadata.extraEnvironmentVariables }} +{{ tpl (toYaml .) $ | indent 8 }} +{{- end }} + volumeMounts: + {{- if or .Values.artifactory.customVolumeMounts .Values.global.customVolumeMounts }} +{{ tpl (include "artifactory.customVolumeMounts" .) . | indent 8 }} + {{- end }} + - name: artifactory-volume + mountPath: {{ .Values.artifactory.persistence.mountPath | quote }} + resources: +{{ toYaml .Values.metadata.resources | indent 10 }} + {{- if .Values.metadata.startupProbe.enabled }} + startupProbe: +{{ tpl .Values.metadata.startupProbe.config . | indent 10 }} + {{- end }} + {{- if .Values.metadata.livenessProbe.enabled }} + livenessProbe: +{{ tpl .Values.metadata.livenessProbe.config . | indent 10 }} + {{- end }} + {{- end }} + {{- if and .Values.onemodel.enabled (not (regexMatch "^.*(oss|cpp-ce|jcr).*$" .Values.artifactory.image.repository)) }} + - name: {{ .Values.onemodel.name }} + image: {{ include "artifactory.getImageInfoByValue" (list . "artifactory") }} + imagePullPolicy: {{ .Values.artifactory.image.pullPolicy }} + {{- if .Values.containerSecurityContext.enabled }} + securityContext: {{- omit .Values.containerSecurityContext "enabled" | toYaml | nindent 10 }} + {{- end }} + command: + - '/bin/bash' + - '-c' + - > + exec /opt/jfrog/artifactory/app/onemodel/bin/entrypoint-onemodel.sh + {{- with .Values.onemodel.lifecycle }} + lifecycle: +{{ toYaml . | indent 10 }} + {{- end }} + env: + - name: JF_SHARED_NODE_ID + valueFrom: + fieldRef: + fieldPath: metadata.name + - name: JF_ONEMODEL_LISTENADDR + value: "0.0.0.0" +{{- with .Values.onemodel.extraEnvironmentVariables }} +{{ tpl (toYaml .) $ | indent 8 }} +{{- end }} + volumeMounts: + {{- if or .Values.artifactory.customVolumeMounts .Values.global.customVolumeMounts }} +{{ tpl (include "artifactory.customVolumeMounts" .) . | indent 8 }} + {{- end }} + - name: artifactory-volume + mountPath: {{ .Values.artifactory.persistence.mountPath | quote }} + resources: +{{ toYaml .Values.onemodel.resources | indent 10 }} + {{- if .Values.onemodel.startupProbe.enabled }} + startupProbe: +{{ tpl .Values.onemodel.startupProbe.config . | indent 10 }} + {{- end }} + {{- if .Values.onemodel.livenessProbe.enabled }} + livenessProbe: +{{ tpl .Values.onemodel.livenessProbe.config . | indent 10 }} + {{- end }} + {{- if .Values.onemodel.readinessProbe.enabled }} + readinessProbe: +{{ tpl .Values.onemodel.readinessProbe.config . | indent 10 }} + {{- end }} + {{- end }} + {{- if .Values.event.enabled }} + - name: {{ .Values.event.name }} + image: {{ include "artifactory.getImageInfoByValue" (list . "artifactory") }} + imagePullPolicy: {{ .Values.artifactory.image.pullPolicy }} + {{- if .Values.containerSecurityContext.enabled }} + securityContext: {{- omit .Values.containerSecurityContext "enabled" | toYaml | nindent 10 }} + {{- end }} + command: + - '/bin/bash' + - '-c' + - > + exec /opt/jfrog/artifactory/app/event/bin/jf-event start + {{- with .Values.event.lifecycle }} + lifecycle: +{{ toYaml . | indent 10 }} + {{- end }} + env: + - name: JF_SHARED_NODE_ID + valueFrom: + fieldRef: + fieldPath: metadata.name +{{- with .Values.event.extraEnvironmentVariables }} +{{ tpl (toYaml .) $ | indent 8 }} +{{- end }} + volumeMounts: + {{- if or .Values.artifactory.customVolumeMounts .Values.global.customVolumeMounts }} +{{ tpl (include "artifactory.customVolumeMounts" .) . | indent 8 }} + {{- end }} + - name: artifactory-volume + mountPath: {{ .Values.artifactory.persistence.mountPath | quote }} + resources: +{{ toYaml .Values.event.resources | indent 10 }} + {{- if .Values.event.startupProbe.enabled }} + startupProbe: +{{ tpl .Values.event.startupProbe.config . | indent 10 }} + {{- end }} + {{- if .Values.event.livenessProbe.enabled }} + livenessProbe: +{{ tpl .Values.event.livenessProbe.config . | indent 10 }} + {{- end }} + {{- end }} + {{- if and .Values.jfconnect.enabled (not (regexMatch "^.*(oss|cpp-ce|jcr).*$" .Values.artifactory.image.repository)) }} + - name: {{ .Values.jfconnect.name }} + image: {{ include "artifactory.getImageInfoByValue" (list . "artifactory") }} + imagePullPolicy: {{ .Values.artifactory.image.pullPolicy }} + {{- if .Values.containerSecurityContext.enabled }} + securityContext: {{- omit .Values.containerSecurityContext "enabled" | toYaml | nindent 10 }} + {{- end }} + command: + - '/bin/bash' + - '-c' + - > + exec /opt/jfrog/artifactory/app/jfconnect/bin/jf-connect start + {{- with .Values.jfconnect.lifecycle }} + lifecycle: +{{ toYaml . | indent 10 }} + {{- end }} + env: + - name: JF_SHARED_NODE_ID + valueFrom: + fieldRef: + fieldPath: metadata.name +{{- with .Values.jfconnect.extraEnvironmentVariables }} +{{ tpl (toYaml .) $ | indent 8 }} +{{- end }} + volumeMounts: + {{- if or .Values.artifactory.customVolumeMounts .Values.global.customVolumeMounts }} +{{ tpl (include "artifactory.customVolumeMounts" .) . | indent 8 }} + {{- end }} + - name: artifactory-volume + mountPath: {{ .Values.artifactory.persistence.mountPath | quote }} + resources: +{{ toYaml .Values.jfconnect.resources | indent 10 }} + {{- if .Values.jfconnect.startupProbe.enabled }} + startupProbe: +{{ tpl .Values.jfconnect.startupProbe.config . | indent 10 }} + {{- end }} + {{- if .Values.jfconnect.livenessProbe.enabled }} + livenessProbe: +{{ tpl .Values.jfconnect.livenessProbe.config . | indent 10 }} + {{- end }} + {{- end }} + {{- if and .Values.access.enabled (not (.Values.access.runOnArtifactoryTomcat | default false)) }} + - name: {{ .Values.access.name }} + image: {{ include "artifactory.getImageInfoByValue" (list . "artifactory") }} + imagePullPolicy: {{ .Values.artifactory.image.pullPolicy }} + {{- if .Values.containerSecurityContext.enabled }} + securityContext: {{- omit .Values.containerSecurityContext "enabled" | toYaml | nindent 10 }} + {{- end }} + {{- if .Values.access.resources }} + resources: +{{ toYaml .Values.access.resources | indent 10 }} + {{- end }} + command: + - '/bin/bash' + - '-c' + - > + set -e; + {{- if .Values.access.preStartCommand }} + echo "Running custom preStartCommand command"; + {{ tpl .Values.access.preStartCommand . }}; + {{- end }} + exec /opt/jfrog/artifactory/app/access/bin/entrypoint-access.sh + {{- with .Values.access.lifecycle }} + lifecycle: +{{ toYaml . | indent 10 }} + {{- end }} + env: + {{- if and (gt (.Values.artifactory.replicaCount | int64) 1) (eq (include "artifactory.isImageProType" .) "true") (eq (include "artifactory.isUsingDerby" .) "false") }} + - name : JF_SHARED_NODE_HAENABLED + value: "true" + {{- end }} + {{- if and (not .Values.waitForDatabase) (not .Values.postgresql.enabled) }} + - name: SKIP_WAIT_FOR_EXTERNAL_DB + value: "true" + {{- end }} + {{- if or .Values.database.secrets.user .Values.database.user }} + - name: JF_SHARED_DATABASE_USERNAME + valueFrom: + secretKeyRef: + {{- if .Values.database.secrets.user }} + name: {{ tpl .Values.database.secrets.user.name . }} + key: {{ tpl .Values.database.secrets.user.key . }} + {{- else if .Values.database.user }} + {{- if not .Values.artifactory.unifiedSecretInstallation }} + name: {{ template "artifactory.fullname" . }}-database-creds + {{- else }} + name: "{{ template "artifactory.unifiedSecretPrependReleaseName" . }}-unified-secret" + {{- end }} + key: db-user + {{- end }} + {{- end }} + {{ if or .Values.database.secrets.password .Values.database.password .Values.postgresql.enabled }} + - name: JF_SHARED_DATABASE_PASSWORD + valueFrom: + secretKeyRef: + {{- if .Values.database.secrets.password }} + name: {{ tpl .Values.database.secrets.password.name . }} + key: {{ tpl .Values.database.secrets.password.key . }} + {{- else if .Values.database.password }} + {{- if not .Values.artifactory.unifiedSecretInstallation }} + name: {{ template "artifactory.fullname" . }}-database-creds + {{- else }} + name: "{{ template "artifactory.unifiedSecretPrependReleaseName" . }}-unified-secret" + {{- end }} + key: db-password + {{- else if .Values.postgresql.enabled }} + name: {{ .Release.Name }}-postgresql + key: postgresql-password + {{- end }} + {{- end }} + {{- if or .Values.database.secrets.url .Values.database.url }} + - name: JF_SHARED_DATABASE_URL + valueFrom: + secretKeyRef: + {{- if .Values.database.secrets.url }} + name: {{ tpl .Values.database.secrets.url.name . }} + key: {{ tpl .Values.database.secrets.url.key . }} + {{- else if .Values.database.url }} + {{- if not .Values.artifactory.unifiedSecretInstallation }} + name: {{ template "artifactory.fullname" . }}-database-creds + {{- else }} + name: "{{ template "artifactory.unifiedSecretPrependReleaseName" . }}-unified-secret" + {{- end }} + key: db-url + {{- end }} + {{- end }} +{{- with .Values.access.extraEnvironmentVariables }} +{{ tpl (toYaml .) $ | indent 8 }} +{{- end }} + volumeMounts: + {{- if .Values.artifactory.customPersistentVolumeClaim }} + - name: {{ .Values.artifactory.customPersistentVolumeClaim.name }} + mountPath: {{ .Values.artifactory.customPersistentVolumeClaim.mountPath }} + {{- end }} + - name: artifactory-volume + mountPath: {{ .Values.artifactory.persistence.mountPath | quote }} + + ######################## Artifactory persistence nfs ########################## + {{- if eq .Values.artifactory.persistence.type "nfs" }} + - name: artifactory-data + mountPath: "{{ .Values.artifactory.persistence.nfs.dataDir }}" + - name: artifactory-backup + mountPath: "{{ .Values.artifactory.persistence.nfs.backupDir }}" + {{- else }} + + ######################## Artifactory persistence googleStorage ########################## + {{- if .Values.artifactory.persistence.googleStorage.gcpServiceAccount.enabled }} + {{- if or (not .Values.artifactory.unifiedSecretInstallation) .Values.artifactory.persistence.googleStorage.gcpServiceAccount.customSecretName }} + - name: gcpcreds-json + {{- else }} + - name: {{ include "artifactory.unifiedCustomSecretVolumeName" . }} + {{- end }} + mountPath: "/artifactory_bootstrap/gcp.credentials.json" + subPath: gcp.credentials.json + {{- end }} + {{- end }} + {{- if or .Values.artifactory.customVolumeMounts .Values.global.customVolumeMounts }} +{{ tpl (include "artifactory.customVolumeMounts" .) . | indent 8 }} + {{- end }} + {{- if .Values.access.startupProbe.enabled }} + startupProbe: +{{ tpl .Values.access.startupProbe.config . | indent 10 }} + {{- end }} + {{- if semverCompare " + exec /opt/jfrog/artifactory/app/topology/bin/entrypoint-topology.sh + {{- with .Values.topology.lifecycle }} + lifecycle: +{{ toYaml . | indent 10 }} + {{- end }} + env: + - name: JF_SHARED_NODE_ID + valueFrom: + fieldRef: + fieldPath: metadata.name + {{- if or .Values.database.secrets.user .Values.database.user }} + - name: JF_SHARED_DATABASE_USERNAME + valueFrom: + secretKeyRef: + {{- if .Values.database.secrets.user }} + name: {{ tpl .Values.database.secrets.user.name . }} + key: {{ tpl .Values.database.secrets.user.key . }} + {{- else if .Values.database.user }} + {{- if not .Values.artifactory.unifiedSecretInstallation }} + name: {{ template "artifactory.fullname" . }}-database-creds + {{- else }} + name: "{{ template "artifactory.unifiedSecretPrependReleaseName" . }}-unified-secret" + {{- end }} + key: db-user + {{- end }} + {{- end }} + {{ if or .Values.database.secrets.password .Values.database.password .Values.postgresql.enabled }} + - name: JF_SHARED_DATABASE_PASSWORD + valueFrom: + secretKeyRef: + {{- if .Values.database.secrets.password }} + name: {{ tpl .Values.database.secrets.password.name . }} + key: {{ tpl .Values.database.secrets.password.key . }} + {{- else if .Values.database.password }} + {{- if not .Values.artifactory.unifiedSecretInstallation }} + name: {{ template "artifactory.fullname" . }}-database-creds + {{- else }} + name: "{{ template "artifactory.unifiedSecretPrependReleaseName" . }}-unified-secret" + {{- end }} + key: db-password + {{- else if .Values.postgresql.enabled }} + name: {{ .Release.Name }}-postgresql + key: postgresql-password + {{- end }} + {{- end }} + {{- if or .Values.database.secrets.url .Values.database.url }} + - name: JF_SHARED_DATABASE_URL + valueFrom: + secretKeyRef: + {{- if .Values.database.secrets.url }} + name: {{ tpl .Values.database.secrets.url.name . }} + key: {{ tpl .Values.database.secrets.url.key . }} + {{- else if .Values.database.url }} + {{- if not .Values.artifactory.unifiedSecretInstallation }} + name: {{ template "artifactory.fullname" . }}-database-creds + {{- else }} + name: "{{ template "artifactory.unifiedSecretPrependReleaseName" . }}-unified-secret" + {{- end }} + key: db-url + {{- end }} + {{- end }} +{{- with .Values.topology.extraEnvironmentVariables }} +{{ tpl (toYaml .) $ | indent 8 }} +{{- end }} + ports: + - containerPort: {{ .Values.topology.internalPort }} + name: http-topology + volumeMounts: + {{- if or .Values.artifactory.customVolumeMounts .Values.global.customVolumeMounts }} +{{ tpl (include "artifactory.customVolumeMounts" .) . | indent 8 }} + {{- end }} + - name: artifactory-volume + mountPath: {{ .Values.artifactory.persistence.mountPath | quote }} + resources: +{{ toYaml .Values.topology.resources | indent 10 }} + {{- if .Values.topology.startupProbe.enabled }} + startupProbe: +{{ tpl .Values.topology.startupProbe.config . | indent 10 }} + {{- end }} + {{- if .Values.topology.readinessProbe.enabled }} + readinessProbe: +{{ tpl .Values.topology.readinessProbe.config . | indent 10 }} + {{- end }} + {{- if .Values.topology.livenessProbe.enabled }} + livenessProbe: +{{ tpl .Values.topology.livenessProbe.config . | indent 10 }} + {{- end }} + {{- end }} + {{- if .Values.observability.enabled }} + - name: {{ .Values.observability.name }} + image: {{ include "artifactory.getImageInfoByValue" (list . "artifactory") }} + imagePullPolicy: {{ .Values.artifactory.image.pullPolicy }} + {{- if .Values.containerSecurityContext.enabled }} + securityContext: {{- omit .Values.containerSecurityContext "enabled" | toYaml | nindent 10 }} + {{- end }} + command: + - '/bin/bash' + - '-c' + - > + exec /opt/jfrog/artifactory/app/observability/bin/jf-observability start + {{- with .Values.observability.lifecycle }} + lifecycle: +{{ toYaml . | indent 10 }} + {{- end }} + env: + - name: JF_SHARED_NODE_ID + valueFrom: + fieldRef: + fieldPath: metadata.name +{{- with .Values.observability.extraEnvironmentVariables }} +{{ tpl (toYaml .) $ | indent 8 }} +{{- end }} + volumeMounts: + {{- if or .Values.artifactory.customVolumeMounts .Values.global.customVolumeMounts }} +{{ tpl (include "artifactory.customVolumeMounts" .) . | indent 8 }} + {{- end }} + - name: artifactory-volume + mountPath: {{ .Values.artifactory.persistence.mountPath | quote }} + resources: +{{ toYaml .Values.observability.resources | indent 10 }} + {{- if .Values.observability.startupProbe.enabled }} + startupProbe: +{{ tpl .Values.observability.startupProbe.config . | indent 10 }} + {{- end }} + {{- if .Values.observability.livenessProbe.enabled }} + livenessProbe: +{{ tpl .Values.observability.livenessProbe.config . | indent 10 }} + {{- end }} + {{- end }} + {{- end }} + + - name: {{ .Values.artifactory.name }} + image: {{ include "artifactory.getImageInfoByValue" (list . "artifactory") }} + imagePullPolicy: {{ .Values.artifactory.image.pullPolicy }} + {{- if .Values.containerSecurityContext.enabled }} + securityContext: {{- omit .Values.containerSecurityContext "enabled" | toYaml | nindent 10 }} + {{- end }} + {{- if .Values.artifactory.resources }} + resources: +{{ toYaml .Values.artifactory.resources | indent 10 }} + {{- end }} + command: + - '/bin/bash' + - '-c' + - > + set -e; + if [ -d /artifactory_extra_conf ] && [ -d /artifactory_bootstrap ]; then + echo "Copying bootstrap config from /artifactory_extra_conf to /artifactory_bootstrap"; + cp -Lrfv /artifactory_extra_conf/ /artifactory_bootstrap/; + fi; + {{- if .Values.artifactory.configMapName }} + echo "Copying bootstrap configs"; + cp -Lrf /bootstrap/* /artifactory_bootstrap/; + {{- end }} + {{- if .Values.artifactory.userPluginSecrets }} + echo "Copying plugins"; + cp -Lrf /tmp/plugin/*/* /artifactory_bootstrap/plugins; + {{- end }} + {{- range .Values.artifactory.copyOnEveryStartup }} + {{- $targetPath := printf "%s/%s" $.Values.artifactory.persistence.mountPath .target }} + {{- $baseDirectory := regexFind ".*/" $targetPath }} + mkdir -p {{ $baseDirectory }}; + cp -Lrf {{ .source }} {{ $.Values.artifactory.persistence.mountPath }}/{{ .target }}; + {{- end }} + {{- if .Values.artifactory.preStartCommand }} + echo "Running custom preStartCommand command"; + {{ tpl .Values.artifactory.preStartCommand . }}; + {{- end }} + exec /entrypoint-artifactory.sh + {{- with .Values.artifactory.lifecycle }} + lifecycle: +{{ toYaml . | indent 10 }} + {{- end }} + env: + {{- if and (gt (.Values.artifactory.replicaCount | int64) 1) (eq (include "artifactory.isImageProType" .) "true") (eq (include "artifactory.isUsingDerby" .) "false") }} + - name : JF_SHARED_NODE_HAENABLED + value: "true" + {{- end }} + {{- if .Values.aws.license.enabled }} + - name: IS_AWS_LICENSE + value: "true" + - name: AWS_REGION + value: {{ .Values.aws.region | quote }} + {{- if .Values.aws.licenseConfigSecretName }} + - name: AWS_WEB_IDENTITY_REFRESH_TOKEN_FILE + value: "/var/run/secrets/product-license/license_token" + - name: AWS_ROLE_ARN + valueFrom: + secretKeyRef: + name: {{ .Values.aws.licenseConfigSecretName }} + key: iam_role + {{- end }} + {{- end }} + {{- if .Values.splitServicesToContainers }} + - name : JF_ROUTER_ENABLED + value: "true" + - name : JF_ROUTER_SERVICE_ENABLED + value: "false" + - name : JF_EVENT_ENABLED + value: "false" + - name : JF_METADATA_ENABLED + value: "false" + - name : JF_FRONTEND_ENABLED + value: "false" + - name: JF_FEDERATION_ENABLED + value: "false" + - name : JF_OBSERVABILITY_ENABLED + value: "false" + - name : JF_JFCONNECT_SERVICE_ENABLED + value: "false" + - name : JF_EVIDENCE_ENABLED + value: "false" + - name : JF_ONEMODEL_ENABLED + value: "false" + {{- if not (.Values.access.runOnArtifactoryTomcat | default false) }} + - name : JF_ACCESS_ENABLED + value: "false" + {{- end}} + - name: JF_TOPOLOGY_SPLIT_CONTAINER_ENABLED + value: "true" + {{- end}} + {{- if and (not .Values.waitForDatabase) (not .Values.postgresql.enabled) }} + - name: SKIP_WAIT_FOR_EXTERNAL_DB + value: "true" + {{- end }} + {{- if or .Values.database.secrets.user .Values.database.user }} + - name: JF_SHARED_DATABASE_USERNAME + valueFrom: + secretKeyRef: + {{- if .Values.database.secrets.user }} + name: {{ tpl .Values.database.secrets.user.name . }} + key: {{ tpl .Values.database.secrets.user.key . }} + {{- else if .Values.database.user }} + {{- if not .Values.artifactory.unifiedSecretInstallation }} + name: {{ template "artifactory.fullname" . }}-database-creds + {{- else }} + name: "{{ template "artifactory.unifiedSecretPrependReleaseName" . }}-unified-secret" + {{- end }} + key: db-user + {{- end }} + {{- end }} + {{ if or .Values.database.secrets.password .Values.database.password .Values.postgresql.enabled }} + - name: JF_SHARED_DATABASE_PASSWORD + valueFrom: + secretKeyRef: + {{- if .Values.database.secrets.password }} + name: {{ tpl .Values.database.secrets.password.name . }} + key: {{ tpl .Values.database.secrets.password.key . }} + {{- else if .Values.database.password }} + {{- if not .Values.artifactory.unifiedSecretInstallation }} + name: {{ template "artifactory.fullname" . }}-database-creds + {{- else }} + name: "{{ template "artifactory.unifiedSecretPrependReleaseName" . }}-unified-secret" + {{- end }} + key: db-password + {{- else if .Values.postgresql.enabled }} + name: {{ .Release.Name }}-postgresql + key: postgresql-password + {{- end }} + {{- end }} + {{- if or .Values.database.secrets.url .Values.database.url }} + - name: JF_SHARED_DATABASE_URL + valueFrom: + secretKeyRef: + {{- if .Values.database.secrets.url }} + name: {{ tpl .Values.database.secrets.url.name . }} + key: {{ tpl .Values.database.secrets.url.key . }} + {{- else if .Values.database.url }} + {{- if not .Values.artifactory.unifiedSecretInstallation }} + name: {{ template "artifactory.fullname" . }}-database-creds + {{- else }} + name: "{{ template "artifactory.unifiedSecretPrependReleaseName" . }}-unified-secret" + {{- end }} + key: db-url + {{- end }} + {{- end }} +{{- with .Values.artifactory.extraEnvironmentVariables }} +{{ tpl (toYaml .) $ | indent 8 }} +{{- end }} + ports: + - containerPort: {{ .Values.artifactory.internalPort }} + name: http + - containerPort: {{ .Values.artifactory.internalArtifactoryPort }} + name: http-internal + {{- if .Values.artifactory.javaOpts.jmx.enabled }} + - containerPort: {{ .Values.artifactory.javaOpts.jmx.port }} + name: tcp-jmx + {{- end }} + {{- if .Values.artifactory.ssh.enabled }} + - containerPort: {{ .Values.artifactory.ssh.internalPort }} + name: tcp-ssh + {{- end }} + volumeMounts: + {{- if .Values.artifactory.customPersistentVolumeClaim }} + - name: {{ .Values.artifactory.customPersistentVolumeClaim.name }} + mountPath: {{ .Values.artifactory.customPersistentVolumeClaim.mountPath }} + {{- end }} + {{- if .Values.aws.licenseConfigSecretName }} + - name: awsmp-product-license + mountPath: "/var/run/secrets/product-license" + {{- end }} + {{- if .Values.artifactory.userPluginSecrets }} + - name: bootstrap-plugins + mountPath: "/artifactory_bootstrap/plugins/" + {{- range .Values.artifactory.userPluginSecrets }} + - name: {{ tpl . $ }} + mountPath: "/tmp/plugin/{{ tpl . $ }}" + {{- end }} + {{- end }} + - name: artifactory-volume + mountPath: {{ .Values.artifactory.persistence.mountPath | quote }} + + ######################## Artifactory config map ########################## + {{- if .Values.artifactory.configMapName }} + - name: bootstrap-config + mountPath: "/bootstrap/" + {{- end }} + + ######################## Artifactory persistence nfs ########################## + {{- if eq .Values.artifactory.persistence.type "nfs" }} + - name: artifactory-data + mountPath: "{{ .Values.artifactory.persistence.nfs.dataDir }}" + - name: artifactory-backup + mountPath: "{{ .Values.artifactory.persistence.nfs.backupDir }}" + {{- else }} + + ######################## Artifactory persistence binarystoreXml ########################## + {{- if or (not .Values.artifactory.unifiedSecretInstallation) .Values.artifactory.persistence.customBinarystoreXmlSecret }} + - name: binarystore-xml + {{- else }} + - name: {{ include "artifactory.unifiedCustomSecretVolumeName" . }} + {{- end }} + mountPath: "/tmp/etc/artifactory/binarystore.xml" + subPath: binarystore.xml + + ######################## Artifactory persistence googleStorage ########################## + {{- if .Values.artifactory.persistence.googleStorage.gcpServiceAccount.enabled }} + {{- if or (not .Values.artifactory.unifiedSecretInstallation) .Values.artifactory.persistence.googleStorage.gcpServiceAccount.customSecretName }} + - name: gcpcreds-json + {{- else }} + - name: {{ include "artifactory.unifiedCustomSecretVolumeName" . }} + {{- end }} + mountPath: "/artifactory_bootstrap/gcp.credentials.json" + subPath: gcp.credentials.json + {{- end }} + {{- end }} + + ######################## Artifactory license ########################## + {{- if or .Values.artifactory.license.secret .Values.artifactory.license.licenseKey }} + {{- if or (not .Values.artifactory.unifiedSecretInstallation) .Values.artifactory.license.secret }} + - name: artifactory-license + {{- else }} + - name: {{ include "artifactory.unifiedCustomSecretVolumeName" . }} + {{- end }} + mountPath: "/artifactory_bootstrap/artifactory.cluster.license" + {{- if .Values.artifactory.license.secret }} + subPath: {{ .Values.artifactory.license.dataKey }} + {{- else if .Values.artifactory.license.licenseKey }} + subPath: artifactory.lic + {{- end }} + {{- end }} + + - name: installer-info + mountPath: "/artifactory_bootstrap/info/installer-info.json" + subPath: installer-info.json + {{- if or .Values.artifactory.customVolumeMounts .Values.global.customVolumeMounts }} +{{ tpl (include "artifactory.customVolumeMounts" .) . | indent 8 }} + {{- end }} + {{- if .Values.artifactory.startupProbe.enabled }} + startupProbe: +{{ tpl .Values.artifactory.startupProbe.config . | indent 10 }} + {{- end }} + {{- if and (not .Values.splitServicesToContainers) (semverCompare "=1.18.0-0" .Capabilities.KubeVersion.GitVersion) }} + ingressClassName: {{ .Values.ingressGrpc.className }} + {{- end }} + {{- if .Values.ingressGrpc.defaultBackend.enabled }} + {{- if .Capabilities.APIVersions.Has "networking.k8s.io/v1" }} + defaultBackend: + service: + name: {{ $serviceName }} + port: + number: {{ $servicePort }} + {{- else }} + backend: + serviceName: {{ $serviceName }} + servicePort: {{ $servicePort }} + {{- end }} + {{- end }} + rules: +{{- if .Values.ingressGrpc.hosts }} + {{- if .Capabilities.APIVersions.Has "networking.k8s.io/v1" }} + {{- range $host := .Values.ingressGrpc.hosts }} + - host: {{ $host | quote }} + http: + paths: + - path: {{ $.Values.ingressGrpc.grpcPath }} + pathType: {{ $.Values.ingressGrpc.pathType | default "ImplementationSpecific" }} + backend: + service: + name: {{ $serviceName }} + port: + number: {{ $servicePort }} + {{- end }} + {{- else }} + {{- range $host := .Values.ingressGrpc.hosts }} + - host: {{ $host | quote }} + http: + paths: + - path: {{ $.Values.ingressGrpc.grpcPath }} + backend: + serviceName: {{ $serviceName }} + servicePort: {{ $servicePort }} + {{- end }} + {{- end }} +{{- end -}} + {{- with .Values.ingressGrpc.additionalRules }} +{{ tpl . $ | indent 2 }} + {{- end }} + + {{- if .Values.ingressGrpc.tls }} + tls: +{{ toYaml .Values.ingressGrpc.tls | indent 4 }} + {{- end -}} + +{{- end -}} diff --git a/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/templates/ingress.yaml b/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/templates/ingress.yaml new file mode 100644 index 0000000000..7a3669161e --- /dev/null +++ b/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/templates/ingress.yaml @@ -0,0 +1,110 @@ +{{- if .Values.ingress.enabled -}} +{{- $serviceName := include "artifactory.fullname" . -}} +{{- $servicePort := .Values.artifactory.externalPort -}} +{{- $serviceRTFSName := include "rtfs.fullname" . -}} +{{- $artifactoryServicePort := .Values.artifactory.externalArtifactoryPort -}} +{{- $ingressName := default ( include "artifactory.fullname" . ) .Values.ingress.name -}} +{{- if .Capabilities.APIVersions.Has "networking.k8s.io/v1" }} +apiVersion: networking.k8s.io/v1 +{{- else if .Capabilities.APIVersions.Has "networking.k8s.io/v1beta1" }} +apiVersion: networking.k8s.io/v1beta1 +{{- else }} +apiVersion: extensions/v1beta1 +{{- end }} +kind: Ingress +metadata: + name: {{ $ingressName }} + labels: + app: {{ template "artifactory.name" . }} + chart: {{ template "artifactory.chart" . }} + release: {{ .Release.Name }} + heritage: {{ .Release.Service }} +{{- if .Values.ingress.labels }} +{{ .Values.ingress.labels | toYaml | trimSuffix "\n"| indent 4 -}} +{{- end}} +{{- if .Values.ingress.annotations }} + annotations: +{{ .Values.ingress.annotations | toYaml | trimSuffix "\n" | indent 4 -}} +{{- end }} +spec: + {{- if and .Values.ingress.className (semverCompare ">=1.18.0-0" .Capabilities.KubeVersion.GitVersion) }} + ingressClassName: {{ .Values.ingress.className }} + {{- end }} + {{- if .Values.ingress.defaultBackend.enabled }} + {{- if .Capabilities.APIVersions.Has "networking.k8s.io/v1" }} + defaultBackend: + service: + name: {{ $serviceName }} + port: + number: {{ $servicePort }} + {{- else }} + backend: + serviceName: {{ $serviceName }} + servicePort: {{ $servicePort }} + {{- end }} + {{- end }} + rules: +{{- if .Values.ingress.hosts }} + {{- if .Capabilities.APIVersions.Has "networking.k8s.io/v1" }} + {{- range $host := .Values.ingress.hosts }} + - host: {{ $host | quote }} + http: + paths: + - path: {{ $.Values.ingress.routerPath }} + pathType: {{ $.Values.ingress.pathType | default "ImplementationSpecific" }} + backend: + service: + name: {{ $serviceName }} + port: + number: {{ $servicePort }} + {{- if not $.Values.ingress.disableRouterBypass }} + - path: {{ $.Values.ingress.artifactoryPath }} + pathType: {{ $.Values.ingress.pathType | default "ImplementationSpecific" }} + backend: + service: + name: {{ $serviceName }} + port: + number: {{ $artifactoryServicePort }} + {{- end }} + {{- if and $.Values.rtfs.enabled $.Values.ingress.enabled (not (regexMatch "^.*(oss|cpp-ce|jcr).*$" $.Values.artifactory.image.repository)) }} + - path: {{ $.Values.ingress.rtfsPath }} + pathType: {{ $.Values.ingress.pathType | default "ImplementationSpecific" }} + backend: + service: + name: {{ $serviceRTFSName }} + port: + name: http-router + {{- end }} + {{- end }} + {{- else }} + {{- range $host := .Values.ingress.hosts }} + - host: {{ $host | quote }} + http: + paths: + - path: {{ $.Values.ingress.routerPath }} + backend: + serviceName: {{ $serviceName }} + servicePort: {{ $servicePort }} + {{- if not $.Values.ingress.disableRouterBypass }} + - path: {{ $.Values.ingress.artifactoryPath }} + backend: + serviceName: {{ $serviceName }} + servicePort: {{ $artifactoryServicePort }} + {{- end }} + {{- end }} + {{- end }} +{{- end -}} + {{- with .Values.ingress.additionalRules }} +{{ tpl . $ | indent 2 }} + {{- end }} + + {{- if .Values.ingress.tls }} + tls: +{{ toYaml .Values.ingress.tls | indent 4 }} + {{- end -}} + +{{- if .Values.customIngress }} +--- +{{ .Values.customIngress | toYaml | trimSuffix "\n" }} +{{- end -}} +{{- end -}} diff --git a/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/templates/logger-configmap.yaml b/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/templates/logger-configmap.yaml new file mode 100644 index 0000000000..41a078b024 --- /dev/null +++ b/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/templates/logger-configmap.yaml @@ -0,0 +1,63 @@ +{{- if or .Values.artifactory.loggers .Values.artifactory.catalinaLoggers }} +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ template "artifactory.fullname" . }}-logger + labels: + app: {{ template "artifactory.name" . }} + chart: {{ template "artifactory.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} +data: + tail-log.sh: | + #!/bin/sh + + LOG_DIR=$1 + LOG_NAME=$2 + PID= + + # Wait for log dir to appear + while [ ! -d ${LOG_DIR} ]; do + sleep 1 + done + + cd ${LOG_DIR} + + LOG_PREFIX=$(echo ${LOG_NAME} | sed 's/.log$//g') + + # Find the log to tail + LOG_FILE=$(ls -1t ./${LOG_PREFIX}.log 2>/dev/null) + + # Wait for the log file + while [ -z "${LOG_FILE}" ]; do + sleep 1 + LOG_FILE=$(ls -1t ./${LOG_PREFIX}.log 2>/dev/null) + done + + echo "Log file ${LOG_FILE} is ready!" + + # Get inode number + INODE_ID=$(ls -i ${LOG_FILE}) + + # echo "Tailing ${LOG_FILE}" + tail -F ${LOG_FILE} & + PID=$! + + # Loop forever to see if a new log was created + while true; do + # Check inode number + NEW_INODE_ID=$(ls -i ${LOG_FILE}) + + # If inode number changed, this means log was rotated and need to start a new tail + if [ "${INODE_ID}" != "${NEW_INODE_ID}" ]; then + kill -9 ${PID} 2>/dev/null + INODE_ID="${NEW_INODE_ID}" + + # Start a new tail + tail -F ${LOG_FILE} & + PID=$! + fi + sleep 1 + done + +{{- end }} diff --git a/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/templates/nginx-artifactory-conf.yaml b/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/templates/nginx-artifactory-conf.yaml new file mode 100644 index 0000000000..3434489943 --- /dev/null +++ b/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/templates/nginx-artifactory-conf.yaml @@ -0,0 +1,18 @@ +{{- if and (not .Values.nginx.customArtifactoryConfigMap) .Values.nginx.enabled }} +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ template "artifactory.fullname" . }}-nginx-artifactory-conf + labels: + app: {{ template "artifactory.name" . }} + chart: {{ template "artifactory.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} +data: + artifactory.conf: | +{{- if .Values.nginx.artifactoryConf }} +{{ tpl .Values.nginx.artifactoryConf . | indent 4 }} +{{- else }} +{{ tpl ( .Files.Get "files/nginx-artifactory-conf.yaml" ) . | indent 4 }} +{{- end }} +{{- end }} \ No newline at end of file diff --git a/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/templates/nginx-certificate-secret.yaml b/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/templates/nginx-certificate-secret.yaml new file mode 100644 index 0000000000..f13d401747 --- /dev/null +++ b/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/templates/nginx-certificate-secret.yaml @@ -0,0 +1,14 @@ +{{- if and (not .Values.nginx.tlsSecretName) .Values.nginx.enabled .Values.nginx.https.enabled }} +apiVersion: v1 +kind: Secret +type: kubernetes.io/tls +metadata: + name: {{ template "artifactory.fullname" . }}-nginx-certificate + labels: + app: {{ template "artifactory.name" . }} + chart: {{ template "artifactory.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} +data: +{{ ( include "artifactory.gen-certs" . ) | indent 2 }} +{{- end }} diff --git a/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/templates/nginx-conf.yaml b/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/templates/nginx-conf.yaml new file mode 100644 index 0000000000..31219d58a9 --- /dev/null +++ b/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/templates/nginx-conf.yaml @@ -0,0 +1,18 @@ +{{- if and (not .Values.nginx.customConfigMap) .Values.nginx.enabled }} +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ template "artifactory.fullname" . }}-nginx-conf + labels: + app: {{ template "artifactory.name" . }} + chart: {{ template "artifactory.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} +data: + nginx.conf: | +{{- if .Values.nginx.mainConf }} +{{ tpl .Values.nginx.mainConf . | indent 4 }} +{{- else }} +{{ tpl ( .Files.Get "files/nginx-main-conf.yaml" ) . | indent 4 }} +{{- end }} +{{- end }} \ No newline at end of file diff --git a/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/templates/nginx-deployment.yaml b/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/templates/nginx-deployment.yaml new file mode 100644 index 0000000000..774bedccaf --- /dev/null +++ b/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/templates/nginx-deployment.yaml @@ -0,0 +1,223 @@ +{{- if .Values.nginx.enabled -}} +{{- $serviceName := include "artifactory.fullname" . -}} +{{- $servicePort := .Values.artifactory.externalPort -}} +apiVersion: apps/v1 +kind: {{ .Values.nginx.kind }} +metadata: + name: {{ template "artifactory.nginx.fullname" . }} + labels: + app: {{ template "artifactory.name" . }} + chart: {{ template "artifactory.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: {{ .Values.nginx.name }} +{{- if .Values.nginx.labels }} +{{ toYaml .Values.nginx.labels | indent 4 }} +{{- end }} +{{- with .Values.nginx.deployment.annotations }} + annotations: +{{ toYaml . | indent 4 }} +{{- end }} +spec: +{{- if eq .Values.nginx.kind "StatefulSet" }} + serviceName: {{ template "artifactory.nginx.fullname" . }} +{{- end }} +{{- if ne .Values.nginx.kind "DaemonSet" }} + replicas: {{ .Values.nginx.replicaCount }} +{{- end }} + selector: + matchLabels: + app: {{ template "artifactory.name" . }} + release: {{ .Release.Name }} + component: {{ .Values.nginx.name }} + template: + metadata: + annotations: + checksum/nginx-conf: {{ include (print $.Template.BasePath "/nginx-conf.yaml") . | sha256sum }} + checksum/nginx-artifactory-conf: {{ include (print $.Template.BasePath "/nginx-artifactory-conf.yaml") . | sha256sum }} + {{- range $key, $value := .Values.nginx.annotations }} + {{ $key }}: {{ $value | quote }} + {{- end }} + labels: + app: {{ template "artifactory.name" . }} + chart: {{ template "artifactory.chart" . }} + component: {{ .Values.nginx.name }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} +{{- if .Values.nginx.labels }} +{{ toYaml .Values.nginx.labels | indent 8 }} +{{- end }} + spec: + {{- if .Values.nginx.podSecurityContext.enabled }} + securityContext: {{- omit .Values.nginx.podSecurityContext "enabled" | toYaml | nindent 8 }} + {{- end }} + serviceAccountName: {{ template "artifactory.serviceAccountName" . }} + terminationGracePeriodSeconds: {{ .Values.nginx.terminationGracePeriodSeconds }} + {{- if or .Values.imagePullSecrets .Values.global.imagePullSecrets }} +{{- include "artifactory.imagePullSecrets" . | indent 6 }} + {{- end }} + {{- if .Values.nginx.priorityClassName }} + priorityClassName: {{ .Values.nginx.priorityClassName | quote }} + {{- end }} + {{- if .Values.nginx.topologySpreadConstraints }} + topologySpreadConstraints: +{{ tpl (toYaml .Values.nginx.topologySpreadConstraints) . | indent 8 }} + {{- end }} + initContainers: + {{- if .Values.nginx.customInitContainers }} +{{ tpl (include "artifactory.nginx.customInitContainers" .) . | indent 6 }} + {{- end }} + - name: "setup" + image: {{ include "artifactory.getImageInfoByValue" (list . "initContainers") }} + imagePullPolicy: {{ .Values.initContainers.image.pullPolicy }} + {{- if .Values.containerSecurityContext.enabled }} + securityContext: {{- omit .Values.containerSecurityContext "enabled" | toYaml | nindent 10 }} + {{- end }} + command: + - '/bin/sh' + - '-c' + - > + rm -rfv {{ .Values.nginx.persistence.mountPath }}/lost+found; + mkdir -p {{ .Values.nginx.persistence.mountPath }}/logs; + resources: + {{- toYaml .Values.initContainers.resources | nindent 10 }} + volumeMounts: + - mountPath: {{ .Values.nginx.persistence.mountPath | quote }} + name: nginx-volume + containers: + - name: {{ .Values.nginx.name }} + image: {{ include "artifactory.getImageInfoByValue" (list . "nginx") }} + imagePullPolicy: {{ .Values.nginx.image.pullPolicy }} + {{- if .Values.nginx.containerSecurityContext.enabled }} + securityContext: {{- omit .Values.nginx.containerSecurityContext "enabled" | toYaml | nindent 10 }} + {{- end }} + {{- if .Values.nginx.customCommand }} + command: +{{- tpl (include "nginx.command" .) . | indent 10 }} + {{- end }} + ports: +{{ if .Values.nginx.customPorts }} +{{ toYaml .Values.nginx.customPorts | indent 8 }} +{{ end }} + # DEPRECATION NOTE: The following is to maintain support for values pre 1.3.1 and + # will be cleaned up in a later version + {{- if .Values.nginx.http }} + {{- if .Values.nginx.http.enabled }} + - containerPort: {{ .Values.nginx.http.internalPort }} + name: http + {{- end }} + {{- else }} # DEPRECATED + - containerPort: {{ .Values.nginx.internalPortHttp }} + name: http-internal + {{- end }} + {{- if .Values.nginx.https }} + {{- if .Values.nginx.https.enabled }} + - containerPort: {{ .Values.nginx.https.internalPort }} + name: https + {{- end }} + {{- else }} # DEPRECATED + - containerPort: {{ .Values.nginx.internalPortHttps }} + name: https-internal + {{- end }} + {{- if .Values.artifactory.ssh.enabled }} + - containerPort: {{ .Values.nginx.ssh.internalPort }} + name: tcp-ssh + {{- end }} + {{- with .Values.nginx.lifecycle }} + lifecycle: +{{ toYaml . | indent 10 }} + {{- end }} + volumeMounts: + - name: nginx-conf + mountPath: /etc/nginx/nginx.conf + subPath: nginx.conf + - name: nginx-artifactory-conf + mountPath: "{{ .Values.nginx.persistence.mountPath }}/conf.d/" + - name: nginx-volume + mountPath: {{ .Values.nginx.persistence.mountPath | quote }} + {{- if .Values.nginx.https.enabled }} + - name: ssl-certificates + mountPath: "{{ .Values.nginx.persistence.mountPath }}/ssl" + {{- end }} + {{- if .Values.nginx.customVolumeMounts }} +{{ tpl (include "artifactory.nginx.customVolumeMounts" .) . | indent 8 }} + {{- end }} + resources: +{{ toYaml .Values.nginx.resources | indent 10 }} + {{- if .Values.nginx.startupProbe.enabled }} + startupProbe: +{{ tpl .Values.nginx.startupProbe.config . | indent 10 }} + {{- end }} + {{- if .Values.nginx.readinessProbe.enabled }} + readinessProbe: +{{ tpl .Values.nginx.readinessProbe.config . | indent 10 }} + {{- end }} + {{- if .Values.nginx.livenessProbe.enabled }} + livenessProbe: +{{ tpl .Values.nginx.livenessProbe.config . | indent 10 }} + {{- end }} + {{- $mountPath := .Values.nginx.persistence.mountPath }} + {{- range .Values.nginx.loggers }} + - name: {{ . | replace "_" "-" | replace "." "-" }} + image: {{ include "artifactory.getImageInfoByValue" (list $ "initContainers") }} + imagePullPolicy: {{ $.Values.initContainers.image.pullPolicy }} + command: + - tail + args: + - '-F' + - '{{ $mountPath }}/logs/{{ . }}' + volumeMounts: + - name: nginx-volume + mountPath: {{ $mountPath }} + resources: +{{ toYaml $.Values.nginx.loggersResources | indent 10 }} + {{- end }} + {{- if .Values.nginx.customSidecarContainers }} +{{ tpl (include "artifactory.nginx.customSidecarContainers" .) . | indent 6 }} + {{- end }} + {{- if or .Values.nginx.nodeSelector .Values.global.nodeSelector }} +{{ tpl (include "nginx.nodeSelector" .) . | indent 6 }} + {{- end }} + {{- with .Values.nginx.affinity }} + affinity: +{{ toYaml . | indent 8 }} + {{- end }} + {{- with .Values.nginx.tolerations }} + tolerations: +{{ toYaml . | indent 8 }} + {{- end }} + volumes: + {{- if .Values.nginx.customVolumes }} +{{ tpl (include "artifactory.nginx.customVolumes" .) . | indent 6 }} + {{- end }} + - name: nginx-conf + configMap: + {{- if .Values.nginx.customConfigMap }} + name: {{ .Values.nginx.customConfigMap }} + {{- else }} + name: {{ template "artifactory.fullname" . }}-nginx-conf + {{- end }} + - name: nginx-artifactory-conf + configMap: + {{- if .Values.nginx.customArtifactoryConfigMap }} + name: {{ .Values.nginx.customArtifactoryConfigMap }} + {{- else }} + name: {{ template "artifactory.fullname" . }}-nginx-artifactory-conf + {{- end }} + - name: nginx-volume + {{- if .Values.nginx.persistence.enabled }} + persistentVolumeClaim: + claimName: {{ .Values.nginx.persistence.existingClaim | default (include "artifactory.nginx.fullname" .) }} + {{- else }} + emptyDir: {} + {{- end }} + {{- if .Values.nginx.https.enabled }} + - name: ssl-certificates + secret: + {{- if .Values.nginx.tlsSecretName }} + secretName: {{ .Values.nginx.tlsSecretName }} + {{- else }} + secretName: {{ template "artifactory.fullname" . }}-nginx-certificate + {{- end }} + {{- end }} +{{- end }} \ No newline at end of file diff --git a/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/templates/nginx-pdb.yaml b/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/templates/nginx-pdb.yaml new file mode 100644 index 0000000000..dff0c23a3e --- /dev/null +++ b/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/templates/nginx-pdb.yaml @@ -0,0 +1,23 @@ +{{- if .Values.nginx.enabled -}} +{{- if semverCompare " + echo "Copy system.yaml to {{ .Values.rtfs.persistence.mountPath }}/etc"; + mkdir -p {{ .Values.rtfs.persistence.mountPath }}/etc; + {{- if .Values.systemYamlOverride.existingSecret }} + cp -fv /tmp/etc/{{ .Values.systemYamlOverride.dataKey }} {{ .Values.rtfs.persistence.mountPath }}/etc/system.yaml; + {{- else }} + cp -fv /tmp/etc/system.yaml {{ .Values.rtfs.persistence.mountPath }}/etc/system.yaml; + {{- end }} + echo "Copying artifactory-federation properties to {{ .Values.rtfs.persistence.mountPath }}/etc"; + mkdir -p {{ .Values.rtfs.persistence.mountPath }}/etc; + echo "Starting to copy files to artifactory-federation.properties"; + found=false; + for file in /tmp/etc/properties/*; do + if [ -f "$file" ]; then + found=true; + key=$(basename "$file"); + value=$(cat "$file"); + echo "Processing file: $file"; + echo "$key=$value" >> {{ .Values.rtfs.persistence.mountPath }}/etc/artifactory-federation.properties; + fi; + done; + if [ "$found" = false ]; then + echo "No matching files found, creating an empty artifactory-federation.properties file"; + touch {{ .Values.rtfs.persistence.mountPath }}/etc/artifactory-federation.properties; + fi + volumeMounts: + - name: data + mountPath: {{ .Values.rtfs.persistence.mountPath | quote }} + {{- if or (not .Values.artifactory.unifiedSecretInstallation) .Values.systemYamlOverride.existingSecret }} + - name: systemyaml + {{- else }} + - name: {{ include "artifactory.unifiedCustomSecretVolumeName" . }} + {{- end }} + {{- if .Values.systemYamlOverride.existingSecret }} + mountPath: "/tmp/etc/{{.Values.systemYamlOverride.dataKey }}" + subPath: {{ .Values.systemYamlOverride.dataKey }} + {{- else }} + mountPath: "/tmp/etc/system.yaml" + subPath: "system.yaml" + {{- end }} + {{- if or .Values.rtfs.persistence.federationProperties .Values.rtfs.persistence.customFederationPropertiesConfig }} + - name: federationproperties + mountPath: /tmp/etc/properties + {{- end }} + containers: + - name: rtfs + image: {{ include "artifactory.getImageInfoByValue" (list . "rtfs") }} + imagePullPolicy: {{ .Values.initContainers.image.pullPolicy | quote }} + {{- if .Values.rtfs.containerSecurityContext.enabled }} + securityContext: {{- omit .Values.rtfs.containerSecurityContext "enabled" | toYaml | nindent 12 }} + {{- end }} + volumeMounts: + - name: data + mountPath: {{ .Values.rtfs.persistence.mountPath | quote }} + {{- if .Values.rtfs.customVolumeMounts }} +{{ tpl (include "artifactory.rtfs.customVolumeMounts" .) . | indent 12 }} + {{- end }} + env: + - name: JF_SHARED_NODE_ID + valueFrom: + fieldRef: + fieldPath: metadata.name + {{- if .Values.rtfs.javaOpts }} + - name: EXTRA_JAVA_OPTS + value: {{ .Values.rtfs.javaOpts }} + {{- end }} + {{- if or .Values.database.secrets.user .Values.database.user }} + - name: JF_SHARED_DATABASE_USERNAME + valueFrom: + secretKeyRef: + {{- if .Values.database.secrets.user }} + name: {{ tpl .Values.database.secrets.user.name . }} + key: {{ tpl .Values.database.secrets.user.key . }} + {{- else if .Values.database.user }} + {{- if not .Values.artifactory.unifiedSecretInstallation }} + name: {{ template "artifactory.fullname" . }}-database-creds + {{- else }} + name: "{{ template "artifactory.unifiedSecretPrependReleaseName" . }}-unified-secret" + {{- end }} + key: db-user + {{- end }} + {{- end }} + {{ if or .Values.database.secrets.password .Values.database.password .Values.postgresql.enabled }} + - name: JF_SHARED_DATABASE_PASSWORD + valueFrom: + secretKeyRef: + {{- if .Values.database.secrets.password }} + name: {{ tpl .Values.database.secrets.password.name . }} + key: {{ tpl .Values.database.secrets.password.key . }} + {{- else if .Values.database.password }} + {{- if not .Values.artifactory.unifiedSecretInstallation }} + name: {{ template "artifactory.fullname" . }}-database-creds + {{- else }} + name: "{{ template "artifactory.unifiedSecretPrependReleaseName" . }}-unified-secret" + {{- end }} + key: db-password + {{- else if .Values.postgresql.enabled }} + name: {{ .Release.Name }}-postgresql + key: postgresql-password + {{- end }} + {{- end }} + {{- if or .Values.database.secrets.url .Values.database.url }} + - name: JF_SHARED_DATABASE_URL + valueFrom: + secretKeyRef: + {{- if .Values.database.secrets.url }} + name: {{ tpl .Values.database.secrets.url.name . }} + key: {{ tpl .Values.database.secrets.url.key . }} + {{- else if .Values.database.url }} + {{- if not .Values.artifactory.unifiedSecretInstallation }} + name: {{ template "artifactory.fullname" . }}-database-creds + {{- else }} + name: "{{ template "artifactory.unifiedSecretPrependReleaseName" . }}-unified-secret" + {{- end }} + key: db-url + {{- end }} + {{- end }} + {{- if or .Values.artifactory.masterKey .Values.global.masterKey .Values.artifactory.masterKeySecretName .Values.global.masterKeySecretName }} + - name: JF_SHARED_SECURITY_MASTERKEY + valueFrom: + secretKeyRef: + {{- if or (not .Values.artifactory.unifiedSecretInstallation) .Values.artifactory.masterKeySecretName .Values.global.masterKeySecretName }} + name: {{ include "artifactory.masterKeySecretName" . }} + {{- else }} + name: "{{ template "artifactory.unifiedSecretPrependReleaseName" . }}-unified-secret" + {{- end }} + key: master-key + {{- end }} + {{- if or .Values.artifactory.joinKey .Values.global.joinKey .Values.artifactory.joinKeySecretName .Values.global.joinKeySecretName }} + - name: JF_SHARED_SECURITY_JOINKEY + valueFrom: + secretKeyRef: + {{- if or (not .Values.artifactory.unifiedSecretInstallation) .Values.artifactory.joinKeySecretName .Values.global.joinKeySecretName }} + name: {{ include "artifactory.joinKeySecretName" . }} + {{- else }} + name: "{{ template "artifactory.unifiedSecretPrependReleaseName" . }}-unified-secret" + {{- end }} + key: join-key + {{- end }} +{{- with .Values.rtfs.extraEnvironmentVariables }} +{{ tpl (toYaml .) $ | indent 12 }} +{{- end }} + resources: {{- toYaml .Values.rtfs.resources | nindent 12 }} + {{- if .Values.rtfs.startupProbe.enabled }} + startupProbe: +{{ tpl .Values.rtfs.startupProbe.config . | indent 12 }} + {{- end }} + {{- if .Values.rtfs.readinessProbe.enabled }} + readinessProbe: +{{ tpl .Values.rtfs.readinessProbe.config . | indent 12 }} + {{- end }} + {{- if .Values.rtfs.livenessProbe.enabled }} + livenessProbe: +{{ tpl .Values.rtfs.livenessProbe.config . | indent 12 }} + {{- end }} + {{- with .Values.rtfs.lifecycle }} + lifecycle: +{{ toYaml . | indent 12 }} + {{- end }} + - name: router + image: {{ include "artifactory.getImageInfoByValue" (list . "router") }} + imagePullPolicy: {{ .Values.router.image.imagePullPolicy }} + resources: {{- toYaml .Values.router.resources | nindent 12 }} + {{- if .Values.rtfs.containerSecurityContext.enabled }} + securityContext: {{- omit .Values.rtfs.containerSecurityContext "enabled" | toYaml | nindent 12 }} + {{- end }} + command: + - '/bin/bash' + - '-c' + - > + exec /opt/jfrog/router/app/bin/entrypoint-router.sh + env: + - name: JF_ROUTER_TOPOLOGY_LOCAL_REQUIREDSERVICETYPES + value: "jfrtfs" + - name: JF_SHARED_JFROGURL + value: {{ tpl (include "rtfs.jfrogUrl" .) . }} + {{- if or .Values.artifactory.masterKey .Values.global.masterKey .Values.artifactory.masterKeySecretName .Values.global.masterKeySecretName }} + - name: JF_SHARED_SECURITY_MASTERKEY + valueFrom: + secretKeyRef: + {{- if or (not .Values.artifactory.unifiedSecretInstallation) .Values.artifactory.masterKeySecretName .Values.global.masterKeySecretName }} + name: {{ include "artifactory.masterKeySecretName" . }} + {{- else }} + name: "{{ template "artifactory.unifiedSecretPrependReleaseName" . }}-unified-secret" + {{- end }} + key: master-key + {{- end }} + {{- if or .Values.artifactory.joinKey .Values.global.joinKey .Values.artifactory.joinKeySecretName .Values.global.joinKeySecretName }} + - name: JF_SHARED_SECURITY_JOINKEY + valueFrom: + secretKeyRef: + {{- if or (not .Values.artifactory.unifiedSecretInstallation) .Values.artifactory.joinKeySecretName .Values.global.joinKeySecretName }} + name: {{ include "artifactory.joinKeySecretName" . }} + {{- else }} + name: "{{ template "artifactory.unifiedSecretPrependReleaseName" . }}-unified-secret" + {{- end }} + key: join-key + {{- end }} +{{- with .Values.router.extraEnvironmentVariables }} +{{ tpl (toYaml .) $ | indent 12 }} +{{- end }} + ports: + - name: http-router + containerPort: {{ .Values.router.internalPort }} + volumeMounts: + - name: data + mountPath: {{ .Values.router.persistence.mountPath | quote }} + {{- if .Values.router.extraVolumeMounts }} + {{- toYaml .Values.router.extraVolumeMounts | nindent 12 }} + {{- end }} + {{- if .Values.router.startupProbe.enabled }} + startupProbe: +{{ tpl .Values.router.startupProbe.config . | indent 12 }} + {{- end }} + {{- if .Values.router.readinessProbe.enabled }} + readinessProbe: +{{ tpl .Values.router.readinessProbe.config . | indent 12 }} + {{- end }} + {{- if .Values.router.livenessProbe.enabled }} + livenessProbe: +{{ tpl .Values.router.livenessProbe.config . | indent 12 }} + {{- end }} + {{- with .Values.router.lifecycle }} + lifecycle: +{{ toYaml . | indent 12 }} + {{- end }} + {{- if .Values.rtfs.customSidecarContainers }} +{{ tpl (include "artifactory.rtfs.customSidecarContainers" .) . | indent 8 }} + {{- end }} + volumes: + # system yaml + {{- if .Values.systemYamlOverride.existingSecret }} + - name: systemyaml + secret: + secretName: {{ .Values.systemYamlOverride.existingSecret }} + {{- end }} + + ######### unifiedSecretInstallation ########### + {{- if and .Values.artifactory.unifiedSecretInstallation (eq (include "artifactory.checkDuplicateUnifiedCustomVolume" .) "false" ) }} + - name: {{ include "artifactory.unifiedCustomSecretVolumeName" . }} + secret: + secretName: "{{ template "artifactory.unifiedSecretPrependReleaseName" . }}-unified-secret" + {{- else if not .Values.artifactory.unifiedSecretInstallation }} + {{- if not .Values.systemYamlOverride.existingSecret }} + - name: systemyaml + secret: + secretName: {{ printf "%s-%s" (include "artifactory.fullname" .) "systemyaml" }} + {{- end }} + {{- end }} + + {{- if .Values.rtfs.customVolumes }} +{{ tpl (include "artifactory.rtfs.customVolumes" .) . | indent 8 }} + {{- end }} + - name: data + emptyDir: {} + {{- if and (.Values.rtfs.persistence.federationProperties) (not .Values.rtfs.persistence.customFederationPropertiesConfig) }} + - name: federationproperties + configMap: + name: {{ include "rtfs.fullname" . }}-properties + {{- end }} + {{- if .Values.rtfs.persistence.customFederationPropertiesConfig }} + - name: federationproperties + configMap: + name: {{ .Values.rtfs.persistence.customFederationPropertiesConfig }} + {{- end }} + {{- if .Values.rtfs.extraVolumes }} + {{- toYaml .Values.rtfs.extraVolumes | nindent 8 }} + {{- end }} + {{- with .Values.rtfs.nodeSelector }} + nodeSelector: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.rtfs.affinity }} + affinity: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.rtfs.tolerations }} + tolerations: + {{- toYaml . | nindent 8 }} + {{- end }} +{{- end }} \ No newline at end of file diff --git a/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/templates/rtfs-hpa.yaml b/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/templates/rtfs-hpa.yaml new file mode 100644 index 0000000000..fd8b0bcc84 --- /dev/null +++ b/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/templates/rtfs-hpa.yaml @@ -0,0 +1,32 @@ +{{- if .Values.rtfs.autoscaling.enabled }} + {{- if semverCompare ">=v1.23.0-0" .Capabilities.KubeVersion.Version }} +apiVersion: autoscaling/v2 + {{- else }} +apiVersion: autoscaling/v2beta2 + {{- end }} +kind: HorizontalPodAutoscaler +metadata: + labels: + app: {{ template "artifactory.fullname" . }} + chart: {{ template "artifactory.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + name: {{ template "rtfs.fullname" . }} +spec: + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: {{ template "rtfs.fullname" . }} + minReplicas: {{ .Values.rtfs.autoscaling.minReplicas }} + maxReplicas: {{ .Values.rtfs.autoscaling.maxReplicas }} + metrics: + - type: Resource + resource: + name: cpu + target: + type: Utilization + averageUtilization: {{ .Values.rtfs.autoscaling.targetCPUUtilizationPercentage }} + {{- if .Values.rtfs.autoscaling.metrics }} +{{ tpl (include "rtfs.metrics" .) . | indent 4 }} + {{- end }} +{{- end }} \ No newline at end of file diff --git a/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/templates/rtfs-properties-configmap.yaml b/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/templates/rtfs-properties-configmap.yaml new file mode 100644 index 0000000000..c75bec9f2b --- /dev/null +++ b/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/templates/rtfs-properties-configmap.yaml @@ -0,0 +1,17 @@ +{{- if and (.Values.rtfs.persistence.federationProperties) (not .Values.rtfs.persistence.customFederationPropertiesConfig) .Values.rtfs.enabled }} +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ include "rtfs.fullname" . }}-properties + namespace: {{ .Release.Namespace | quote }} + labels: + app: {{ template "artifactory.name" . }} + chart: {{ template "artifactory.chart" . }} + component: {{ .Values.rtfs.name }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} +data: + {{- range $key, $value := .Values.rtfs.persistence.federationProperties }} + {{ $key }}: {{ tpl ($value | default "") . | quote }} + {{- end }} + {{- end }} \ No newline at end of file diff --git a/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/templates/rtfs-service.yaml b/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/templates/rtfs-service.yaml new file mode 100644 index 0000000000..3d16053a63 --- /dev/null +++ b/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/templates/rtfs-service.yaml @@ -0,0 +1,42 @@ +{{- if .Values.rtfs.enabled -}} +apiVersion: v1 +kind: Service +metadata: + name: {{ include "rtfs.fullname" . }} + labels: + app: {{ template "artifactory.name" . }} + chart: {{ template "artifactory.chart" . }} + component: {{ .Values.rtfs.name }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} +{{- if .Values.rtfs.service.labels }} +{{ toYaml .Values.rtfs.service.labels | indent 4 }} +{{- end }} +{{- with .Values.rtfs.service.annotations }} + annotations: +{{ toYaml . | indent 4 }} +{{- end }} +spec: + type: {{ .Values.rtfs.service.type }} + {{- if eq .Values.rtfs.service.type "LoadBalancer" }} + {{- if .Values.rtfs.service.loadBalancerIP }} + loadBalancerIP: {{ .Values.rtfs.service.loadBalancerIP }} + {{- end }} + {{- if .Values.rtfs.service.loadBalancerSourceRanges }} + loadBalancerSourceRanges: + {{- toYaml .Values.rtfs.service.loadBalancerSourceRanges | nindent 4 }} + {{- end }} + {{- end }} + {{- if (or (eq .Values.rtfs.service.type "LoadBalancer") (eq .Values.rtfs.service.type "NodePort")) }} + externalTrafficPolicy: {{ .Values.rtfs.service.externalTrafficPolicy | quote }} + {{- end }} + ports: + - port: {{ .Values.router.externalPort }} + targetPort: {{ .Values.router.internalPort }} + protocol: TCP + name: http-router + selector: + app: {{ template "artifactory.name" . }} + release: {{ .Release.Name }} + component: {{ .Values.rtfs.name }} +{{- end }} \ No newline at end of file diff --git a/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/values.yaml b/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/values.yaml new file mode 100644 index 0000000000..b062296bb4 --- /dev/null +++ b/charts/jfrog/artifactory-jcr/107.104.10/charts/artifactory/values.yaml @@ -0,0 +1,2049 @@ +## Default values for artifactory. +## This is a YAML-formatted file. +## Beware when changing values here. You should know what you are doing! +## Access the values with {{ .Values.key.subkey }} +global: + # imageRegistry: releases-docker.jfrog.io + # imagePullSecrets: + # - myRegistryKeySecretName + ## Chart.AppVersion can be overidden using global.versions.artifactory or .Values.artifactory.image.tag + ## Note: Order of preference is 1) global.versions 2) .Values.artifactory.image.tag 3) Chart.AppVersion + ## This applies also for nginx images (.Values.nginx.image.tag) + versions: {} + # artifactory: + # initContainers: + # rtfs: + # joinKey: + # masterKey: + # joinKeySecretName: + # masterKeySecretName: + + ## Note: tags customInitContainersBegin,customInitContainers,customVolumes,customVolumeMounts,customSidecarContainers can be used both from global and application level simultaneously + # customInitContainersBegin: | + + # customInitContainers: | + + # customVolumes: | + + # customVolumeMounts: | + + # customSidecarContainers: | + + ## certificates added to this secret will be copied to $JFROG_HOME/artifactory/var/etc/security/keys/trusted directory + customCertificates: + enabled: false + # certificateSecretName: + ## Applies to artifactory and nginx pods + nodeSelector: {} +## String to partially override artifactory.fullname template (will maintain the release name) +# nameOverride: + +## String to fully override artifactory.fullname template +# fullnameOverride: + +## Init containers +initContainers: + image: + registry: releases-docker.jfrog.io + repository: ubi9/ubi-minimal + tag: 9.5.1733767867 + pullPolicy: IfNotPresent + resources: + requests: + memory: "50Mi" + cpu: "10m" + limits: + memory: "1Gi" + cpu: "1" +installer: + type: + platform: +## The installerInfo is intentionally commented out and the previous content has been moved under `files/installer-info.json` +## To override the content in `files/installer-info.json`, Uncomment the `installerInfo` and add relevant data +# installerInfo: '{}' + +## For supporting pulling from private registries +# imagePullSecrets: +# - myRegistryKeySecretName + +## Artifactory systemYaml override +## This is for advanced usecases where users wants to provide their own systemYaml for configuring artifactory +## Refer: https://www.jfrog.com/confluence/display/JFROG/Artifactory+System+YAML +## Note: This will override existing (default) .Values.artifactory.systemYaml in values.yaml +## Alternatively, systemYaml can be overidden via customInitContainers using external sources like vaults, external repositories etc. Please refer customInitContainer section below for an example. +## Note: Order of preference is 1) customInitContainers 2) systemYamlOverride existingSecret 3) default systemYaml in values.yaml +systemYamlOverride: + ## You can use a pre-existing secret by specifying existingSecret + existingSecret: + ## The dataKey should be the name of the secret data key created. + dataKey: +## Role Based Access Control +## Ref: https://kubernetes.io/docs/admin/authorization/rbac/ +rbac: + create: false + role: + ## Rules to create. It follows the role specification + rules: + - apiGroups: + - '' + resources: + - services + - endpoints + - pods + verbs: + - get + - watch + - list +## Service Account +## Ref: https://kubernetes.io/docs/admin/service-accounts-admin/ +serviceAccount: + create: false + ## The name of the ServiceAccount to use. + ## If not set and create is true, a name is generated using the fullname template + name: + ## Service Account annotations + annotations: {} + ## Explicitly mounts the API credentials for the Service Account + automountServiceAccountToken: false +ingress: + enabled: false + defaultBackend: + enabled: true + ## Used to create an Ingress record. + hosts: [] + ## Useful in controllers where ImplementationSpecific is considered as a Regex Match and, not a Prefix match + pathType: ImplementationSpecific + routerPath: / + artifactoryPath: /artifactory/ + rtfsPath: /artifactory/service/rtfs/ + className: "" + annotations: {} + # nginx.ingress.kubernetes.io/configuration-snippet: | + # proxy_pass_header Server; + # proxy_set_header X-JFrog-Override-Base-Url https://; + # kubernetes.io/tls-acme: "true" + # nginx.ingress.kubernetes.io/proxy-body-size: "0" + labels: {} + # traffic-type: external + # traffic-type: internal + tls: [] + ## Secrets must be manually created in the namespace. + # - secretName: chart-example-tls + # hosts: + # - artifactory.domain.example + + ## Additional ingress rules + additionalRules: [] + ## This is an experimental feature, enabling this feature will route all traffic through the Router. + disableRouterBypass: false +## Allows to add custom ingress +customIngress: "" +## There is a need to separate HTTP1.1 traffic from gRPC (HTTP2) to benefit +## 1. Use the Nginx keepalive for HTTP1.1 +## 2. Keep gRPC over the HTTP2 connections only +ingressGrpc: + enabled: false + name: grpc + defaultBackend: + enabled: true + ## Used to create an Ingress record. + hosts: [] + ## Useful in controllers where ImplementationSpecific is considered as a Regex Match and, not a Prefix match + pathType: ImplementationSpecific + grpcPath: /com.jfrog + className: "" + ## For supporting GRPC protocol traffic sent to the backend it is necessary to change the backend protocol + ## Example: nginx.ingress.kubernetes.io/backend-protocol: GRPCS + annotations: {} + # nginx.ingress.kubernetes.io/configuration-snippet: | + # proxy_pass_header Server; + # proxy_set_header X-JFrog-Override-Base-Url https://; + # kubernetes.io/tls-acme: "true" + # nginx.ingress.kubernetes.io/proxy-body-size: "0" + labels: {} + # traffic-type: external + # traffic-type: internal + tls: [] + ## Secrets must be manually created in the namespace. + # - secretName: chart-example-tls + # hosts: + # - artifactory.domain.example + + ## Additional ingress rules + additionalRules: [] +networkpolicy: [] +## Allows all ingress and egress +# - name: artifactory +# podSelector: +# matchLabels: +# app: artifactory +# egress: +# - {} +# ingress: +# - {} +## Uncomment to allow only artifactory pods to communicate with postgresql (if postgresql.enabled is true) +# - name: postgresql +# podSelector: +# matchLabels: +# app: postgresql +# ingress: +# - from: +# - podSelector: +# matchLabels: +# app: artifactory + +## Apply horizontal pod auto scaling on artifactory pods +## Ref: https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/ +autoscaling: + enabled: false + minReplicas: 1 + maxReplicas: 3 + targetCPUUtilizationPercentage: 70 +## You can use a pre-existing secret with keys license_token and iam_role by specifying licenseConfigSecretName +## Example : Create a generic secret using `kubectl create secret generic --from-literal=license_token=${TOKEN} --from-literal=iam_role=${ROLE_ARN}` +aws: + license: + enabled: false + licenseConfigSecretName: + region: us-east-1 +## Container Security Context +## ref: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/#set-the-security-context-for-a-container +## @param containerSecurityContext.enabled Enabled containers' Security Context +## @param containerSecurityContext.runAsNonRoot Set container's Security Context runAsNonRoot +## @param containerSecurityContext.privileged Set container's Security Context privileged +## @param containerSecurityContext.allowPrivilegeEscalation Set container's Security Context allowPrivilegeEscalation +## @param containerSecurityContext.capabilities.drop List of capabilities to be dropped +## @param containerSecurityContext.seccompProfile.type Set container's Security Context seccomp profile +## +containerSecurityContext: + enabled: true + runAsNonRoot: true + privileged: false + allowPrivilegeEscalation: false + seccompProfile: + type: RuntimeDefault + capabilities: + drop: + - ALL +## The following router settings are to configure only when splitServicesToContainers set to true +router: + name: router + image: + registry: releases-docker.jfrog.io + repository: jfrog/router + tag: 7.149.1 + pullPolicy: IfNotPresent + serviceRegistry: + ## Service registry (Access) TLS verification skipped if enabled + insecure: false + internalPort: 8082 + externalPort: 8082 + tlsEnabled: false + ## Extra environment variables that can be used to tune router to your needs. + ## Uncomment and set value as needed + extraEnvironmentVariables: + # - name: MY_ENV_VAR + # value: "" + resources: {} + # requests: + # memory: "100Mi" + # cpu: "100m" + # limits: + # memory: "1Gi" + # cpu: "1" + + ## Add lifecycle hooks for router container + lifecycle: + ## From Artifactory versions 7.52.x, Wait for Artifactory to complete any open uploads or downloads before terminating + preStop: + exec: + command: ["sh", "-c", "while [[ $(curl --fail --silent --connect-timeout 2 http://localhost:8081/artifactory/api/v1/system/liveness) =~ OK ]]; do echo Artifactory is still alive; sleep 2; done"] + # postStart: + # exec: + # command: ["/bin/sh", "-c", "echo Hello from the postStart handler"] + ## Add custom volumesMounts + customVolumeMounts: | + # - name: custom-script + # mountPath: /scripts/script.sh + # subPath: script.sh + livenessProbe: + enabled: true + config: | + exec: + command: + - sh + - -c + - curl -s -k --fail --max-time {{ .Values.probes.timeoutSeconds }} {{ include "artifactory.scheme" . }}://localhost:{{ .Values.router.internalPort }}/router/api/v1/system/liveness + initialDelaySeconds: {{ if semverCompare " prepended. + unifiedSecretPrependReleaseName: true + ## For HA installation, set this value > 1. This is only supported in Artifactory 7.25.x (appVersions) and above. + replicaCount: 1 + # minAvailable: 1 + + ## Note that by default we use appVersion to get image tag/version + image: + registry: releases-docker.jfrog.io + repository: jfrog/artifactory-pro + # tag: + pullPolicy: IfNotPresent + labels: {} + updateStrategy: + type: RollingUpdate + ## ref: https://kubernetes.io/docs/tasks/administer-cluster/configure-multiple-schedulers/ + schedulerName: + ## Create a priority class for the Artifactory pod or use an existing one + ## NOTE - Maximum allowed value of a user defined priority is 1000000000 + priorityClass: + create: false + value: 1000000000 + ## Override default name + # name: + ## Use an existing priority class + # existingPriorityClass: + ## Spread Artifactory pods evenly across your nodes or some other topology + topologySpreadConstraints: [] + # - maxSkew: 1 + # topologyKey: kubernetes.io/hostname + # whenUnsatisfiable: DoNotSchedule + # labelSelector: + # matchLabels: + # app: '{{ template "artifactory.name" . }}' + # role: '{{ template "artifactory.name" . }}' + # release: "{{ .Release.Name }}" + + ## Delete the db.properties file in ARTIFACTORY_HOME/etc/db.properties + deleteDBPropertiesOnStartup: true + ## certificates added to this secret will be copied to $JFROG_HOME/artifactory/var/etc/security/keys/trusted directory + customCertificates: + enabled: false + # certificateSecretName: + database: + maxOpenConnections: 80 + tomcat: + maintenanceConnector: + port: 8091 + connector: + maxThreads: 200 + sendReasonPhrase: false + extraConfig: 'acceptCount="400"' + ## Support for metrics is only available for Artifactory 7.7.x (appVersions) and above. + ## To enable set `.Values.artifactory.metrics.enabled` to `true` + ## Note : Depricated openMetrics as part of 7.87.x and renamed to `metrics` + ## Refer - https://www.jfrog.com/confluence/display/JFROG/Open+Metrics + metrics: + enabled: false + ## Settings for pushing metrics to Insight - enable filebeat to true + filebeat: + enabled: false + log: + enabled: false + ## Log level for filebeat. Possible values: debug, info, warning, or error. + level: "info" + ## Elasticsearch details for filebeat to connect + elasticsearch: + url: "Elasticsearch url where JFrog Insight is installed For example, http://:8082" + username: "" + password: "" + ## Support for Cold Artifact Storage + ## set 'coldStorage.enabled' to 'true' only for Artifactory instance that you are designating as the Cold instance + ## Refer - https://jfrog.com/help/r/jfrog-platform-administration-documentation/setting-up-cold-artifact-storage + coldStorage: + enabled: false + ## Support for workers + ## set 'worker.enabled' to 'true' to enable artifactory addon that executes workers on artifactory events + worker: + enabled: false + ## Set servicePrependReleaseName to true to prepend service release name in statefulset + servicePrependReleaseName: false + ## This directory is intended for use with NFS eventual configuration for HA + haDataDir: + enabled: false + path: + haBackupDir: + enabled: false + path: + ## Files to copy to ARTIFACTORY_HOME/ on each Artifactory startup + ## Note : From 107.46.x chart versions, copyOnEveryStartup is not needed for binarystore.xml, it is always copied via initContainers + copyOnEveryStartup: + ## Absolute path + # - source: /artifactory_bootstrap/artifactory.lic + ## Relative to ARTIFACTORY_HOME/ + # target: etc/artifactory/ + + ## Sidecar containers for tailing Artifactory logs + loggers: [] + # - access-audit.log + # - access-request.log + # - access-security-audit.log + # - access-service.log + # - artifactory-access.log + # - artifactory-event.log + # - artifactory-import-export.log + # - artifactory-request.log + # - artifactory-service.log + # - frontend-request.log + # - frontend-service.log + # - metadata-request.log + # - metadata-service.log + # - router-request.log + # - router-service.log + # - router-traefik.log + # - derby.log + + ## Loggers containers resources + loggersResources: {} + # requests: + # memory: "10Mi" + # cpu: "10m" + # limits: + # memory: "100Mi" + # cpu: "50m" + + ## Sidecar containers for tailing Tomcat (catalina) logs + catalinaLoggers: [] + # - tomcat-catalina.log + # - tomcat-localhost.log + + ## Tomcat (catalina) loggers resources + catalinaLoggersResources: {} + # requests: + # memory: "10Mi" + # cpu: "10m" + # limits: + # memory: "100Mi" + # cpu: "50m" + + ## Migration support from 6.x to 7.x + migration: + enabled: false + timeoutSeconds: 3600 + ## Extra pre-start command in migration Init Container to install JDBC driver for MySql/MariaDb/Oracle + # preStartCommand: "mkdir -p /opt/jfrog/artifactory/var/bootstrap/artifactory/tomcat/lib; cd /opt/jfrog/artifactory/var/bootstrap/artifactory/tomcat/lib && curl -o /opt/jfrog/artifactory/var/bootstrap/artifactory/tomcat/lib/mysql-connector-java-5.1.41.jar https://repo1.maven.org/maven2/mysql/mysql-connector-java/5.1.41/mysql-connector-java-5.1.41.jar" + ## Add custom init containers execution before predefined init containers + customInitContainersBegin: | + # - name: "custom-setup" + # image: {{ include "artifactory.getImageInfoByValue" (list . "initContainers") }} + # imagePullPolicy: {{ .Values.initContainers.image.pullPolicy }} + # securityContext: + # runAsNonRoot: true + # allowPrivilegeEscalation: false + # capabilities: + # drop: + # - NET_RAW + # command: + # - 'sh' + # - '-c' + # - 'touch {{ .Values.artifactory.persistence.mountPath }}/example-custom-setup' + # volumeMounts: + # - mountPath: "{{ .Values.artifactory.persistence.mountPath }}" + # name: artifactory-volume + ## Add custom init containers execution after predefined init containers + customInitContainers: | + # - name: "custom-systemyaml-setup" + # image: {{ include "artifactory.getImageInfoByValue" (list . "initContainers") }} + # imagePullPolicy: {{ .Values.initContainers.image.pullPolicy }} + # securityContext: + # runAsNonRoot: true + # allowPrivilegeEscalation: false + # capabilities: + # drop: + # - NET_RAW + # command: + # - 'sh' + # - '-c' + # - 'curl -o {{ .Values.artifactory.persistence.mountPath }}/etc/system.yaml https:///systemyaml' + # volumeMounts: + # - mountPath: "{{ .Values.artifactory.persistence.mountPath }}" + # name: artifactory-volume + ## Add custom sidecar containers + ## - The provided example uses a custom volume (customVolumes) + customSidecarContainers: | + # - name: "sidecar-list-etc" + # image: {{ include "artifactory.getImageInfoByValue" (list . "initContainers") }} + # imagePullPolicy: {{ .Values.initContainers.image.pullPolicy }} + # securityContext: + # runAsNonRoot: true + # allowPrivilegeEscalation: false + # capabilities: + # drop: + # - NET_RAW + # command: + # - 'sh' + # - '-c' + # - 'sh /scripts/script.sh' + # volumeMounts: + # - mountPath: "{{ .Values.artifactory.persistence.mountPath }}" + # name: artifactory-volume + # - mountPath: "/scripts/script.sh" + # name: custom-script + # subPath: script.sh + # resources: + # requests: + # memory: "32Mi" + # cpu: "50m" + # limits: + # memory: "128Mi" + # cpu: "100m" + ## Add custom volumes + ## If .Values.artifactory.unifiedSecretInstallation is true then secret name should be '{{ template "artifactory.unifiedSecretPrependReleaseName" . }}-unified-secret' + customVolumes: | + # - name: custom-script + # configMap: + # name: custom-script + ## Add custom volumesMounts + customVolumeMounts: | + # - name: custom-script + # mountPath: "/scripts/script.sh" + # subPath: script.sh + # - name: posthook-start + # mountPath: "/scripts/posthoook-start.sh" + # subPath: posthoook-start.sh + # - name: prehook-start + # mountPath: "/scripts/prehook-start.sh" + # subPath: prehook-start.sh + ## Add custom persistent volume mounts - Available to the entire namespace + customPersistentVolumeClaim: {} + # name: + # mountPath: + # accessModes: + # - "-" + # size: + # storageClassName: + + ## Artifactory license. + license: + ## licenseKey is the license key in plain text. Use either this or the license.secret setting + licenseKey: + ## If artifactory.license.secret is passed, it will be mounted as + ## ARTIFACTORY_HOME/etc/artifactory.lic and loaded at run time. + secret: + ## The dataKey should be the name of the secret data key created. + dataKey: + ## Create configMap with artifactory.config.import.xml and security.import.xml and pass name of configMap in following parameter + configMapName: + ## Add any list of configmaps to Artifactory + configMaps: | + # posthook-start.sh: |- + # echo "This is a post start script" + # posthook-end.sh: |- + # echo "This is a post end script" + ## List of secrets for Artifactory user plugins. + ## One Secret per plugin's files. + userPluginSecrets: + # - archive-old-artifacts + # - build-cleanup + # - webhook + # - '{{ template "my-chart.fullname" . }}' + + ## Artifactory requires a unique master key. + ## You can generate one with the command: "openssl rand -hex 32" + ## An initial one is auto generated by Artifactory on first startup. + # masterKey: FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + ## Alternatively, you can use a pre-existing secret with a key called master-key by specifying masterKeySecretName + # masterKeySecretName: + + ## Join Key to connect other services to Artifactory + ## IMPORTANT: Setting this value overrides the existing joinKey + ## IMPORTANT: You should NOT use the example joinKey for a production deployment! + # joinKey: EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE + ## Alternatively, you can use a pre-existing secret with a key called join-key by specifying joinKeySecretName + # joinKeySecretName: + + ## Registration Token for JFConnect + # jfConnectToken: + ## Alternatively, you can use a pre-existing secret with a key called jfconnect-token by specifying jfConnectTokenSecretName + # jfConnectTokenSecretName: + + ## Add custom secrets - secret per file + ## If .Values.artifactory.unifiedSecretInstallation is true then secret name should be '{{ template "artifactory.unifiedSecretPrependReleaseName" . }}-unified-secret' common to all secrets + customSecrets: + # - name: custom-secret + # key: custom-secret.yaml + # data: > + # custom_secret_config: + # parameter1: value1 + # parameter2: value2 + # - name: custom-secret2 + # key: custom-secret2.config + # data: | + # here the custom secret 2 config + + ## If false, all service console logs will not redirect to a common console.log + consoleLog: false + ## admin allows to set the password for the default admin user. + ## See: https://www.jfrog.com/confluence/display/JFROG/Users+and+Groups#UsersandGroups-RecreatingtheDefaultAdminUserrecreate + admin: + ip: "127.0.0.1" + username: "admin" + password: + secret: + dataKey: + ## Extra pre-start command to install JDBC driver for MySql/MariaDb/Oracle + # preStartCommand: "mkdir -p /opt/jfrog/artifactory/var/bootstrap/artifactory/tomcat/lib; cd /opt/jfrog/artifactory/var/bootstrap/artifactory/tomcat/lib && curl -o /opt/jfrog/artifactory/var/bootstrap/artifactory/tomcat/lib/mysql-connector-java-5.1.41.jar https://repo1.maven.org/maven2/mysql/mysql-connector-java/5.1.41/mysql-connector-java-5.1.41.jar" + + ## Add lifecycle hooks for artifactory container + lifecycle: {} + # postStart: + # exec: + # command: ["/bin/sh", "-c", "echo Hello from the postStart handler"] + # preStop: + # exec: + # command: ["/bin/sh","-c","echo Hello from the preStop handler"] + + ## Extra environment variables that can be used to tune Artifactory to your needs. + ## Uncomment and set value as needed + extraEnvironmentVariables: + # - name: SERVER_XML_ARTIFACTORY_PORT + # value: "8081" + # - name: SERVER_XML_ARTIFACTORY_MAX_THREADS + # value: "200" + # - name: SERVER_XML_ACCESS_MAX_THREADS + # value: "50" + # - name: SERVER_XML_ARTIFACTORY_EXTRA_CONFIG + # value: "" + # - name: SERVER_XML_ACCESS_EXTRA_CONFIG + # value: "" + # - name: SERVER_XML_EXTRA_CONNECTOR + # value: "" + # - name: DB_POOL_MAX_ACTIVE + # value: "100" + # - name: DB_POOL_MAX_IDLE + # value: "10" + # - name: MY_SECRET_ENV_VAR + # valueFrom: + # secretKeyRef: + # name: my-secret-name + # key: my-secret-key + + ## System YAML entries now reside under files/system.yaml. + ## You can provide the specific values that you want to add or override under 'artifactory.extraSystemYaml'. + ## For example: + ## extraSystemYaml: + ## shared: + ## node: + ## id: my-instance + ## The entries provided under 'artifactory.extraSystemYaml' are merged with files/system.yaml to create the final system.yaml. + ## If you have already provided system.yaml under, 'artifactory.systemYaml', the values in that entry take precedence over files/system.yaml + ## You can modify specific entries with your own value under `artifactory.extraSystemYaml`, The values under extraSystemYaml overrides the values under 'artifactory.systemYaml' and files/system.yaml + extraSystemYaml: {} + ## systemYaml is intentionally commented and the previous content has been moved under files/system.yaml. + ## You have to add the all entries of the system.yaml file here, and it overrides the values in files/system.yaml. + # systemYaml: + annotations: {} + service: + name: artifactory + type: ClusterIP + ## @param service.ipFamilyPolicy Controller Service ipFamilyPolicy (optional, cloud specific) + ## This can be either SingleStack, PreferDualStack or RequireDualStack + ## ref: https://kubernetes.io/docs/concepts/services-networking/dual-stack/#services + ## + ipFamilyPolicy: "" + ## @param service.ipFamilies Controller Service ipFamilies (optional, cloud specific) + ## This can be either ["IPv4"], ["IPv6"], ["IPv4", "IPv6"] or ["IPv6", "IPv4"] + ## ref: https://kubernetes.io/docs/concepts/services-networking/dual-stack/#services + ## + ipFamilies: [] + ## For supporting whitelist on the Artifactory service (useful if setting service.type=LoadBalancer) + ## Set this to a list of IP CIDR ranges + ## Example: loadBalancerSourceRanges: ['10.10.10.5/32', '10.11.10.5/32'] + ## or pass from helm command line + ## Example: helm install ... --set nginx.service.loadBalancerSourceRanges='{10.10.10.5/32,10.11.10.5/32}' + loadBalancerSourceRanges: [] + annotations: {} + ## If the type is NodePort you can set a fixed port + # nodePort: 32082 + serviceGrpc: + name: grpc + type: ClusterIP + ## @param service.ipFamilyPolicy Controller Service ipFamilyPolicy (optional, cloud specific) + ## This can be either SingleStack, PreferDualStack or RequireDualStack + ## ref: https://kubernetes.io/docs/concepts/services-networking/dual-stack/#services + ## + ipFamilyPolicy: "" + ## @param service.ipFamilies Controller Service ipFamilies (optional, cloud specific) + ## This can be either ["IPv4"], ["IPv6"], ["IPv4", "IPv6"] or ["IPv6", "IPv4"] + ## ref: https://kubernetes.io/docs/concepts/services-networking/dual-stack/#services + ## + ipFamilies: [] + ## For supporting whitelist on the Artifactory service (useful if setting service.type=LoadBalancer) + ## Set this to a list of IP CIDR ranges + ## Example: loadBalancerSourceRanges: ['10.10.10.5/32', '10.11.10.5/32'] + ## or pass from helm command line + ## Example: helm install ... --set nginx.service.loadBalancerSourceRanges='{10.10.10.5/32,10.11.10.5/32}' + loadBalancerSourceRanges: [] + annotations: {} + ## If the type is NodePort you can set a fixed port + # nodePort: 32082 + statefulset: + annotations: {} + ## IMPORTANT: If overriding artifactory.internalPort: + ## DO NOT use port lower than 1024 as Artifactory runs as non-root and cannot bind to ports lower than 1024! + externalPort: 8082 + internalPort: 8082 + externalArtifactoryPort: 8081 + internalArtifactoryPort: 8081 + terminationGracePeriodSeconds: 30 + ## Pod Security Context + ## ref: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/ + ## @param artifactory.podSecurityContext.enabled Enable security context + ## @param artifactory.podSecurityContext.runAsNonRoot Set pod's Security Context runAsNonRoot + ## @param artifactory.podSecurityContext.runAsUser User ID for the pod + ## @param artifactory.podSecurityContext.runASGroup Group ID for the pod + ## @param artifactory.podSecurityContext.fsGroup Group ID for the pod + ## + podSecurityContext: + enabled: true + runAsNonRoot: true + runAsUser: 1030 + runAsGroup: 1030 + fsGroup: 1030 + # fsGroupChangePolicy: "Always" + # seLinuxOptions: {} + livenessProbe: + enabled: true + config: | + exec: + command: + - sh + - -c + - curl -s -k --fail --max-time {{ .Values.probes.timeoutSeconds }} http://localhost:{{ .Values.artifactory.tomcat.maintenanceConnector.port }}/artifactory/api/v1/system/liveness + initialDelaySeconds: {{ if semverCompare "", + # "private_key_id": "?????", + # "private_key": "-----BEGIN PRIVATE KEY-----\n????????==\n-----END PRIVATE KEY-----\n", + # "client_email": "???@j.iam.gserviceaccount.com", + # "client_id": "???????", + # "auth_uri": "https://accounts.google.com/o/oauth2/auth", + # "token_uri": "https://oauth2.googleapis.com/token", + # "auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs", + # "client_x509_cert_url": "https://www.googleapis.com/robot/v1....." + # } + endpoint: commondatastorage.googleapis.com + httpsOnly: false + ## Set a unique bucket name + bucketName: "artifactory-gcp" + ## GCP Bucket Authentication with Identity and Credential is deprecated. + ## identity: + ## credential: + path: "artifactory/filestore" + bucketExists: false + useInstanceCredentials: false + enableSignedUrlRedirect: false + # signedUrlExpirySeconds: 30 + ## For artifactory.persistence.type aws-s3-v3, s3-storage-v3-direct, cluster-s3-storage-v3, s3-storage-v3-archive + awsS3V3: + testConnection: false + identity: + credential: + region: + bucketName: artifactory-aws + path: artifactory/filestore + endpoint: + port: + useHttp: + maxConnections: 50 + connectionTimeout: + socketTimeout: + kmsServerSideEncryptionKeyId: + kmsKeyRegion: + kmsCryptoMode: + useInstanceCredentials: true + usePresigning: false + signatureExpirySeconds: 300 + signedUrlExpirySeconds: 30 + cloudFrontDomainName: + cloudFrontKeyPairId: + cloudFrontPrivateKey: + enableSignedUrlRedirect: false + enablePathStyleAccess: false + multiPartLimit: + multipartElementSize: + ## For artifactory.persistence.type azure-blob, azure-blob-storage-direct, cluster-azure-blob-storage, azure-blob-storage-v2-direct + azureBlob: + accountName: + accountKey: + endpoint: + containerName: + multiPartLimit: 100000000 + multipartElementSize: 50000000 + testConnection: false + ## artifactory data Persistent Volume Storage Class + ## If defined, storageClassName: + ## If set to "-", storageClassName: "", which disables dynamic provisioning + ## If undefined (the default) or set to null, no storageClassName spec is + ## set, choosing the default provisioner. (gp2 on AWS, standard on + ## GKE, AWS & OpenStack) + ## + # storageClassName: "-" + ## Annotations for the Persistent Volume Claim + annotations: {} + ## Uncomment the following resources definitions or pass them from command line + ## to control the cpu and memory resources allocated by the Kubernetes cluster + resources: {} + # requests: + # memory: "1Gi" + # cpu: "500m" + # limits: + # memory: "2Gi" + # cpu: "1" + ## The following Java options are passed to the java process running Artifactory. + ## You should set them according to the resources set above + javaOpts: + # xms: "1g" + # xmx: "2g" + jmx: + enabled: false + port: 9010 + host: + ssl: false + ## When authenticate is true, accessFile and passwordFile are required + authenticate: false + accessFile: + passwordFile: + # corePoolSize: 24 + # other: "" + nodeSelector: {} + tolerations: [] + affinity: {} + ## Only used if "affinity" is empty + podAntiAffinity: + ## Valid values are "soft" or "hard"; any other value indicates no anti-affinity + type: "soft" + topologyKey: "kubernetes.io/hostname" + ssh: + enabled: false + internalPort: 1339 + externalPort: 1339 +frontend: + name: frontend + enabled: true + internalPort: 8070 + ## Extra environment variables that can be used to tune frontend to your needs. + ## Uncomment and set value as needed + extraEnvironmentVariables: + # - name: MY_ENV_VAR + # value: "" + resources: {} + # requests: + # memory: "100Mi" + # cpu: "100m" + # limits: + # memory: "1Gi" + # cpu: "1" + + ## Add lifecycle hooks for frontend container + lifecycle: {} + # postStart: + # exec: + # command: ["/bin/sh", "-c", "echo Hello from the postStart handler"] + # preStop: + # exec: + # command: ["/bin/sh","-c","echo Hello from the preStop handler"] + + ## Session settings + session: + ## Time in minutes after which the frontend token will need to be refreshed + timeoutMinutes: '30' + ## The following settings are to configure the frequency of the liveness and startup probes when splitServicesToContainers set to true + livenessProbe: + enabled: true + config: | + exec: + command: + - sh + - -c + - curl --fail --max-time {{ .Values.probes.timeoutSeconds }} http://localhost:{{ .Values.frontend.internalPort }}/api/v1/system/liveness + initialDelaySeconds: {{ if semverCompare " --cert=ca.crt --key=ca.private.key` + # customCertificatesSecretName: + + ## When resetAccessCAKeys is true, Access will regenerate the CA certificate and matching private key + # resetAccessCAKeys: false + database: + maxOpenConnections: 80 + tomcat: + connector: + maxThreads: 50 + sendReasonPhrase: false + extraConfig: 'acceptCount="100"' + livenessProbe: + enabled: true + config: | + exec: + command: + - sh + - -c + - curl --fail --max-time {{ .Values.probes.timeoutSeconds }} http://localhost:8040/access/api/v1/system/liveness + initialDelaySeconds: {{ if semverCompare " --cert=tls.crt --key=tls.key` + # customCertificatesSecretName: + + ## The following settings are to configure the frequency of the liveness and startup probes when splitServicesToContainers set to true + livenessProbe: + enabled: true + config: | + exec: + command: + - sh + - -c + - curl --fail --max-time {{ .Values.probes.timeoutSeconds }} http://localhost:{{ .Values.jfconnect.internalPort }}/api/v1/system/liveness + initialDelaySeconds: {{ if semverCompare " /var/opt/jfrog/nginx/message"] + # preStop: + # exec: + # command: ["/bin/sh","-c","nginx -s quit; while killall -0 nginx; do sleep 1; done"] + + ## Sidecar containers for tailing Nginx logs + loggers: [] + # - access.log + # - error.log + + ## Loggers containers resources + loggersResources: {} + # requests: + # memory: "64Mi" + # cpu: "25m" + # limits: + # memory: "128Mi" + # cpu: "50m" + + ## Logs options + logs: + stderr: false + stdout: false + level: warn + ## A list of custom ports to expose on the NGINX pod. Follows the conventional Kubernetes yaml syntax for container ports. + customPorts: [] + # - containerPort: 8066 + # name: docker + + ## The nginx main conf was moved to files/nginx-main-conf.yaml. This key is commented out to keep support for the old configuration + # mainConf: | + + ## The nginx artifactory conf was moved to files/nginx-artifactory-conf.yaml. This key is commented out to keep support for the old configuration + # artifactoryConf: | + customInitContainers: "" + customSidecarContainers: "" + customVolumes: "" + customVolumeMounts: "" + customCommand: + ## allows overwriting the command for the nginx container. + ## defaults to [ 'nginx', '-g', 'daemon off;' ] + + service: + ## For minikube, set this to NodePort, elsewhere use LoadBalancer + type: LoadBalancer + ssloffload: false + ## @param service.ssloffloadForceHttps Only enabled when service.ssloffload is set to True. + ## Force all requests from NGINX to the upstream server are over HTTPS, even when SSL offloading is enabled. + ## This is useful in environments where internal traffic must remain secure with https only. + ssloffloadForceHttps: false + ## @param service.ipFamilyPolicy Controller Service ipFamilyPolicy (optional, cloud specific) + ## This can be either SingleStack, PreferDualStack or RequireDualStack + ## ref: https://kubernetes.io/docs/concepts/services-networking/dual-stack/#services + ## + ipFamilyPolicy: "" + ## @param service.ipFamilies Controller Service ipFamilies (optional, cloud specific) + ## This can be either ["IPv4"], ["IPv6"], ["IPv4", "IPv6"] or ["IPv6", "IPv4"] + ## ref: https://kubernetes.io/docs/concepts/services-networking/dual-stack/#services + ## + ipFamilies: [] + ## For supporting whitelist on the Nginx LoadBalancer service + ## Set this to a list of IP CIDR ranges + ## Example: loadBalancerSourceRanges: ['10.10.10.5/32', '10.11.10.5/32'] + ## or pass from helm command line + ## Example: helm install ... --set nginx.service.loadBalancerSourceRanges='{10.10.10.5/32,10.11.10.5/32}' + loadBalancerSourceRanges: [] + annotations: {} + ## Provide static ip address + loadBalancerIP: + ## There are two available options: "Cluster" (default) and "Local". + externalTrafficPolicy: Cluster + ## If the type is NodePort you can set a fixed port + # nodePort: 32082 + ## A list of custom ports to be exposed on nginx service. Follows the conventional Kubernetes yaml syntax for service ports. + customPorts: [] + # - port: 8066 + # targetPort: 8066 + # protocol: TCP + # name: docker + ## Renamed nginx internalPort 80,443 to 8080,8443 to support openshift + http: + enabled: true + externalPort: 80 + internalPort: 8080 + https: + enabled: true + externalPort: 443 + internalPort: 8443 + ssh: + internalPort: 1339 + externalPort: 1339 + ## DEPRECATED: The following will be removed in a future release + # externalPortHttp: 8080 + # internalPortHttp: 8080 + # externalPortHttps: 8443 + # internalPortHttps: 8443 + + ## The following settings are to configure the frequency of the liveness and readiness probes. + livenessProbe: + enabled: true + config: | + exec: + command: + - sh + - -c + - curl -s -k --fail --max-time {{ .Values.probes.timeoutSeconds }} {{ include "nginx.scheme" . }}://localhost:{{ include "nginx.port" . }}/ + initialDelaySeconds: {{ if semverCompare " + ## If set to "-", storageClassName: "", which disables dynamic provisioning + ## If undefined (the default) or set to null, no storageClassName spec is + ## set, choosing the default provisioner. (gp2 on AWS, standard on + ## GKE, AWS & OpenStack) + ## + # storageClassName: "-" + resources: {} + # requests: + # memory: "250Mi" + # cpu: "100m" + # limits: + # memory: "250Mi" + # cpu: "500m" + nodeSelector: {} + tolerations: [] + affinity: {} +## Database configurations +## Use the wait-for-db init container. Set to false to skip +waitForDatabase: true +## Configuration values for the PostgreSQL dependency sub-chart +## ref: https://github.com/bitnami/charts/blob/master/bitnami/postgresql/README.md +postgresql: + enabled: true + image: + registry: releases-docker.jfrog.io + repository: bitnami/postgresql + tag: 15.6.0-debian-11-r16 + postgresqlUsername: artifactory + postgresqlPassword: "" + postgresqlDatabase: artifactory + postgresqlExtendedConf: + listenAddresses: "*" + maxConnections: "1500" + persistence: + enabled: true + size: 200Gi + # existingClaim: + service: + port: 5432 + primary: + nodeSelector: {} + affinity: {} + tolerations: [] + readReplicas: + nodeSelector: {} + affinity: {} + tolerations: [] + resources: {} + securityContext: + enabled: true + containerSecurityContext: + enabled: true + # requests: + # memory: "512Mi" + # cpu: "100m" + # limits: + # memory: "1Gi" + # cpu: "500m" +## If NOT using the PostgreSQL in this chart (postgresql.enabled=false), +## specify custom database details here or leave empty and Artifactory will use embedded derby +database: + ## To run Artifactory with any database other than PostgreSQL allowNonPostgresql set to true. + allowNonPostgresql: false + type: + driver: + ## If you set the url, leave host and port empty + url: + ## If you would like this chart to create the secret containing the db + ## password, use these values + user: + password: + ## If you have existing Kubernetes secrets containing db credentials, use + ## these values + secrets: {} + # user: + # name: "rds-artifactory" + # key: "db-user" + # password: + # name: "rds-artifactory" + # key: "db-password" + # url: + # name: "rds-artifactory" + # key: "db-url" +## Filebeat Sidecar container +## The provided filebeat configuration is for Artifactory logs. It assumes you have a logstash installed and configured properly. +filebeat: + enabled: false + name: artifactory-filebeat + image: + repository: "docker.elastic.co/beats/filebeat" + version: 7.16.2 + logstashUrl: "logstash:5044" + livenessProbe: + exec: + command: + - sh + - -c + - | + #!/usr/bin/env bash -e + curl --fail 127.0.0.1:5066 + failureThreshold: 3 + initialDelaySeconds: 10 + periodSeconds: 10 + timeoutSeconds: 5 + readinessProbe: + exec: + command: + - sh + - -c + - | + #!/usr/bin/env bash -e + filebeat test output + failureThreshold: 3 + initialDelaySeconds: 10 + periodSeconds: 10 + timeoutSeconds: 5 + resources: {} + # requests: + # memory: "100Mi" + # cpu: "100m" + # limits: + # memory: "100Mi" + # cpu: "100m" + + filebeatYml: | + logging.level: info + path.data: {{ .Values.artifactory.persistence.mountPath }}/log/filebeat + name: artifactory-filebeat + queue.spool: + file: + permissions: 0760 + filebeat.inputs: + - type: log + enabled: true + close_eof: ${CLOSE:false} + paths: + - {{ .Values.artifactory.persistence.mountPath }}/log/*.log + fields: + service: "jfrt" + log_type: "artifactory" + output: + logstash: + hosts: ["{{ .Values.filebeat.logstashUrl }}"] + extraEnvironmentVariables: {} + # - name: MY_ENV_VAR + # value: "s" +## RTFS deployment +rtfs: + name: rtfs + enabled: false + image: + registry: releases-docker.jfrog.io + repository: jfrog/artifactory-federation + pullPolicy: IfNotPresent + tag: 1.4.18 + jfrogUrl: "" + replicaCount: 1 + internalRestPort: 8025 + apiContext: artifactory/service/rtfs + persistence: + mountPath: "/var/opt/jfrog/federation" + federationProperties: {} + ## Apply horizontal pod auto scaling on distribution pods + ## Ref: https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/ + autoscaling: + enabled: false + minReplicas: 1 + maxReplicas: 3 + targetCPUUtilizationPercentage: 70 + metrics: | + # - type: Resource + # resource: + # name: memory + # target: + # type: Utilization + # averageUtilization: 75 # Target memory usage is 75% of the allocated memory per pod + ## @param federationProperties Configuration to be added to the config map, overriding Spring configuration. + ## @param customFederationPropertiesConfig Specifies the name of a custom configMap to use instead of the default one. + ## The name of the custom configMap will be initialized from the value of customFederationPropertiesConfig. + # customFederationPropertiesConfig: + javaOpts: "" + service: + type: ClusterIP + restPort: 8025 + grpcPort: 8026 + ## @param service.loadBalancerSourceRanges Address(es) that are allowed when service is `LoadBalancer` + ## https://kubernetes.io/docs/tasks/access-application-cluster/configure-cloud-provider-firewall/#restrict-access-for-loadbalancer-service + ## e.g: + ## loadBalancerSourceRanges: + ## - 10.10.10.0/24 + ## + loadBalancerSourceRanges: [] + ## @param service.externalIPs Set the ExternalIPs + ## + externalIPs: [] + ## @param service.externalTrafficPolicy Enable client source IP preservation + ## ref https://kubernetes.io/docs/tasks/access-application-cluster/create-external-load-balancer/#preserving-the-client-source-ip + ## + externalTrafficPolicy: Cluster + ## @param service.loadBalancerIP Set the LoadBalancerIP + ## + loadBalancerIP: "" + ## @param service.labels Service labels. Evaluated as a template + labels: {} + ## @param service.annotations Service annotations. Evaluated as a template + ## Example: + ## annotations: + ## service.beta.kubernetes.io/aws-load-balancer-internal: 0.0.0.0/0 + annotations: {} + livenessProbe: + enabled: true + config: | + httpGet: + path: /{{ .Values.rtfs.apiContext }}/api/v1/system/liveness + port: {{ .Values.rtfs.internalRestPort }} + scheme: HTTP + initialDelaySeconds: 10 + periodSeconds: 10 + timeoutSeconds: {{ .Values.probes.timeoutSeconds }} + failureThreshold: 5 + successThreshold: 1 + readinessProbe: + enabled: true + config: | + httpGet: + path: /router/api/v1/system/readiness + port: {{ .Values.router.internalPort }} + scheme: HTTP + initialDelaySeconds: 10 + periodSeconds: 5 + timeoutSeconds: {{ .Values.probes.timeoutSeconds }} + failureThreshold: 30 + successThreshold: 1 + startupProbe: + enabled: true + config: | + httpGet: + path: /{{ .Values.rtfs.apiContext }}/api/v1/system/readiness + port: {{ .Values.rtfs.internalRestPort }} + scheme: HTTP + initialDelaySeconds: 120 + periodSeconds: 5 + timeoutSeconds: {{ .Values.probes.timeoutSeconds }} + failureThreshold: 30 + successThreshold: 1 + resources: {} + # We usually recommend not to specify default resources and to leave this as a conscious + # choice for the user. This also increases chances charts run on environments with little + # resources, such as Minikube. If you do want to specify resources, uncomment the following + # lines, adjust them as necessary, and remove the curly braces after 'resources:'. + # limits: + # cpu: 100m + # memory: 128Mi + # requests: + # cpu: 100m + # memory: 128Mi + + # Add lifecycle hooks for observability container + lifecycle: {} + # postStart: + # exec: + # command: ["/bin/sh", "-c", "echo Hello from the postStart handler"] + # preStop: + # exec: + # command: ["/bin/sh","-c","echo Hello from the preStop handler"] + + fullnameOverride: "" + annotations: {} + deployment: + annotations: {} + labels: {} + podSecurityContext: + enabled: true + runAsNonRoot: true + runAsUser: 1030 + runAsGroup: 1030 + fsGroup: 1030 + containerSecurityContext: + enabled: true + runAsNonRoot: true + allowPrivilegeEscalation: false + seccompProfile: + type: RuntimeDefault + capabilities: + drop: + - ALL + nodeSelector: {} + tolerations: [] + affinity: {} + customInitContainers: "" + customSidecarContainers: "" + customVolumes: "" + customVolumeMounts: "" + ## @param extraEnvironmentVariables that can be used to tune PDN Server to your needs. + ## Example: + ## extraEnvironmentVariables: + ## - name: MY_ENV_VAR + ## value: "" + extraEnvironmentVariables: [] +## Allows to add additional kubernetes resources +## Use --- as a separator between multiple resources +## For an example, refer - https://github.com/jfrog/log-analytics-prometheus/blob/master/helm/artifactory-values.yaml +additionalResources: "" +## Adding entries to a Pod's /etc/hosts file +## For an example, refer - https://kubernetes.io/docs/concepts/services-networking/add-entries-to-pod-etc-hosts-with-host-aliases +hostAliases: [] +# - ip: "127.0.0.1" +# hostnames: +# - "foo.local" +# - "bar.local" +# - ip: "10.1.2.3" +# hostnames: +# - "foo.remote" +# - "bar.remote" + +## Toggling this feature is seamless and requires helm upgrade +## will enable all microservices to run in different containers in a single pod (by default it is true) +splitServicesToContainers: true +## Specify common probes parameters +probes: + timeoutSeconds: 5 diff --git a/charts/jfrog/artifactory-jcr/107.104.10/ci/default-values.yaml b/charts/jfrog/artifactory-jcr/107.104.10/ci/default-values.yaml new file mode 100644 index 0000000000..86355d3b3c --- /dev/null +++ b/charts/jfrog/artifactory-jcr/107.104.10/ci/default-values.yaml @@ -0,0 +1,7 @@ +# Leave this file empty to ensure that CI runs builds against the default configuration in values.yaml. +artifactory: + databaseUpgradeReady: true + + # To Fix ct tool --reuse-values - PASSWORDS ERROR: you must provide your current passwords when upgrade the release + postgresql: + postgresqlPassword: password diff --git a/charts/jfrog/artifactory-jcr/107.104.10/logo/jcr-logo.png b/charts/jfrog/artifactory-jcr/107.104.10/logo/jcr-logo.png new file mode 100644 index 0000000000000000000000000000000000000000..b1e312e3219a8e0bb1831976646260683f6d447f GIT binary patch literal 77047 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
    3t{2R#w6vF)uYze!GvJ=o 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 f`MkUvo-M@p#hFOE>-xWCTUi literal 0 HcmV?d00001 diff --git a/charts/jfrog/artifactory-jcr/107.104.10/questions.yml b/charts/jfrog/artifactory-jcr/107.104.10/questions.yml new file mode 100644 index 0000000000..9cde428709 --- /dev/null +++ b/charts/jfrog/artifactory-jcr/107.104.10/questions.yml @@ -0,0 +1,271 @@ +questions: +# Advance Settings +- variable: artifactory.artifactory.masterKey + default: "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + description: "Artifactory master key. For security reasons, we strongly recommend you generate your own master key using this command: 'openssl rand -hex 32'" + type: string + label: Artifactory master key + group: "Security Settings" + +# Container Images +- variable: defaultImage + default: true + description: "Use default Docker image" + label: Use Default Image + type: boolean + show_subquestion_if: false + group: "Container Images" + subquestions: + - variable: artifactory.artifactory.image.repository + default: "docker.bintray.io/jfrog/artifactory-jcr" + description: "JFrog Container Registry image name" + type: string + label: JFrog Container Registry Image Name + - variable: artifactory.artifactory.image.version + default: "7.6.3" + description: "JFrog Container Registry image tag" + type: string + label: JFrog Container Registry Image Tag + - variable: artifactory.imagePullSecrets + description: "Image Pull Secret" + type: string + label: Image Pull Secret + +# Services and LoadBalancing Settings +- variable: artifactory.ingress.enabled + default: false + description: "Expose app using Layer 7 Load Balancer - ingress" + type: boolean + label: Expose app using Layer 7 Load Balancer + show_subquestion_if: true + group: "Services and Load Balancing" + required: true + subquestions: + - variable: artifactory.ingress.hosts[0] + default: "xip.io" + description: "Hostname to your artifactory installation" + type: hostname + required: true + label: Hostname + +# Nginx Settings +- variable: artifactory.nginx.enabled + default: true + description: "Enable nginx server" + type: boolean + label: Enable Nginx Server + group: "Services and Load Balancing" + required: true + show_if: "artifactory.ingress.enabled=false" +- variable: artifactory.nginx.service.type + default: "LoadBalancer" + description: "Nginx service type" + type: enum + required: true + label: Nginx Service Type + show_if: "artifactory.nginx.enabled=true&&artifactory.ingress.enabled=false" + group: "Services and Load Balancing" + options: + - "ClusterIP" + - "NodePort" + - "LoadBalancer" +- variable: artifactory.nginx.service.loadBalancerIP + default: "" + description: "Provide Static IP to configure with Nginx" + type: string + label: Config Nginx LoadBalancer IP + show_if: "artifactory.nginx.enabled=true&&artifactory.nginx.service.type=LoadBalancer&&artifactory.ingress.enabled=false" + group: "Services and Load Balancing" +- variable: artifactory.nginx.tlsSecretName + default: "" + description: "Provide SSL Secret name to configure with Nginx" + type: string + label: Config Nginx SSL Secret + show_if: "artifactory.nginx.enabled=true&&artifactory.ingress.enabled=false" + group: "Services and Load Balancing" +- variable: artifactory.nginx.customArtifactoryConfigMap + default: "" + description: "Provide configMap name to configure Nginx with custom `artifactory.conf`" + type: string + label: ConfigMap for Nginx Artifactory Config + show_if: "artifactory.nginx.enabled=true&&artifactory.ingress.enabled=false" + group: "Services and Load Balancing" + +# Database Settings +- variable: artifactory.postgresql.enabled + default: true + description: "Enable PostgreSQL" + type: boolean + required: true + label: Enable PostgreSQL + group: "Database Settings" + show_subquestion_if: true + subquestions: + - variable: artifactory.postgresql.postgresqlPassword + default: "" + description: "PostgreSQL password" + type: password + required: true + label: PostgreSQL Password + group: "Database Settings" + show_if: "artifactory.postgresql.enabled=true" + - variable: artifactory.postgresql.persistence.size + default: 20Gi + description: "PostgreSQL persistent volume size" + type: string + label: PostgreSQL Persistent Volume Size + show_if: "artifactory.postgresql.enabled=true" + - variable: artifactory.postgresql.persistence.storageClass + default: "" + description: "If undefined or null, uses the default StorageClass. Default to null" + type: storageclass + label: Default StorageClass for PostgreSQL + show_if: "artifactory.postgresql.enabled=true" + - variable: artifactory.postgresql.resources.requests.cpu + default: "200m" + description: "PostgreSQL initial cpu request" + type: string + label: PostgreSQL Initial CPU Request + show_if: "artifactory.postgresql.enabled=true" + - variable: artifactory.postgresql.resources.requests.memory + default: "500Mi" + description: "PostgreSQL initial memory request" + type: string + label: PostgreSQL Initial Memory Request + show_if: "artifactory.postgresql.enabled=true" + - variable: artifactory.postgresql.resources.limits.cpu + default: "1" + description: "PostgreSQL cpu limit" + type: string + label: PostgreSQL CPU Limit + show_if: "artifactory.postgresql.enabled=true" + - variable: artifactory.postgresql.resources.limits.memory + default: "1Gi" + description: "PostgreSQL memory limit" + type: string + label: PostgreSQL Memory Limit + show_if: "artifactory.postgresql.enabled=true" +- variable: artifactory.database.type + default: "postgresql" + description: "xternal database type (postgresql, mysql, oracle or mssql)" + type: enum + required: true + label: External Database Type + group: "Database Settings" + show_if: "artifactory.postgresql.enabled=false" + options: + - "postgresql" + - "mysql" + - "oracle" + - "mssql" +- variable: artifactory.database.url + default: "" + description: "External database URL. If you set the url, leave host and port empty" + type: string + label: External Database URL + group: "Database Settings" + show_if: "artifactory.postgresql.enabled=false" +- variable: artifactory.database.host + default: "" + description: "External database hostname" + type: string + label: External Database Hostname + group: "Database Settings" + show_if: "artifactory.postgresql.enabled=false" +- variable: artifactory.database.port + default: "" + description: "External database port" + type: string + label: External Database Port + group: "Database Settings" + show_if: "artifactory.postgresql.enabled=false" +- variable: artifactory.database.user + default: "" + description: "External database username" + type: string + label: External Database Username + group: "Database Settings" + show_if: "artifactory.postgresql.enabled=false" +- variable: artifactory.database.password + default: "" + description: "External database password" + type: password + label: External Database Password + group: "Database Settings" + show_if: "artifactory.postgresql.enabled=false" + +# Advance Settings +- variable: artifactory.advancedOptions + default: false + description: "Show advanced configurations" + label: Show Advanced Configurations + type: boolean + show_subquestion_if: true + group: "Advanced Options" + subquestions: + - variable: artifactory.artifactory.primary.resources.requests.cpu + default: "500m" + description: "Artifactory primary node initial cpu request" + type: string + label: Artifactory Primary Node Initial CPU Request + - variable: artifactory.artifactory.primary.resources.requests.memory + default: "1Gi" + description: "Artifactory primary node initial memory request" + type: string + label: Artifactory Primary Node Initial Memory Request + - variable: artifactory.artifactory.primary.javaOpts.xms + default: "1g" + description: "Artifactory primary node java Xms size" + type: string + label: Artifactory Primary Node Java Xms Size + - variable: artifactory.artifactory.primary.resources.limits.cpu + default: "2" + description: "Artifactory primary node cpu limit" + type: string + label: Artifactory Primary Node CPU Limit + - variable: artifactory.artifactory.primary.resources.limits.memory + default: "4Gi" + description: "Artifactory primary node memory limit" + type: string + label: Artifactory Primary Node Memory Limit + - variable: artifactory.artifactory.primary.javaOpts.xmx + default: "4g" + description: "Artifactory primary node java Xmx size" + type: string + label: Artifactory Primary Node Java Xmx Size + - variable: artifactory.artifactory.node.resources.requests.cpu + default: "500m" + description: "Artifactory member node initial cpu request" + type: string + label: Artifactory Member Node Initial CPU Request + - variable: artifactory.artifactory.node.resources.requests.memory + default: "2Gi" + description: "Artifactory member node initial memory request" + type: string + label: Artifactory Member Node Initial Memory Request + - variable: artifactory.artifactory.node.javaOpts.xms + default: "1g" + description: "Artifactory member node java Xms size" + type: string + label: Artifactory Member Node Java Xms Size + - variable: artifactory.artifactory.node.resources.limits.cpu + default: "2" + description: "Artifactory member node cpu limit" + type: string + label: Artifactory Member Node CPU Limit + - variable: artifactory.artifactory.node.resources.limits.memory + default: "4Gi" + description: "Artifactory member node memory limit" + type: string + label: Artifactory Member Node Memory Limit + - variable: artifactory.artifactory.node.javaOpts.xmx + default: "4g" + description: "Artifactory member node java Xmx size" + type: string + label: Artifactory Member Node Java Xmx Size + +# Internal Settings +- variable: installerInfo + default: '\{\"productId\": \"RancherHelm_artifactory-jcr/7.6.3\", \"features\": \[\{\"featureId\": \"Partner/ACC-007246\"\}\]\}' + type: string + group: "Internal Settings (Do not modify)" diff --git a/charts/jfrog/artifactory-jcr/107.104.10/templates/NOTES.txt b/charts/jfrog/artifactory-jcr/107.104.10/templates/NOTES.txt new file mode 100644 index 0000000000..035bf84174 --- /dev/null +++ b/charts/jfrog/artifactory-jcr/107.104.10/templates/NOTES.txt @@ -0,0 +1 @@ +Congratulations. You have just deployed JFrog Container Registry! diff --git a/charts/jfrog/artifactory-jcr/107.104.10/values.yaml b/charts/jfrog/artifactory-jcr/107.104.10/values.yaml new file mode 100644 index 0000000000..da37a0d02e --- /dev/null +++ b/charts/jfrog/artifactory-jcr/107.104.10/values.yaml @@ -0,0 +1,80 @@ +# Default values for artifactory-jcr. +# This is a YAML-formatted file. + +# Beware when changing values here. You should know what you are doing! +# Access the values with {{ .Values.key.subkey }} + +# This chart is based on the main artifactory chart with some customizations. +# See all supported configuration keys in https://github.com/jfrog/charts/tree/master/stable/artifactory + +## All values are under the 'artifactory' sub chart. +artifactory: + + ## Artifactory + ## See full list of supported Artifactory options and documentation in artifactory chart: https://github.com/jfrog/charts/tree/master/stable/artifactory + artifactory: + ## Default tag is from the artifactory sub-chart in the requirements.yaml + image: + registry: releases-docker.jfrog.io + repository: jfrog/artifactory-jcr + # tag: + + ## Uncomment the following resources definitions or pass them from command line + ## to control the cpu and memory resources allocated by the Kubernetes cluster + resources: {} + # requests: + # memory: "1Gi" + # cpu: "500m" + # limits: + # memory: "4Gi" + # cpu: "1" + ## The following Java options are passed to the java process running Artifactory. + ## You should set them according to the resources set above. + ## IMPORTANT: Make sure resources.limits.memory is at least 1G more than Xmx. + javaOpts: {} + # xms: "1g" + # xmx: "3g" + # other: "" + + installer: + platform: jcr-helm + + installerInfo: '{"productId":"Helm_artifactory-jcr/{{ .Chart.Version }}","features":[{"featureId":"Platform/{{ printf "%s-%s" "kubernetes" .Capabilities.KubeVersion.Version }}"},{"featureId":"Database/{{ .Values.database.type }}"},{"featureId":"PostgreSQL_Enabled/{{ .Values.postgresql.enabled }}"},{"featureId":"Nginx_Enabled/{{ .Values.nginx.enabled }}"},{"featureId":"ArtifactoryPersistence_Type/{{ .Values.artifactory.persistence.type }}"},{"featureId":"SplitServicesToContainers_Enabled/{{ .Values.splitServicesToContainers }}"},{"featureId":"UnifiedSecretInstallation_Enabled/{{ .Values.artifactory.unifiedSecretInstallation }}"},{"featureId":"Filebeat_Enabled/{{ .Values.filebeat.enabled }}"},{"featureId":"ReplicaCount/{{ .Values.artifactory.replicaCount }}"}]}' + + ## Nginx + ## See full list of supported Nginx options and documentation in artifactory chart: https://github.com/jfrog/charts/tree/master/stable/artifactory + nginx: + enabled: true + tlsSecretName: "" + service: + type: LoadBalancer + + ## Ingress + ## See full list of supported Ingress options and documentation in artifactory chart: https://github.com/jfrog/charts/tree/master/stable/artifactory + ingress: + enabled: false + tls: + + ## PostgreSQL + ## See list of supported postgresql options and documentation in artifactory chart: https://github.com/jfrog/charts/tree/master/stable/artifactory + ## Configuration values for the PostgreSQL dependency sub-chart + ## ref: https://github.com/bitnami/charts/blob/master/bitnami/postgresql/README.md + postgresql: + enabled: true + + ## This key is required for upgrades to protect old PostgreSQL chart's breaking changes. + databaseUpgradeReady: "yes" + + ## If NOT using the PostgreSQL in this chart (artifactory.postgresql.enabled=false), + ## specify custom database details here or leave empty and Artifactory will use embedded derby. + ## See full list of database options and documentation in artifactory chart: https://github.com/jfrog/charts/tree/master/stable/artifactory + # database: + + jfconnect: + enabled: false + + rtfs: + enabled: false + + onemodel: + enabled: false \ No newline at end of file diff --git a/charts/kasten/k10/7.5.301/Chart.yaml b/charts/kasten/k10/7.5.301/Chart.yaml index a67b88252a..8fca20c7c4 100644 --- a/charts/kasten/k10/7.5.301/Chart.yaml +++ b/charts/kasten/k10/7.5.301/Chart.yaml @@ -1,7 +1,6 @@ annotations: catalog.cattle.io/certified: partner catalog.cattle.io/display-name: K10 - catalog.cattle.io/featured: "3" catalog.cattle.io/kube-version: '>= 1.17.0-0' catalog.cattle.io/release-name: k10 apiVersion: v2 diff --git a/charts/kasten/k10/7.5.401/Chart.lock b/charts/kasten/k10/7.5.401/Chart.lock new file mode 100644 index 0000000000..a598fb3bbb --- /dev/null +++ b/charts/kasten/k10/7.5.401/Chart.lock @@ -0,0 +1,6 @@ +dependencies: +- name: prometheus + repository: "" + version: 26.1.0 +digest: sha256:9ed7fd751d9c9c3fd4d1c903f06c0e70f69cc34902b5b80b110a19e8fc78f5e5 +generated: "2025-02-25T22:02:34.381897768Z" diff --git a/charts/kasten/k10/7.5.401/Chart.yaml b/charts/kasten/k10/7.5.401/Chart.yaml new file mode 100644 index 0000000000..f25c89384e --- /dev/null +++ b/charts/kasten/k10/7.5.401/Chart.yaml @@ -0,0 +1,22 @@ +annotations: + catalog.cattle.io/certified: partner + catalog.cattle.io/display-name: K10 + catalog.cattle.io/featured: "3" + catalog.cattle.io/kube-version: '>= 1.17.0-0' + catalog.cattle.io/release-name: k10 +apiVersion: v2 +appVersion: 7.5.4 +dependencies: +- condition: prometheus.server.enabled + name: prometheus + repository: "" + version: 26.1.0 +description: Kasten’s K10 Data Management Platform +home: https://kasten.io/ +icon: file://assets/icons/k10.png +kubeVersion: '>= 1.17.0-0' +maintainers: +- email: contact@kasten.io + name: kastenIO +name: k10 +version: 7.5.401 diff --git a/charts/kasten/k10/7.5.401/README.md b/charts/kasten/k10/7.5.401/README.md new file mode 100644 index 0000000000..caf5925085 --- /dev/null +++ b/charts/kasten/k10/7.5.401/README.md @@ -0,0 +1,342 @@ +# Kasten's K10 Helm chart. + +[Kasten's k10](https://docs.kasten.io/) is a data lifecycle management system for all your persistence.enabled +container-based applications. + +## TL;DR; + +```console +$ helm install kasten/k10 --name=k10 --namespace=kasten-io +``` +Additionally, K10 images are available in Platform One's **Iron Bank** hardened container registry. +To install using these images, follow the instructions found +[here](https://docs.kasten.io/latest/install/ironbank.html). + +## Introduction + +This chart bootstraps Kasten's K10 platform on a [Kubernetes](http://kubernetes.io) cluster using +the [Helm](https://helm.sh) package manager. + +## Prerequisites + +- Kubernetes 1.23 - 1.26 + +## Installing the Chart + +To install the chart on a [GKE](https://cloud.google.com/container-engine/) cluster + +```console +$ helm install kasten/k10 --name=k10 --namespace=kasten-io +``` + +To install the chart on an [AWS](https://aws.amazon.com/) [kops](https://github.com/kubernetes/kops)-created cluster + +```console +$ helm install kasten/k10 --name=k10 --namespace=kasten-io --set secrets.awsAccessKeyId="${AWS_ACCESS_KEY_ID}" \ + --set secrets.awsSecretAccessKey="${AWS_SECRET_ACCESS_KEY}" +``` + +> **Tip**: List all releases using `helm list` + +## Uninstalling the Chart + +To uninstall/delete the `k10` application: + +```console +$ helm delete k10 --purge +``` + +## Configuration + +The following table lists the configurable parameters of the K10 +chart and their default values. + +Parameter | Description | Default +--- | --- | --- +`eula.accept`| Whether to enable accept EULA before installation | `false` +`eula.company` | Company name. Required field if EULA is accepted | `None` +`eula.email` | Contact email. Required field if EULA is accepted | `None` +`license` | License string obtained from Kasten | `None` +`rbac.create` | Whether to enable RBAC with a specific cluster role and binding for K10 | `true` +`scc.create` | Whether to create a SecurityContextConstraints for K10 ServiceAccounts | `false` +`scc.priority` | Sets the SecurityContextConstraints priority | `15` +`services.dashboardbff.hostNetwork` | Whether the dashboardbff Pods may use the node network | `false` +`services.executor.hostNetwork` | Whether the executor Pods may use the node network | `false` +`services.aggregatedapis.hostNetwork` | Whether the aggregatedapis Pods may use the node network | `false` +`serviceAccount.create`| Specifies whether a ServiceAccount should be created | `true` +`serviceAccount.name` | The name of the ServiceAccount to use. If not set, a name is derived using the release and chart names. | `None` +`ingress.create` | Specifies whether the K10 dashboard should be exposed via ingress | `false` +`ingress.name` | Optional name of the Ingress object for the K10 dashboard. If not set, the name is formed using the release name. | `{Release.Name}-ingress` +`ingress.class` | Cluster ingress controller class: `nginx`, `GCE` | `None` +`ingress.host` | FQDN (e.g., `k10.example.com`) for name-based virtual host | `None` +`ingress.urlPath` | URL path for K10 Dashboard (e.g., `/k10`) | `Release.Name` +`ingress.pathType` | Specifies the path type for the ingress resource | `ImplementationSpecific` +`ingress.annotations` | Additional Ingress object annotations | `{}` +`ingress.tls.enabled` | Configures a TLS use for `ingress.host` | `false` +`ingress.tls.secretName` | Optional TLS secret name | `None` +`ingress.defaultBackend.service.enabled` | Configures the default backend backed by a service for the K10 dashboard Ingress (mutually exclusive setting with `ingress.defaultBackend.resource.enabled`). | `false` +`ingress.defaultBackend.service.name` | The name of a service referenced by the default backend (required if the service-backed default backend is used). | `None` +`ingress.defaultBackend.service.port.name` | The port name of a service referenced by the default backend (mutually exclusive setting with port `number`, required if the service-backed default backend is used). | `None` +`ingress.defaultBackend.service.port.number` | The port number of a service referenced by the default backend (mutually exclusive setting with port `name`, required if the service-backed default backend is used). | `None` +`ingress.defaultBackend.resource.enabled` | Configures the default backend backed by a resource for the K10 dashboard Ingress (mutually exclusive setting with `ingress.defaultBackend.service.enabled`). | `false` +`ingress.defaultBackend.resource.apiGroup` | Optional API group of a resource backing the default backend. | `''` +`ingress.defaultBackend.resource.kind` | The type of a resource being referenced by the default backend (required if the resource default backend is used). | `None` +`ingress.defaultBackend.resource.name` | The name of a resource being referenced by the default backend (required if the resource default backend is used). | `None` +`global.persistence.size` | Default global size of volumes for K10 persistent services | `20Gi` +`global.persistence.catalog.size` | Size of a volume for catalog service | `global.persistence.size` +`global.persistence.jobs.size` | Size of a volume for jobs service | `global.persistence.size` +`global.persistence.logging.size` | Size of a volume for logging service | `global.persistence.size` +`global.persistence.metering.size` | Size of a volume for metering service | `global.persistence.size` +`global.persistence.storageClass` | Specified StorageClassName will be used for PVCs | `None` +`global.podLabels` | Configures custom labels to be set to all Kasten Pods | `None` +`global.podAnnotations` | Configures custom annotations to be set to all Kasten Pods | `None` +`global.airgapped.repository` | Specify the helm repository for offline (airgapped) installation | `''` +`global.imagePullSecret` | Provide secret which contains docker config for private repository. Use `k10-ecr` when secrets.dockerConfigPath is used. | `''` +`global.prometheus.external.host` | Provide external prometheus host name | `''` +`global.prometheus.external.port` | Provide external prometheus port number | `''` +`global.prometheus.external.baseURL` | Provide Base URL of external prometheus | `''` +`global.network.enable_ipv6` | Enable `IPv6` support for K10 | `false` +`google.workloadIdentityFederation.enabled` | Enable Google Workload Identity Federation for K10 | `false` +`google.workloadIdentityFederation.idp.type` | Identity Provider type for Google Workload Identity Federation for K10 | `''` +`google.workloadIdentityFederation.idp.aud` | Audience for whom the ID Token from Identity Provider is intended | `''` +`secrets.awsAccessKeyId` | AWS access key ID (required for AWS deployment) | `None` +`secrets.awsSecretAccessKey` | AWS access key secret | `None` +`secrets.awsIamRole` | ARN of the AWS IAM role assumed by K10 to perform any AWS operation. | `None` +`secrets.awsClientSecretName` | The secret that contains AWS access key ID, AWS access key secret and AWS IAM role for AWS | `None` +`secrets.googleApiKey` | Non-default base64 encoded GCP Service Account key | `None` +`secrets.googleProjectId` | Sets Google Project ID other than the one used in the GCP Service Account | `None` +`secrets.azureTenantId` | Azure tenant ID (required for Azure deployment) | `None` +`secrets.azureClientId` | Azure Service App ID | `None` +`secrets.azureClientSecret` | Azure Service APP secret | `None` +`secrets.azureClientSecretName` | The secret that contains ClientID, ClientSecret and TenantID for Azure | `None` +`secrets.azureResourceGroup` | Resource Group name that was created for the Kubernetes cluster | `None` +`secrets.azureSubscriptionID` | Subscription ID in your Azure tenant | `None` +`secrets.azureResourceMgrEndpoint` | Resource management endpoint for the Azure Stack instance | `None` +`secrets.azureADEndpoint` | Azure Active Directory login endpoint | `None` +`secrets.azureADResourceID` | Azure Active Directory resource ID to obtain AD tokens | `None` +`secrets.microsoftEntraIDEndpoint` | Microsoft Entra ID login endpoint | `None` +`secrets.microsoftEntraIDResourceID` | Microsoft Entra ID resource ID to obtain AD tokens | `None` +`secrets.azureCloudEnvID` | Azure Cloud Environment ID | `None` +`secrets.vsphereEndpoint` | vSphere endpoint for login | `None` +`secrets.vsphereUsername` | vSphere username for login | `None` +`secrets.vspherePassword` | vSphere password for login | `None` +`secrets.vsphereClientSecretName` | The secret that contains vSphere username, vSphere password and vSphere endpoint | `None` +`secrets.dockerConfig` | Set base64 encoded docker config to use for image pull operations. Alternative to the ``secrets.dockerConfigPath`` | `None` +`secrets.dockerConfigPath` | Use ``--set-file secrets.dockerConfigPath=path_to_docker_config.yaml`` to specify docker config for image pull. Will be overwritten if ``secrets.dockerConfig`` is set | `None` +`cacertconfigmap.name` | Name of the ConfigMap that contains a certificate for a trusted root certificate authority | `None` +`clusterName` | Cluster name for better logs visibility | `None` +`metering.awsRegion` | Sets AWS_REGION for metering service | `None` +`metering.mode` | Control license reporting (set to `airgap` for private-network installs) | `None` +`metering.reportCollectionPeriod` | Sets metric report collection period (in seconds) | `1800` +`metering.reportPushPeriod` | Sets metric report push period (in seconds) | `3600` +`metering.promoID` | Sets K10 promotion ID from marketing campaigns | `None` +`metering.awsMarketplace` | Sets AWS cloud metering license mode | `false` +`metering.awsManagedLicense` | Sets AWS managed license mode | `false` +`metering.redhatMarketplacePayg` | Sets Red Hat cloud metering license mode | `false` +`metering.licenseConfigSecretName` | Sets AWS managed license config secret | `None` +`externalGateway.create` | Configures an external gateway for K10 API services | `false` +`externalGateway.annotations` | Standard annotations for the services | `None` +`externalGateway.fqdn.name` | Domain name for the K10 API services | `None` +`externalGateway.fqdn.type` | Supported gateway type: `route53-mapper` or `external-dns` | `None` +`externalGateway.awsSSLCertARN` | ARN for the AWS ACM SSL certificate used in the K10 API server | `None` +`auth.basicAuth.enabled` | Configures basic authentication for the K10 dashboard | `false` +`auth.basicAuth.htpasswd` | A username and password pair separated by a colon character | `None` +`auth.basicAuth.secretName` | Name of an existing Secret that contains a file generated with htpasswd | `None` +`auth.k10AdminGroups` | A list of groups whose members are granted admin level access to K10's dashboard | `None` +`auth.k10AdminUsers` | A list of users who are granted admin level access to K10's dashboard | `None` +`auth.tokenAuth.enabled` | Configures token based authentication for the K10 dashboard | `false` +`auth.oidcAuth.enabled` | Configures Open ID Connect based authentication for the K10 dashboard | `false` +`auth.oidcAuth.providerURL` | URL for the OIDC Provider | `None` +`auth.oidcAuth.redirectURL` | URL to the K10 gateway service | `None` +`auth.oidcAuth.scopes` | Space separated OIDC scopes required for userinfo. Example: "profile email" | `None` +`auth.oidcAuth.prompt` | The type of prompt to be used during authentication (none, consent, login or select_account) | `select_account` +`auth.oidcAuth.clientID` | Client ID given by the OIDC provider for K10 | `None` +`auth.oidcAuth.clientSecret` | Client secret given by the OIDC provider for K10 | `None` +`auth.oidcAuth.clientSecretName` | The secret that contains the Client ID and Client secret given by the OIDC provider for K10 | `None` +`auth.oidcAuth.usernameClaim` | The claim to be used as the username | `sub` +`auth.oidcAuth.usernamePrefix` | Prefix that has to be used with the username obtained from the username claim | `None` +`auth.oidcAuth.groupClaim` | Name of a custom OpenID Connect claim for specifying user groups | `None` +`auth.oidcAuth.groupPrefix` | All groups will be prefixed with this value to prevent conflicts | `None` +`auth.oidcAuth.sessionDuration` | Maximum OIDC session duration | `1h` +`auth.oidcAuth.refreshTokenSupport` | Enable OIDC Refresh Token support | `false` +`auth.openshift.enabled` | Enables access to the K10 dashboard by authenticating with the OpenShift OAuth server | `false` +`auth.openshift.serviceAccount` | Name of the service account that represents an OAuth client | `None` +`auth.openshift.clientSecret` | The token corresponding to the service account | `None` +`auth.openshift.clientSecretName` | The secret that contains the token corresponding to the service account | `None` +`auth.openshift.dashboardURL` | The URL used for accessing K10's dashboard | `None` +`auth.openshift.openshiftURL` | The URL for accessing OpenShift's API server | `None` +`auth.openshift.insecureCA` | To turn off SSL verification of connections to OpenShift | `false` +`auth.openshift.useServiceAccountCA` | Set this to true to use the CA certificate corresponding to the Service Account ``auth.openshift.serviceAccount`` usually found at ``/var/run/secrets/kubernetes.io/serviceaccount/ca.crt`` | `false` +`auth.openshift.caCertsAutoExtraction` | Set this to false to disable the OCP CA certificates automatic extraction to the K10 namespace | `true` +`auth.ldap.enabled` | Configures Active Directory/LDAP based authentication for the K10 dashboard | `false` +`auth.ldap.restartPod` | To force a restart of the authentication service Pod (useful when updating authentication config) | `false` +`auth.ldap.dashboardURL` | The URL used for accessing K10's dashboard | `None` +`auth.ldap.host` | Host and optional port of the AD/LDAP server in the form `host:port` | `None` +`auth.ldap.insecureNoSSL` | Required if the AD/LDAP host is not using TLS | `false` +`auth.ldap.insecureSkipVerifySSL` | To turn off SSL verification of connections to the AD/LDAP host | `false` +`auth.ldap.startTLS` | When set to true, ldap:// is used to connect to the server followed by creation of a TLS session. When set to false, ldaps:// is used. | `false` +`auth.ldap.bindDN` | The Distinguished Name(username) used for connecting to the AD/LDAP host | `None` +`auth.ldap.bindPW` | The password corresponding to the `bindDN` for connecting to the AD/LDAP host | `None` +`auth.ldap.bindPWSecretName` | The name of the secret that contains the password corresponding to the `bindDN` for connecting to the AD/LDAP host | `None` +`auth.ldap.userSearch.baseDN` | The base Distinguished Name to start the AD/LDAP search from | `None` +`auth.ldap.userSearch.filter` | Optional filter to apply when searching the directory | `None` +`auth.ldap.userSearch.username` | Attribute used for comparing user entries when searching the directory | `None` +`auth.ldap.userSearch.idAttr` | AD/LDAP attribute in a user's entry that should map to the user ID field in a token | `None` +`auth.ldap.userSearch.emailAttr` | AD/LDAP attribute in a user's entry that should map to the email field in a token | `None` +`auth.ldap.userSearch.nameAttr` | AD/LDAP attribute in a user's entry that should map to the name field in a token | `None` +`auth.ldap.userSearch.preferredUsernameAttr` | AD/LDAP attribute in a user's entry that should map to the preferred_username field in a token | `None` +`auth.ldap.groupSearch.baseDN` | The base Distinguished Name to start the AD/LDAP group search from | `None` +`auth.ldap.groupSearch.filter` | Optional filter to apply when searching the directory for groups | `None` +`auth.ldap.groupSearch.nameAttr` | The AD/LDAP attribute that represents a group's name in the directory | `None` +`auth.ldap.groupSearch.userMatchers` | List of field pairs that are used to match a user to a group. | `None` +`auth.ldap.groupSearch.userMatchers.userAttr` | Attribute in the user's entry that must match with the `groupAttr` while searching for groups | `None` +`auth.ldap.groupSearch.userMatchers.groupAttr` | Attribute in the group's entry that must match with the `userAttr` while searching for groups | `None` +`auth.groupAllowList` | A list of groups whose members are allowed access to K10's dashboard | `None` +`services.securityContext` | Custom [security context](https://kubernetes.io/docs/tasks/configure-pod-container/security-context/) for K10 service containers | `{"runAsUser" : 1000, "fsGroup": 1000}` +`services.securityContext.runAsUser` | User ID K10 service containers run as| `1000` +`services.securityContext.runAsGroup` | Group ID K10 service containers run as| `1000` +`services.securityContext.fsGroup` | FSGroup that owns K10 service container volumes | `1000` +`siem.logging.cluster.enabled` | Whether to enable writing K10 audit event logs to stdout (standard output) | `true` +`siem.logging.cloud.path` | Directory path for saving audit logs in a cloud object store | `k10audit/` +`siem.logging.cloud.awsS3.enabled` | Whether to enable sending K10 audit event logs to AWS S3 | `true` +`injectGenericVolumeBackupSidecar.enabled` | Enables injection of sidecar container required to perform Generic Volume Backup into workload Pods | `false` +`injectGenericVolumeBackupSidecar.namespaceSelector.matchLabels` | Set of labels to select namespaces in which sidecar injection is enabled for workloads | `{}` +`injectGenericVolumeBackupSidecar.objectSelector.matchLabels` | Set of labels to filter workload objects in which the sidecar is injected | `{}` +`injectGenericVolumeBackupSidecar.webhookServer.port` | Port number on which the mutating webhook server accepts request | `8080` +`gateway.resources.[requests\|limits].[cpu\|memory]` | Resource requests and limits for gateway Pod | `{}` +`gateway.service.externalPort` | Specifies the gateway services external port | `80` +`genericVolumeSnapshot.resources.[requests\|limits].[cpu\|memory]` | Specifies resource requests and limits for generic backup sidecar and all temporary Kasten worker Pods. Superseded by ActionPodSpec | `{}` +`multicluster.enabled` | Choose whether to enable the multi-cluster system components and capabilities | `true` +`multicluster.primary.create` | Choose whether to setup cluster as a multi-cluster primary | `false` +`multicluster.primary.name` | Primary cluster name | `''` +`multicluster.primary.ingressURL` | Primary cluster dashboard URL | `''` +`prometheus.k10image.registry` | (optional) Set Prometheus image registry. | `gcr.io` +`prometheus.k10image.repository` | (optional) Set Prometheus image repository. | `kasten-images` +`prometheus.rbac.create` | (optional) Whether to create Prometheus RBAC configuration. Warning - this action will allow prometheus to scrape Pods in all k8s namespaces | `false` +`prometheus.alertmanager.enabled` | DEPRECATED: (optional) Enable Prometheus `alertmanager` service | `false` +`prometheus.alertmanager.serviceAccount.create` | DEPRECATED: (optional) Set true to create ServiceAccount for `alertmanager` | `false` +`prometheus.networkPolicy.enabled` | DEPRECATED: (optional) Enable Prometheus `networkPolicy` | `false` +`prometheus.prometheus-node-exporter.enabled` | DEPRECATED: (optional) Enable Prometheus `node-exporter` | `false` +`prometheus.prometheus-node-exporter.serviceAccount.create` | DEPRECATED: (optional) Set true to create ServiceAccount for `prometheus-node-exporter` | `false` +`prometheus.prometheus-pushgateway.enabled` | DEPRECATED: (optional) Enable Prometheus `pushgateway` | `false` +`prometheus.prometheus-pushgateway.serviceAccount.create` | DEPRECATED: (optional) Set true to create ServiceAccount for `prometheus-pushgateway` | `false` +`prometheus.scrapeCAdvisor` | DEPRECATED: (optional) Enable Prometheus ScrapeCAdvisor | `false` +`prometheus.server.enabled` | (optional) If false, K10's Prometheus server will not be created, reducing the dashboard's functionality. | `true` +`prometheus.server.securityContext.runAsUser` | (optional) Set security context `runAsUser` ID for Prometheus server Pod | `65534` +`prometheus.server.securityContext.runAsNonRoot` | (optional) Enable security context `runAsNonRoot` for Prometheus server Pod | `true` +`prometheus.server.securityContext.runAsGroup` | (optional) Set security context `runAsGroup` ID for Prometheus server Pod | `65534` +`prometheus.server.securityContext.fsGroup` | (optional) Set security context `fsGroup` ID for Prometheus server Pod | `65534` +`prometheus.server.retention` | (optional) K10 Prometheus data retention | `"30d"` +`prometheus.server.strategy.rollingUpdate.maxSurge` | DEPRECATED: (optional) The number of Prometheus server Pods that can be created above the desired amount of Pods during an update | `"100%"` +`prometheus.server.strategy.rollingUpdate.maxUnavailable` | DEPRECATED: (optional) The number of Prometheus server Pods that can be unavailable during the upgrade process | `"100%"` +`prometheus.server.strategy.type` | DEPRECATED: (optional) Change default deployment strategy for Prometheus server | `"RollingUpdate"` +`prometheus.server.persistentVolume.enabled` | DEPRECATED: (optional) If true, K10 Prometheus server will create a Persistent Volume Claim | `true` +`prometheus.server.persistentVolume.size` | (optional) K10 Prometheus server data Persistent Volume size | `8Gi` +`prometheus.server.persistentVolume.storageClass` | (optional) StorageClassName used to create Prometheus PVC. Setting this option overwrites global StorageClass value | `""` +`prometheus.server.configMapOverrideName` | DEPRECATED: (optional) Prometheus configmap name to override default generated name| `k10-prometheus-config` +`prometheus.server.fullnameOverride` | (optional) Prometheus deployment name to override default generated name| `prometheus-server` +`prometheus.server.baseURL` | (optional) K10 Prometheus external url path at which the server can be accessed | `/k10/prometheus/` +`prometheus.server.prefixURL` | (optional) K10 Prometheus prefix slug at which the server can be accessed | `/k10/prometheus/` +`prometheus.server.serviceAccounts.server.create` | DEPRECATED: (optional) Set true to create ServiceAccount for Prometheus server service | `true` +`resources...[requests\|limits].[cpu\|memory]` | Overwriting the default K10 [container resource requests and limits](https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/) | varies depending on the container +`route.enabled` | Specifies whether the K10 dashboard should be exposed via route | `false` +`route.host` | FQDN (e.g., `.k10.example.com`) for name-based virtual host | `""` +`route.path` | URL path for K10 Dashboard (e.g., `/k10`) | `/` +`route.annotations` | Additional Route object annotations | `{}` +`route.labels` | Additional Route object labels | `{}` +`route.tls.enabled` | Configures a TLS use for `route.host` | `false` +`route.tls.insecureEdgeTerminationPolicy` | Specifies behavior for insecure scheme traffic | `Redirect` +`route.tls.termination` | Specifies the TLS termination of the route | `edge` +`limiter.executorReplicas` | Specifies the number of executor-svc Pods used to process Kasten jobs | 3 +`limiter.executorThreads` | Specifies the number of threads per executor-svc Pod used to process Kasten jobs | 8 +`limiter.workloadSnapshotsPerAction` | Per action limit of concurrent manifest data snapshots, based on workload (ex. Namespace, Deployment, StatefulSet, VirtualMachine) | 5 +`limiter.csiSnapshotsPerCluster` | Cluster-wide limit of concurrent CSI VolumeSnapshot creation requests | `10` +`limiter.directSnapshotsPerCluster` | Cluster-wide limit of concurrent non-CSI snapshot creation requests | `10` +`limiter.snapshotExportsPerAction` | Per action limit of concurrent volume export operations | `3` +`limiter.snapshotExportsPerCluster` | Cluster-wide limit of concurrent volume export operations | `10` +`limiter.genericVolumeBackupsPerCluster` | Cluster-wide limit of concurrent Generic Volume Backup operations | `10` +`limiter.imageCopiesPerCluster` | Cluster-wide limit of concurrent ImageStream container image backup (i.e. copy from) and restore (i.e. copy to) operations | `10` +`limiter.workloadRestoresPerAction` | Per action limit of concurrent manifest data restores, based on workload (ex. Namespace, Deployment, StatefulSet, VirtualMachine) | 3 +`limiter.csiSnapshotRestoresPerAction` | Per action limit of concurrent CSI volume provisioning requests when restoring from VolumeSnapshots | 3 +`limiter.volumeRestoresPerAction` | Per action limit of concurrent volume restore operations from an exported backup | 3 +`limiter.volumeRestoresPerCluster` | Cluster-wide limit of concurrent volume restore operations from exported backups | `10` +`cluster.domainName` | Specifies the domain name of the cluster | `""` +`timeout.blueprintBackup` | Specifies the timeout (in minutes) for Blueprint backup actions | `45` +`timeout.blueprintRestore` | Specifies the timeout (in minutes) for Blueprint restore actions | `600` +`timeout.blueprintDelete` | Specifies the timeout (in minutes) for Blueprint delete actions | `45` +`timeout.blueprintHooks` | Specifies the timeout (in minutes) for Blueprint backupPrehook and backupPosthook actions | `20` +`timeout.checkRepoPodReady` | Specifies the timeout (in minutes) for temporary worker Pods used to validate backup repository existence | `20` +`timeout.statsPodReady` | Specifies the timeout (in minutes) for temporary worker Pods used to collect repository statistics | `20` +`timeout.efsRestorePodReady` | Specifies the timeout (in minutes) for temporary worker Pods used for shareable volume restore operations | `45` +`timeout.workerPodReady` | Specifies the timeout (in minutes) for all other temporary worker Pods used during Veeam Kasten operations | `15` +`timeout.jobWait` | Specifies the timeout (in minutes) for completing execution of any child job, after which the parent job will be canceled. If no value is set, a default of 10 hours will be used | `None` +`awsConfig.assumeRoleDuration` | Duration of a session token generated by AWS for an IAM role. The minimum value is 15 minutes and the maximum value is the maximum duration setting for that IAM role. For documentation about how to view and edit the maximum session duration for an IAM role see https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_use.html#id_roles_use_view-role-max-session. The value accepts a number along with a single character ``m``(for minutes) or ``h`` (for hours) Examples: 60m or 2h | `''` +`awsConfig.efsBackupVaultName` | Specifies the AWS EFS backup vault name | `k10vault` +`vmWare.taskTimeoutMin` | Specifies the timeout for VMWare operations | `60` +`encryption.primaryKey.awsCmkKeyId` | Specifies the AWS CMK key ID for encrypting K10 Primary Key | `None` +`garbagecollector.daemonPeriod` | Sets garbage collection period (in seconds) | `21600` +`garbagecollector.keepMaxActions` | Sets maximum actions to keep | `1000` +`garbagecollector.actions.enabled` | Enables action collectors | `false` +`kubeVirtVMs.snapshot.unfreezeTimeout` | Defines the time duration within which the VMs must be unfrozen while backing them up. To know more about format [go doc](https://pkg.go.dev/time#ParseDuration) can be followed | `5m` +`excludedApps` | Specifies a list of applications to be excluded from the dashboard & compliance considerations. Format should be a :ref:`YAML array` | `["kube-system", "kube-ingress", "kube-node-lease", "kube-public", "kube-rook-ceph"]` +`workerPodMetricSidecar.enabled` | Enables a sidecar container for temporary worker Pods used to push Pod performance metrics to Prometheus | `true` +`workerPodMetricSidecar.metricLifetime` | Specifies the period after which metrics for an individual worker Pod are removed from Prometheus | `2m` +`workerPodMetricSidecar.pushGatewayInterval` | Specifies the frequency for pushing metrics into Prometheus | `30s` +`workerPodMetricSidecar.resources.[requests\|limits].[cpu\|memory]` | Specifies resource requests and limits for the temporary worker Pod metric sidecar | `{}` +`forceRootInBlueprintActions` | Forces any Pod created by a Blueprint to run as root user | `true` +`defaultPriorityClassName` | Specifies the default [priority class](https://kubernetes.io/docs/concepts/scheduling-eviction/pod-priority-preemption/#priorityclass) name for all K10 deployments and ephemeral Pods | `None` +`priorityClassName.` | Overrides the default [priority class](https://kubernetes.io/docs/concepts/scheduling-eviction/pod-priority-preemption/#priorityclass) name for the specified deployment | `{}` +`ephemeralPVCOverhead` | Set the percentage increase for the ephemeral Persistent Volume Claim's storage request, e.g. PVC size = (file raw size) * (1 + `ephemeralPVCOverhead`) | `0.1` +`datastore.parallelUploads` | Specifies how many files can be uploaded in parallel to the data store | `8` +`datastore.parallelDownloads` | Specifies how many files can be downloaded in parallel from the data store | `8` +`datastore.parallelBlockUploads` | Specifies how many blocks can be uploaded in parallel to the data store | `8` +`datastore.parallelBlockDownloads` | Specifies how many blocks can be downloaded in parallel from the data store | `8` +`kastenDisasterRecovery.quickMode.enabled` | Enables K10 Quick Disaster Recovery | `false` +`fips.enabled` | Specifies whether K10 should be run in the FIPS mode of operation | `false` +`workerPodCRDs.enabled` | Specifies whether K10 should use `ActionPodSpec` for granular resource control of worker Pods | `false` +`workerPodCRDs.resourcesRequests.maxCPU` | Max CPU which might be setup in `ActionPodSpec` | `''` +`workerPodCRDs.resourcesRequests.maxMemory` | Max memory which might be setup in `ActionPodSpec` | `''` +`workerPodCRDs.defaultActionPodSpec.name` | The name of `ActionPodSpec` that will be used by default for worker Pod resources. | `''` +`workerPodCRDs.defaultActionPodSpec.namespace` | The namespace of `ActionPodSpec` that will be used by default for worker Pod resources. | `''` + + + +## Helm tips and tricks + +There is a way of setting values via a yaml file instead of using `--set`. +First, copy/paste values into a file (e.g., my_values.yaml): + +```yaml +secrets: + awsAccessKeyId: ${AWS_ACCESS_KEY_ID} + awsSecretAccessKey: ${AWS_SECRET_ACCESS_KEY} +``` + +and then run: + +```bash + envsubst < my_values.yaml > my_values_out.yaml && helm install k10 kasten/k10 -f my_values_out.yaml +``` + +To set a single value from a file, `--set-file` may be used over `--set`: + +```bash + helm install k10 kasten/k10 --set-file license=my_license.lic +``` + + +To use non-default GCP ServiceAccount (SA) credentials, the credentials JSON file needs to be encoded into a base64 +string: + +```bash + sa_key=$(base64 -w0 sa-key.json) + helm install k10 kasten/k10 --namespace=kasten-io --set secrets.googleApiKey=$sa_key +``` + +If the Google Service Account belongs to a project other than the one in which the cluster +is located, then the project's ID of the cluster must be also provided during the installation: + +```bash + sa_key=$(base64 -w0 sa-key.json) + helm install k10 kasten/k10 --namespace=kasten-io --set secrets.googleApiKey=$sa_key --set secrets.googleProjectId= +``` diff --git a/charts/kasten/k10/7.5.401/app-readme.md b/charts/kasten/k10/7.5.401/app-readme.md new file mode 100644 index 0000000000..1b221891be --- /dev/null +++ b/charts/kasten/k10/7.5.401/app-readme.md @@ -0,0 +1,5 @@ +The K10 data management platform, purpose-built for Kubernetes, provides enterprise operations teams an easy-to-use, scalable, and secure system for backup/restore, disaster recovery, and mobility of Kubernetes applications. + +K10’s application-centric approach and deep integrations with relational and NoSQL databases, Kubernetes distributions, and all clouds provide teams the freedom of infrastructure choice without sacrificing operational simplicity. Policy-driven and extensible, K10 provides a native Kubernetes API and includes features such as full-spectrum consistency, database integrations, automatic application discovery, multi-cloud mobility, and a powerful web-based user interface. + +For more information, refer to the docs [https://docs.kasten.io/](https://docs.kasten.io/) diff --git a/charts/kasten/k10/7.5.401/charts/prometheus/.helmignore b/charts/kasten/k10/7.5.401/charts/prometheus/.helmignore new file mode 100644 index 0000000000..825c007791 --- /dev/null +++ b/charts/kasten/k10/7.5.401/charts/prometheus/.helmignore @@ -0,0 +1,23 @@ +# 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 + +OWNERS diff --git a/charts/kasten/k10/7.5.401/charts/prometheus/Chart.lock b/charts/kasten/k10/7.5.401/charts/prometheus/Chart.lock new file mode 100644 index 0000000000..76f0686d9e --- /dev/null +++ b/charts/kasten/k10/7.5.401/charts/prometheus/Chart.lock @@ -0,0 +1,15 @@ +dependencies: +- name: alertmanager + repository: https://prometheus-community.github.io/helm-charts + version: 1.13.1 +- name: kube-state-metrics + repository: https://prometheus-community.github.io/helm-charts + version: 5.28.0 +- name: prometheus-node-exporter + repository: https://prometheus-community.github.io/helm-charts + version: 4.43.1 +- name: prometheus-pushgateway + repository: https://prometheus-community.github.io/helm-charts + version: 2.16.0 +digest: sha256:a6732d7d72e5ac83623517b0da7782493df63f9900ca6c2787a40872fd61333c +generated: "2025-01-02T15:11:50.218607-05:00" diff --git a/charts/kasten/k10/7.5.401/charts/prometheus/Chart.yaml b/charts/kasten/k10/7.5.401/charts/prometheus/Chart.yaml new file mode 100644 index 0000000000..7c877fd659 --- /dev/null +++ b/charts/kasten/k10/7.5.401/charts/prometheus/Chart.yaml @@ -0,0 +1,53 @@ +annotations: + artifacthub.io/license: Apache-2.0 + artifacthub.io/links: | + - name: Chart Source + url: https://github.com/prometheus-community/helm-charts + - name: Upstream Project + url: https://github.com/prometheus/prometheus +apiVersion: v2 +appVersion: v3.1.0 +dependencies: +- condition: alertmanager.enabled + name: alertmanager + repository: https://prometheus-community.github.io/helm-charts + version: 1.13.* +- condition: kube-state-metrics.enabled + name: kube-state-metrics + repository: https://prometheus-community.github.io/helm-charts + version: 5.28.* +- condition: prometheus-node-exporter.enabled + name: prometheus-node-exporter + repository: https://prometheus-community.github.io/helm-charts + version: 4.43.* +- condition: prometheus-pushgateway.enabled + name: prometheus-pushgateway + repository: https://prometheus-community.github.io/helm-charts + version: 2.16.* +description: Prometheus is a monitoring system and time series database. +home: https://prometheus.io/ +icon: https://raw.githubusercontent.com/prometheus/prometheus.github.io/master/assets/prometheus_logo-cb55bb5c346.png +keywords: +- monitoring +- prometheus +kubeVersion: '>=1.19.0-0' +maintainers: +- email: gianrubio@gmail.com + name: gianrubio +- email: zanhsieh@gmail.com + name: zanhsieh +- email: miroslav.hadzhiev@gmail.com + name: Xtigyro +- email: naseem@transit.app + name: naseemkullah +- email: rootsandtrees@posteo.de + name: zeritti +name: prometheus +sources: +- https://github.com/prometheus/alertmanager +- https://github.com/prometheus/prometheus +- https://github.com/prometheus/pushgateway +- https://github.com/prometheus/node_exporter +- https://github.com/kubernetes/kube-state-metrics +type: application +version: 26.1.0 diff --git a/charts/kasten/k10/7.5.401/charts/prometheus/OWNERS b/charts/kasten/k10/7.5.401/charts/prometheus/OWNERS new file mode 100644 index 0000000000..0cfd95021c --- /dev/null +++ b/charts/kasten/k10/7.5.401/charts/prometheus/OWNERS @@ -0,0 +1,6 @@ +approvers: +- mgoodness +- gianrubio +reviewers: +- mgoodness +- gianrubio diff --git a/charts/kasten/k10/7.5.401/charts/prometheus/README.md b/charts/kasten/k10/7.5.401/charts/prometheus/README.md new file mode 100644 index 0000000000..7dd51f4b44 --- /dev/null +++ b/charts/kasten/k10/7.5.401/charts/prometheus/README.md @@ -0,0 +1,387 @@ +# Prometheus + +[Prometheus](https://prometheus.io/), a [Cloud Native Computing Foundation](https://cncf.io/) project, is a systems and service monitoring system. It collects metrics from configured targets at given intervals, evaluates rule expressions, displays the results, and can trigger alerts if some condition is observed to be true. + +This chart bootstraps a [Prometheus](https://prometheus.io/) deployment on a [Kubernetes](http://kubernetes.io) cluster using the [Helm](https://helm.sh) package manager. + +## Prerequisites + +- Kubernetes 1.19+ +- Helm 3.7+ + +## Get Repository Info + +```console +helm repo add prometheus-community https://prometheus-community.github.io/helm-charts +helm repo update +``` + +_See [helm repository](https://helm.sh/docs/helm/helm_repo/) for command documentation._ + +## Install Chart + +Starting with version 16.0, the Prometheus chart requires Helm 3.7+ in order to install successfully. Please check your `helm` release before installation. + +```console +helm install [RELEASE_NAME] prometheus-community/prometheus +``` + +_See [configuration](#configuration) below._ + +_See [helm install](https://helm.sh/docs/helm/helm_install/) for command documentation._ + +## Dependencies + +By default this chart installs additional, dependent charts: + +- [alertmanager](https://github.com/prometheus-community/helm-charts/tree/main/charts/alertmanager) +- [kube-state-metrics](https://github.com/prometheus-community/helm-charts/tree/main/charts/kube-state-metrics) +- [prometheus-node-exporter](https://github.com/prometheus-community/helm-charts/tree/main/charts/prometheus-node-exporter) +- [prometheus-pushgateway](https://github.com/walker-tom/helm-charts/tree/main/charts/prometheus-pushgateway) + +To disable the dependency during installation, set `alertmanager.enabled`, `kube-state-metrics.enabled`, `prometheus-node-exporter.enabled` and `prometheus-pushgateway.enabled` to `false`. + +_See [helm dependency](https://helm.sh/docs/helm/helm_dependency/) for command documentation._ + +## Uninstall Chart + +```console +helm uninstall [RELEASE_NAME] +``` + +This removes all the Kubernetes components associated with the chart and deletes the release. + +_See [helm uninstall](https://helm.sh/docs/helm/helm_uninstall/) for command documentation._ + +## Updating values.schema.json + +A [`values.schema.json`](https://helm.sh/docs/topics/charts/#schema-files) file has been added to validate chart values. When `values.yaml` file has a structure change (i.e. add a new field, change value type, etc.), modify `values.schema.json` file manually or run `helm schema-gen values.yaml > values.schema.json` to ensure the schema is aligned with the latest values. Refer to [helm plugin `helm-schema-gen`](https://github.com/karuppiah7890/helm-schema-gen) for plugin installation instructions. + +## Upgrading Chart + +```console +helm upgrade [RELEASE_NAME] prometheus-community/prometheus --install +``` + +_See [helm upgrade](https://helm.sh/docs/helm/helm_upgrade/) for command documentation._ + +### To 26.0 + +This release changes default version of promethues to v3.0.0, See official [migration guide](https://prometheus.io/docs/prometheus/latest/migration/#prometheus-3-0-migration-guide +) and [release notes](https://github.com/prometheus/prometheus/releases/tag/v3.0.0) for more details. + +### To 25.0 + +The `server.remoteRead[].url` and `server.remoteWrite[].url` fields now support templating. Allowing for `url` values such as `https://{{ .Release.Name }}.example.com`. + +Any entries in these which previously included `{{` or `}}` must be escaped with `{{ "{{" }}` and `{{ "}}" }}` respectively. Entries which did not previously include the template-like syntax will not be affected. + +### To 24.0 + +Require Kubernetes 1.19+ + +Release 1.0.0 of the _alertmanager_ replaced [configmap-reload](https://github.com/jimmidyson/configmap-reload) with [prometheus-config-reloader](https://github.com/prometheus-operator/prometheus-operator/tree/main/cmd/prometheus-config-reloader). +Extra command-line arguments specified via `configmapReload.prometheus.extraArgs` are not compatible and will break with the new prometheus-config-reloader. Please, refer to the [sources](https://github.com/prometheus-operator/prometheus-operator/blob/main/cmd/prometheus-config-reloader/main.go) in order to make the appropriate adjustment to the extra command-line arguments. + +### To 23.0 + +Release 5.0.0 of the _kube-state-metrics_ chart introduced a separation of the `image.repository` value in two distinct values: + +```console + image: + registry: registry.k8s.io + repository: kube-state-metrics/kube-state-metrics +``` + +If a custom values file or CLI flags set `kube-state.metrics.image.repository`, please, set the new values accordingly. + +If you are upgrading _prometheus-pushgateway_ with the chart and _prometheus-pushgateway_ has been deployed as a statefulset with a persistent volume, the statefulset must be deleted before upgrading the chart, e.g.: + +```bash +kubectl delete sts -l app.kubernetes.io/name=prometheus-pushgateway -n monitoring --cascade=orphan +``` + +Users are advised to review changes in the corresponding chart releases before upgrading. + +### To 22.0 + +The `app.kubernetes.io/version` label has been removed from the pod selector. + +Therefore, you must delete the previous StatefulSet or Deployment before upgrading. Performing this operation will cause **Prometheus to stop functioning** until the upgrade is complete. + +```console +kubectl delete deploy,sts -l app.kubernetes.io/name=prometheus +``` + +### To 21.0 + +The Kubernetes labels have been updated to follow [Helm 3 label and annotation best practices](https://helm.sh/docs/chart_best_practices/labels/). +Specifically, labels mapping is listed below: + +| OLD | NEW | +|--------------------|------------------------------| +|heritage | app.kubernetes.io/managed-by | +|chart | helm.sh/chart | +|[container version] | app.kubernetes.io/version | +|app | app.kubernetes.io/name | +|release | app.kubernetes.io/instance | + +Therefore, depending on the way you've configured the chart, the previous StatefulSet or Deployment need to be deleted before upgrade. + +If `runAsStatefulSet: false` (this is the default): + +```console +kubectl delete deploy -l app=prometheus +``` + +If `runAsStatefulSet: true`: + +```console +kubectl delete sts -l app=prometheus +``` + +After that do the actual upgrade: + +```console +helm upgrade -i prometheus prometheus-community/prometheus +``` + +### To 20.0 + +The [configmap-reload](https://github.com/jimmidyson/configmap-reload) container was replaced by the [prometheus-config-reloader](https://github.com/prometheus-operator/prometheus-operator/tree/main/cmd/prometheus-config-reloader). +Extra command-line arguments specified via configmapReload.prometheus.extraArgs are not compatible and will break with the new prometheus-config-reloader, refer to the [sources](https://github.com/prometheus-operator/prometheus-operator/blob/main/cmd/prometheus-config-reloader/main.go) in order to make the appropriate adjustment to the extra command-line arguments. + +### To 19.0 + +Prometheus has been updated to version v2.40.5. + +Prometheus-pushgateway was updated to version 2.0.0 which adapted [Helm label and annotation best practices](https://helm.sh/docs/chart_best_practices/labels/). +See the [upgrade docs of the prometheus-pushgateway chart](https://github.com/prometheus-community/helm-charts/tree/main/charts/prometheus-pushgateway#to-200) to see whats to do, before you upgrade Prometheus! + +The condition in Chart.yaml to disable kube-state-metrics has been changed from `kubeStateMetrics.enabled` to `kube-state-metrics.enabled` + +The Docker image tag is used from appVersion field in Chart.yaml by default. + +Unused subchart configs has been removed and subchart config is now on the bottom of the config file. + +If Prometheus is used as deployment the updatestrategy has been changed to "Recreate" by default, so Helm updates work out of the box. + +`.Values.server.extraTemplates` & `.Values.server.extraObjects` has been removed in favour of `.Values.extraManifests`, which can do the same. + +`.Values.server.enabled` has been removed as it's useless now that all components are created by subcharts. + +All files in `templates/server` directory has been moved to `templates` directory. + +```bash +helm upgrade [RELEASE_NAME] prometheus-community/prometheus --version 19.0.0 +``` + +### To 18.0 + +Version 18.0.0 uses alertmanager service from the [alertmanager chart](https://github.com/prometheus-community/helm-charts/tree/main/charts/alertmanager). If you've made some config changes, please check the old `alertmanager` and the new `alertmanager` configuration section in values.yaml for differences. + +Note that the `configmapReload` section for `alertmanager` was moved out of dedicated section (`configmapReload.alertmanager`) to alertmanager embedded (`alertmanager.configmapReload`). + +Before you update, please scale down the `prometheus-server` deployment to `0` then perform upgrade: + +```bash +# In 17.x +kubectl scale deploy prometheus-server --replicas=0 +# Upgrade +helm upgrade [RELEASE_NAME] prometheus-community/prometheus --version 18.0.0 +``` + +### To 17.0 + +Version 17.0.0 uses pushgateway service from the [prometheus-pushgateway chart](https://github.com/prometheus-community/helm-charts/tree/main/charts/prometheus-pushgateway). If you've made some config changes, please check the old `pushgateway` and the new `prometheus-pushgateway` configuration section in values.yaml for differences. + +Before you update, please scale down the `prometheus-server` deployment to `0` then perform upgrade: + +```bash +# In 16.x +kubectl scale deploy prometheus-server --replicas=0 +# Upgrade +helm upgrade [RELEASE_NAME] prometheus-community/prometheus --version 17.0.0 +``` + +### To 16.0 + +Starting from version 16.0 embedded services (like alertmanager, node-exporter etc.) are moved out of Prometheus chart and the respecting charts from this repository are used as dependencies. Version 16.0.0 moves node-exporter service to [prometheus-node-exporter chart](https://github.com/prometheus-community/helm-charts/tree/main/charts/prometheus-node-exporter). If you've made some config changes, please check the old `nodeExporter` and the new `prometheus-node-exporter` configuration section in values.yaml for differences. + +Before you update, please scale down the `prometheus-server` deployment to `0` then perform upgrade: + +```bash +# In 15.x +kubectl scale deploy prometheus-server --replicas=0 +# Upgrade +helm upgrade [RELEASE_NAME] prometheus-community/prometheus --version 16.0.0 +``` + +### To 15.0 + +Version 15.0.0 changes the relabeling config, aligning it with the [Prometheus community conventions](https://github.com/prometheus/prometheus/pull/9832). If you've made manual changes to the relabeling config, you have to adapt your changes. + +Before you update please execute the following command, to be able to update kube-state-metrics: + +```bash +kubectl delete deployments.apps -l app.kubernetes.io/instance=prometheus,app.kubernetes.io/name=kube-state-metrics --cascade=orphan +``` + +### To 9.0 + +Version 9.0 adds a new option to enable or disable the Prometheus Server. This supports the use case of running a Prometheus server in one k8s cluster and scraping exporters in another cluster while using the same chart for each deployment. To install the server `server.enabled` must be set to `true`. + +### To 5.0 + +As of version 5.0, this chart uses Prometheus 2.x. This version of prometheus introduces a new data format and is not compatible with prometheus 1.x. It is recommended to install this as a new release, as updating existing releases will not work. See the [prometheus docs](https://prometheus.io/docs/prometheus/latest/migration/#storage) for instructions on retaining your old data. + +Prometheus version 2.x has made changes to alertmanager, storage and recording rules. Check out the migration guide [here](https://prometheus.io/docs/prometheus/2.0/migration/). + +Users of this chart will need to update their alerting rules to the new format before they can upgrade. + +### Example Migration + +Assuming you have an existing release of the prometheus chart, named `prometheus-old`. In order to update to prometheus 2.x while keeping your old data do the following: + +1. Update the `prometheus-old` release. Disable scraping on every component besides the prometheus server, similar to the configuration below: + + ```yaml + alertmanager: + enabled: false + alertmanagerFiles: + alertmanager.yml: "" + kubeStateMetrics: + enabled: false + nodeExporter: + enabled: false + pushgateway: + enabled: false + server: + extraArgs: + storage.local.retention: 720h + serverFiles: + alerts: "" + prometheus.yml: "" + rules: "" + ``` + +1. Deploy a new release of the chart with version 5.0+ using prometheus 2.x. In the values.yaml set the scrape config as usual, and also add the `prometheus-old` instance as a remote-read target. + + ```yaml + prometheus.yml: + ... + remote_read: + - url: http://prometheus-old/api/v1/read + ... + ``` + + Old data will be available when you query the new prometheus instance. + +## Configuration + +See [Customizing the Chart Before Installing](https://helm.sh/docs/intro/using_helm/#customizing-the-chart-before-installing). To see all configurable options with detailed comments, visit the chart's [values.yaml](./values.yaml), or run these configuration commands: + +```console +helm show values prometheus-community/prometheus +``` + +You may similarly use the above configuration commands on each chart [dependency](#dependencies) to see its configurations. + +### Scraping Pod Metrics via Annotations + +This chart uses a default configuration that causes prometheus to scrape a variety of kubernetes resource types, provided they have the correct annotations. In this section we describe how to configure pods to be scraped; for information on how other resource types can be scraped you can do a `helm template` to get the kubernetes resource definitions, and then reference the prometheus configuration in the ConfigMap against the prometheus documentation for [relabel_config](https://prometheus.io/docs/prometheus/latest/configuration/configuration/#relabel_config) and [kubernetes_sd_config](https://prometheus.io/docs/prometheus/latest/configuration/configuration/#kubernetes_sd_config). + +In order to get prometheus to scrape pods, you must add annotations to the pods as below: + +```yaml +metadata: + annotations: + prometheus.io/scrape: "true" + prometheus.io/path: /metrics + prometheus.io/port: "8080" +``` + +You should adjust `prometheus.io/path` based on the URL that your pod serves metrics from. `prometheus.io/port` should be set to the port that your pod serves metrics from. Note that the values for `prometheus.io/scrape` and `prometheus.io/port` must be enclosed in double quotes. + +### Sharing Alerts Between Services + +Note that when [installing](#install-chart) or [upgrading](#upgrading-chart) you may use multiple values override files. This is particularly useful when you have alerts belonging to multiple services in the cluster. For example, + +```yaml +# values.yaml +# ... + +# service1-alert.yaml +serverFiles: + alerts: + service1: + - alert: anAlert + # ... + +# service2-alert.yaml +serverFiles: + alerts: + service2: + - alert: anAlert + # ... +``` + +```console +helm install [RELEASE_NAME] prometheus-community/prometheus -f values.yaml -f service1-alert.yaml -f service2-alert.yaml +``` + +### RBAC Configuration + +Roles and RoleBindings resources will be created automatically for `server` service. + +To manually setup RBAC you need to set the parameter `rbac.create=false` and specify the service account to be used for each service by setting the parameters: `serviceAccounts.{{ component }}.create` to `false` and `serviceAccounts.{{ component }}.name` to the name of a pre-existing service account. + +> **Tip**: You can refer to the default `*-clusterrole.yaml` and `*-clusterrolebinding.yaml` files in [templates](templates/) to customize your own. + +### ConfigMap Files + +AlertManager is configured through [alertmanager.yml](https://prometheus.io/docs/alerting/configuration/). This file (and any others listed in `alertmanagerFiles`) will be mounted into the `alertmanager` pod. + +Prometheus is configured through [prometheus.yml](https://prometheus.io/docs/operating/configuration/). This file (and any others listed in `serverFiles`) will be mounted into the `server` pod. + +### Ingress TLS + +If your cluster allows automatic creation/retrieval of TLS certificates (e.g. [cert-manager](https://github.com/jetstack/cert-manager)), please refer to the documentation for that mechanism. + +To manually configure TLS, first create/retrieve a key & certificate pair for the address(es) you wish to protect. Then create a TLS secret in the namespace: + +```console +kubectl create secret tls prometheus-server-tls --cert=path/to/tls.cert --key=path/to/tls.key +``` + +Include the secret's name, along with the desired hostnames, in the alertmanager/server Ingress TLS section of your custom `values.yaml` file: + +```yaml +server: + ingress: + ## If true, Prometheus server Ingress will be created + ## + enabled: true + + ## Prometheus server Ingress hostnames + ## Must be provided if Ingress is enabled + ## + hosts: + - prometheus.domain.com + + ## Prometheus server Ingress TLS configuration + ## Secrets must be manually created in the namespace + ## + tls: + - secretName: prometheus-server-tls + hosts: + - prometheus.domain.com +``` + +### NetworkPolicy + +Enabling Network Policy for Prometheus will secure connections to Alert Manager and Kube State Metrics by only accepting connections from Prometheus Server. All inbound connections to Prometheus Server are still allowed. + +To enable network policy for Prometheus, install a networking plugin that implements the Kubernetes NetworkPolicy spec, and set `networkPolicy.enabled` to true. + +If NetworkPolicy is enabled for Prometheus' scrape targets, you may also need to manually create a networkpolicy which allows it. diff --git a/charts/kasten/k10/7.5.401/charts/prometheus/charts/alertmanager/.helmignore b/charts/kasten/k10/7.5.401/charts/prometheus/charts/alertmanager/.helmignore new file mode 100644 index 0000000000..7653e97e66 --- /dev/null +++ b/charts/kasten/k10/7.5.401/charts/prometheus/charts/alertmanager/.helmignore @@ -0,0 +1,25 @@ +# 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/ + +unittests/ diff --git a/charts/kasten/k10/7.5.401/charts/prometheus/charts/alertmanager/Chart.yaml b/charts/kasten/k10/7.5.401/charts/prometheus/charts/alertmanager/Chart.yaml new file mode 100644 index 0000000000..df7c2a3986 --- /dev/null +++ b/charts/kasten/k10/7.5.401/charts/prometheus/charts/alertmanager/Chart.yaml @@ -0,0 +1,24 @@ +annotations: + artifacthub.io/license: Apache-2.0 + artifacthub.io/links: | + - name: Chart Source + url: https://github.com/prometheus-community/helm-charts +apiVersion: v2 +appVersion: v0.27.0 +description: The Alertmanager handles alerts sent by client applications such as the + Prometheus server. +home: https://prometheus.io/ +icon: https://raw.githubusercontent.com/prometheus/prometheus.github.io/master/assets/prometheus_logo-cb55bb5c346.png +keywords: +- monitoring +kubeVersion: '>=1.19.0-0' +maintainers: +- email: monotek23@gmail.com + name: monotek +- email: naseem@transit.app + name: naseemkullah +name: alertmanager +sources: +- https://github.com/prometheus/alertmanager +type: application +version: 1.13.1 diff --git a/charts/kasten/k10/7.5.401/charts/prometheus/charts/alertmanager/README.md b/charts/kasten/k10/7.5.401/charts/prometheus/charts/alertmanager/README.md new file mode 100644 index 0000000000..d3f4df73a2 --- /dev/null +++ b/charts/kasten/k10/7.5.401/charts/prometheus/charts/alertmanager/README.md @@ -0,0 +1,62 @@ +# Alertmanager + +As per [prometheus.io documentation](https://prometheus.io/docs/alerting/latest/alertmanager/): +> The Alertmanager handles alerts sent by client applications such as the +> Prometheus server. It takes care of deduplicating, grouping, and routing them +> to the correct receiver integration such as email, PagerDuty, or OpsGenie. It +> also takes care of silencing and inhibition of alerts. + +## Prerequisites + +Kubernetes 1.14+ + +## Get Repository Info + +```console +helm repo add prometheus-community https://prometheus-community.github.io/helm-charts +helm repo update +``` + +_See [`helm repo`](https://helm.sh/docs/helm/helm_repo/) for command documentation._ + +## Install Chart + +```console +helm install [RELEASE_NAME] prometheus-community/alertmanager +``` + +_See [configuration](#configuration) below._ + +_See [helm install](https://helm.sh/docs/helm/helm_install/) for command documentation._ + +## Uninstall Chart + +```console +helm uninstall [RELEASE_NAME] +``` + +This removes all the Kubernetes components associated with the chart and deletes the release. + +_See [helm uninstall](https://helm.sh/docs/helm/helm_uninstall/) for command documentation._ + +## Upgrading Chart + +```console +helm upgrade [RELEASE_NAME] [CHART] --install +``` + +_See [helm upgrade](https://helm.sh/docs/helm/helm_upgrade/) for command documentation._ + +### To 1.0 + +The [configmap-reload](https://github.com/jimmidyson/configmap-reload) container was replaced by the [prometheus-config-reloader](https://github.com/prometheus-operator/prometheus-operator/tree/main/cmd/prometheus-config-reloader). +Extra command-line arguments specified via configmapReload.prometheus.extraArgs are not compatible and will break with the new prometheus-config-reloader, refer to the [sources](https://github.com/prometheus-operator/prometheus-operator/blob/main/cmd/prometheus-config-reloader/main.go) in order to make the appropriate adjustment to the extea command-line arguments. +The `networking.k8s.io/v1beta1` is no longer supported. use [`networking.k8s.io/v1`](https://kubernetes.io/docs/reference/using-api/deprecation-guide/#ingressclass-v122). + +## Configuration + +See [Customizing the Chart Before Installing](https://helm.sh/docs/intro/using_helm/#customizing-the-chart-before-installing). To see all configurable options with detailed comments, visit the chart's [values.yaml](./values.yaml), or run these configuration commands: + +```console +helm show values prometheus-community/alertmanager +``` diff --git a/charts/kasten/k10/7.5.401/charts/prometheus/charts/alertmanager/ci/config-reload-values.yaml b/charts/kasten/k10/7.5.401/charts/prometheus/charts/alertmanager/ci/config-reload-values.yaml new file mode 100644 index 0000000000..cba5de8e29 --- /dev/null +++ b/charts/kasten/k10/7.5.401/charts/prometheus/charts/alertmanager/ci/config-reload-values.yaml @@ -0,0 +1,2 @@ +configmapReload: + enabled: true diff --git a/charts/kasten/k10/7.5.401/charts/prometheus/charts/alertmanager/templates/NOTES.txt b/charts/kasten/k10/7.5.401/charts/prometheus/charts/alertmanager/templates/NOTES.txt new file mode 100644 index 0000000000..46ea5bee59 --- /dev/null +++ b/charts/kasten/k10/7.5.401/charts/prometheus/charts/alertmanager/templates/NOTES.txt @@ -0,0 +1,21 @@ +1. Get the application URL by running these commands: +{{- if .Values.ingress.enabled }} +{{- range $host := .Values.ingress.hosts }} + {{- range .paths }} + http{{ if $.Values.ingress.tls }}s{{ end }}://{{ $host.host }}{{ .path }} + {{- end }} +{{- end }} +{{- else if contains "NodePort" .Values.service.type }} + export NODE_PORT=$(kubectl get --namespace {{ include "alertmanager.namespace" . }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ include "alertmanager.fullname" . }}) + export NODE_IP=$(kubectl get nodes --namespace {{ include "alertmanager.namespace" . }} -o jsonpath="{.items[0].status.addresses[0].address}") + echo http://$NODE_IP:$NODE_PORT +{{- else if contains "LoadBalancer" .Values.service.type }} + NOTE: It may take a few minutes for the LoadBalancer IP to be available. + You can watch the status of by running 'kubectl get --namespace {{ include "alertmanager.namespace" . }} svc -w {{ include "alertmanager.fullname" . }}' + export SERVICE_IP=$(kubectl get svc --namespace {{ include "alertmanager.namespace" . }} {{ include "alertmanager.fullname" . }} --template "{{"{{ range (index .status.loadBalancer.ingress 0) }}{{.}}{{ end }}"}}") + echo http://$SERVICE_IP:{{ .Values.service.port }} +{{- else if contains "ClusterIP" .Values.service.type }} + export POD_NAME=$(kubectl get pods --namespace {{ include "alertmanager.namespace" . }} -l "app.kubernetes.io/name={{ include "alertmanager.name" . }},app.kubernetes.io/instance={{ .Release.Name }}" -o jsonpath="{.items[0].metadata.name}") + echo "Visit http://127.0.0.1:{{ .Values.service.port }} to use your application" + kubectl --namespace {{ include "alertmanager.namespace" . }} port-forward $POD_NAME {{ .Values.service.port }}:80 +{{- end }} diff --git a/charts/kasten/k10/7.5.401/charts/prometheus/charts/alertmanager/templates/_helpers.tpl b/charts/kasten/k10/7.5.401/charts/prometheus/charts/alertmanager/templates/_helpers.tpl new file mode 100644 index 0000000000..827b6ee9f7 --- /dev/null +++ b/charts/kasten/k10/7.5.401/charts/prometheus/charts/alertmanager/templates/_helpers.tpl @@ -0,0 +1,92 @@ +{{/* vim: set filetype=mustache: */}} +{{/* +Expand the name of the chart. +*/}} +{{- define "alertmanager.name" -}} +{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Create a default fully qualified app name. +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +If release name contains chart name it will be used as a full name. +*/}} +{{- define "alertmanager.fullname" -}} +{{- if .Values.fullnameOverride }} +{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- $name := default .Chart.Name .Values.nameOverride }} +{{- if contains $name .Release.Name }} +{{- .Release.Name | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }} +{{- end }} +{{- end }} +{{- end }} + +{{/* +Create chart name and version as used by the chart label. +*/}} +{{- define "alertmanager.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Common labels +*/}} +{{- define "alertmanager.labels" -}} +helm.sh/chart: {{ include "alertmanager.chart" . }} +{{ include "alertmanager.selectorLabels" . }} +{{- with .Chart.AppVersion }} +app.kubernetes.io/version: {{ . | quote }} +{{- end }} +app.kubernetes.io/managed-by: {{ .Release.Service }} +{{- end }} + +{{/* +Selector labels +*/}} +{{- define "alertmanager.selectorLabels" -}} +app.kubernetes.io/name: {{ include "alertmanager.name" . }} +app.kubernetes.io/instance: {{ .Release.Name }} +{{- end }} + +{{/* +Create the name of the service account to use +*/}} +{{- define "alertmanager.serviceAccountName" -}} +{{- if .Values.serviceAccount.create }} +{{- default (include "alertmanager.fullname" .) .Values.serviceAccount.name }} +{{- else }} +{{- default "default" .Values.serviceAccount.name }} +{{- end }} +{{- end }} + +{{/* +Define Ingress apiVersion +*/}} +{{- define "alertmanager.ingress.apiVersion" -}} +{{- printf "networking.k8s.io/v1" }} +{{- end }} + +{{/* +Define Pdb apiVersion +*/}} +{{- define "alertmanager.pdb.apiVersion" -}} +{{- if $.Capabilities.APIVersions.Has "policy/v1/PodDisruptionBudget" }} +{{- printf "policy/v1" }} +{{- else }} +{{- printf "policy/v1beta1" }} +{{- end }} +{{- end }} + +{{/* +Allow overriding alertmanager namespace +*/}} +{{- define "alertmanager.namespace" -}} +{{- if .Values.namespaceOverride -}} +{{- .Values.namespaceOverride -}} +{{- else -}} +{{- .Release.Namespace -}} +{{- end -}} +{{- end -}} diff --git a/charts/kasten/k10/7.5.401/charts/prometheus/charts/alertmanager/templates/configmap.yaml b/charts/kasten/k10/7.5.401/charts/prometheus/charts/alertmanager/templates/configmap.yaml new file mode 100644 index 0000000000..9e5882dc86 --- /dev/null +++ b/charts/kasten/k10/7.5.401/charts/prometheus/charts/alertmanager/templates/configmap.yaml @@ -0,0 +1,21 @@ +{{- if .Values.config.enabled }} +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ include "alertmanager.fullname" . }} + labels: + {{- include "alertmanager.labels" . | nindent 4 }} + {{- with .Values.configAnnotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} + namespace: {{ include "alertmanager.namespace" . }} +data: + alertmanager.yml: | + {{- $config := omit .Values.config "enabled" }} + {{- toYaml $config | default "{}" | nindent 4 }} + {{- range $key, $value := .Values.templates }} + {{ $key }}: |- + {{- $value | nindent 4 }} + {{- end }} +{{- end }} diff --git a/charts/kasten/k10/7.5.401/charts/prometheus/charts/alertmanager/templates/ingress.yaml b/charts/kasten/k10/7.5.401/charts/prometheus/charts/alertmanager/templates/ingress.yaml new file mode 100644 index 0000000000..e729a8ad3b --- /dev/null +++ b/charts/kasten/k10/7.5.401/charts/prometheus/charts/alertmanager/templates/ingress.yaml @@ -0,0 +1,44 @@ +{{- if .Values.ingress.enabled }} +{{- $fullName := include "alertmanager.fullname" . }} +{{- $svcPort := .Values.service.port }} +apiVersion: {{ include "alertmanager.ingress.apiVersion" . }} +kind: Ingress +metadata: + name: {{ $fullName }} + labels: + {{- include "alertmanager.labels" . | nindent 4 }} + {{- with .Values.ingress.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} + namespace: {{ include "alertmanager.namespace" . }} +spec: + {{- if .Values.ingress.className }} + ingressClassName: {{ .Values.ingress.className }} + {{- end }} + {{- if .Values.ingress.tls }} + tls: + {{- range .Values.ingress.tls }} + - hosts: + {{- range .hosts }} + - {{ . | quote }} + {{- end }} + secretName: {{ .secretName }} + {{- end }} + {{- end }} + rules: + {{- range .Values.ingress.hosts }} + - host: {{ .host | quote }} + http: + paths: + {{- range .paths }} + - path: {{ .path }} + pathType: {{ .pathType }} + backend: + service: + name: {{ $fullName }} + port: + number: {{ $svcPort }} + {{- end }} + {{- end }} +{{- end }} diff --git a/charts/kasten/k10/7.5.401/charts/prometheus/charts/alertmanager/templates/ingressperreplica.yaml b/charts/kasten/k10/7.5.401/charts/prometheus/charts/alertmanager/templates/ingressperreplica.yaml new file mode 100644 index 0000000000..6f5a023500 --- /dev/null +++ b/charts/kasten/k10/7.5.401/charts/prometheus/charts/alertmanager/templates/ingressperreplica.yaml @@ -0,0 +1,56 @@ +{{- if and .Values.servicePerReplica.enabled .Values.ingressPerReplica.enabled }} +{{- $pathType := .Values.ingressPerReplica.pathType }} +{{- $count := .Values.replicaCount | int -}} +{{- $servicePort := .Values.service.port -}} +{{- $ingressValues := .Values.ingressPerReplica -}} +{{- $fullName := include "alertmanager.fullname" . }} +apiVersion: v1 +kind: List +metadata: + name: {{ $fullName }}-ingressperreplica + namespace: {{ include "alertmanager.namespace" . }} +items: +{{- range $i, $e := until $count }} + - kind: Ingress + apiVersion: {{ include "alertmanager.ingress.apiVersion" $ }} + metadata: + name: {{ $fullName }}-{{ $i }} + namespace: {{ include "alertmanager.namespace" $ }} + labels: + {{- include "alertmanager.labels" $ | nindent 8 }} + {{- if $ingressValues.labels }} +{{ toYaml $ingressValues.labels | indent 8 }} + {{- end }} + {{- if $ingressValues.annotations }} + annotations: +{{ toYaml $ingressValues.annotations | indent 8 }} + {{- end }} + spec: + {{- if $ingressValues.className }} + ingressClassName: {{ $ingressValues.className }} + {{- end }} + rules: + - host: {{ $ingressValues.hostPrefix }}-{{ $i }}.{{ $ingressValues.hostDomain }} + http: + paths: + {{- range $p := $ingressValues.paths }} + - path: {{ tpl $p $ }} + pathType: {{ $pathType }} + backend: + service: + name: {{ $fullName }}-{{ $i }} + port: + name: http + {{- end -}} + {{- if or $ingressValues.tlsSecretName $ingressValues.tlsSecretPerReplica.enabled }} + tls: + - hosts: + - {{ $ingressValues.hostPrefix }}-{{ $i }}.{{ $ingressValues.hostDomain }} + {{- if $ingressValues.tlsSecretPerReplica.enabled }} + secretName: {{ $ingressValues.tlsSecretPerReplica.prefix }}-{{ $i }} + {{- else }} + secretName: {{ $ingressValues.tlsSecretName }} + {{- end }} + {{- end }} +{{- end -}} +{{- end -}} diff --git a/charts/kasten/k10/7.5.401/charts/prometheus/charts/alertmanager/templates/pdb.yaml b/charts/kasten/k10/7.5.401/charts/prometheus/charts/alertmanager/templates/pdb.yaml new file mode 100644 index 0000000000..103e9ecde1 --- /dev/null +++ b/charts/kasten/k10/7.5.401/charts/prometheus/charts/alertmanager/templates/pdb.yaml @@ -0,0 +1,14 @@ +{{- if .Values.podDisruptionBudget }} +apiVersion: {{ include "alertmanager.pdb.apiVersion" . }} +kind: PodDisruptionBudget +metadata: + name: {{ include "alertmanager.fullname" . }} + labels: + {{- include "alertmanager.labels" . | nindent 4 }} + namespace: {{ include "alertmanager.namespace" . }} +spec: + selector: + matchLabels: + {{- include "alertmanager.selectorLabels" . | nindent 6 }} + {{- toYaml .Values.podDisruptionBudget | nindent 2 }} +{{- end }} diff --git a/charts/kasten/k10/7.5.401/charts/prometheus/charts/alertmanager/templates/serviceaccount.yaml b/charts/kasten/k10/7.5.401/charts/prometheus/charts/alertmanager/templates/serviceaccount.yaml new file mode 100644 index 0000000000..bc9ccaaff9 --- /dev/null +++ b/charts/kasten/k10/7.5.401/charts/prometheus/charts/alertmanager/templates/serviceaccount.yaml @@ -0,0 +1,14 @@ +{{- if .Values.serviceAccount.create }} +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ include "alertmanager.serviceAccountName" . }} + labels: + {{- include "alertmanager.labels" . | nindent 4 }} + {{- with .Values.serviceAccount.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} + namespace: {{ include "alertmanager.namespace" . }} +automountServiceAccountToken: {{ .Values.automountServiceAccountToken }} +{{- end }} diff --git a/charts/kasten/k10/7.5.401/charts/prometheus/charts/alertmanager/templates/serviceperreplica.yaml b/charts/kasten/k10/7.5.401/charts/prometheus/charts/alertmanager/templates/serviceperreplica.yaml new file mode 100644 index 0000000000..faa75b3ba2 --- /dev/null +++ b/charts/kasten/k10/7.5.401/charts/prometheus/charts/alertmanager/templates/serviceperreplica.yaml @@ -0,0 +1,44 @@ +{{- if and .Values.servicePerReplica.enabled }} +{{- $count := .Values.replicaCount | int -}} +{{- $serviceValues := .Values.servicePerReplica -}} +apiVersion: v1 +kind: List +metadata: + name: {{ include "alertmanager.fullname" . }}-serviceperreplica + namespace: {{ include "alertmanager.namespace" . }} +items: +{{- range $i, $e := until $count }} + - apiVersion: v1 + kind: Service + metadata: + name: {{ include "alertmanager.fullname" $ }}-{{ $i }} + namespace: {{ include "alertmanager.namespace" $ }} + labels: + {{- include "alertmanager.labels" $ | nindent 8 }} + {{- if $serviceValues.annotations }} + annotations: +{{ toYaml $serviceValues.annotations | indent 8 }} + {{- end }} + spec: + {{- if $serviceValues.clusterIP }} + clusterIP: {{ $serviceValues.clusterIP }} + {{- end }} + {{- if $serviceValues.loadBalancerSourceRanges }} + loadBalancerSourceRanges: + {{- range $cidr := $serviceValues.loadBalancerSourceRanges }} + - {{ $cidr }} + {{- end }} + {{- end }} + {{- if ne $serviceValues.type "ClusterIP" }} + externalTrafficPolicy: {{ $serviceValues.externalTrafficPolicy }} + {{- end }} + ports: + - name: http + port: {{ $.Values.service.port }} + targetPort: http + selector: + {{- include "alertmanager.selectorLabels" $ | nindent 8 }} + statefulset.kubernetes.io/pod-name: {{ include "alertmanager.fullname" $ }}-{{ $i }} + type: "{{ $serviceValues.type }}" +{{- end }} +{{- end }} diff --git a/charts/kasten/k10/7.5.401/charts/prometheus/charts/alertmanager/templates/services.yaml b/charts/kasten/k10/7.5.401/charts/prometheus/charts/alertmanager/templates/services.yaml new file mode 100644 index 0000000000..eefb9ce161 --- /dev/null +++ b/charts/kasten/k10/7.5.401/charts/prometheus/charts/alertmanager/templates/services.yaml @@ -0,0 +1,75 @@ +apiVersion: v1 +kind: Service +metadata: + name: {{ include "alertmanager.fullname" . }} + labels: + {{- include "alertmanager.labels" . | nindent 4 }} + {{- with .Values.service.labels }} + {{- toYaml . | nindent 4 }} + {{- end }} + {{- with .Values.service.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} + namespace: {{ include "alertmanager.namespace" . }} +spec: + {{- if .Values.service.ipDualStack.enabled }} + ipFamilies: {{ toYaml .Values.service.ipDualStack.ipFamilies | nindent 4 }} + ipFamilyPolicy: {{ .Values.service.ipDualStack.ipFamilyPolicy }} + {{- end }} + type: {{ .Values.service.type }} + {{- with .Values.service.loadBalancerIP }} + loadBalancerIP: {{ . }} + {{- end }} + {{- with .Values.service.loadBalancerSourceRanges }} + loadBalancerSourceRanges: + {{- range $cidr := . }} + - {{ $cidr }} + {{- end }} + {{- end }} + ports: + - port: {{ .Values.service.port }} + targetPort: http + protocol: TCP + name: http + {{- if (and (eq .Values.service.type "NodePort") .Values.service.nodePort) }} + nodePort: {{ .Values.service.nodePort }} + {{- end }} + {{- with .Values.service.extraPorts }} + {{- toYaml . | nindent 4 }} + {{- end }} + selector: + {{- include "alertmanager.selectorLabels" . | nindent 4 }} +--- +apiVersion: v1 +kind: Service +metadata: + name: {{ include "alertmanager.fullname" . }}-headless + labels: + {{- include "alertmanager.labels" . | nindent 4 }} + {{- with .Values.service.labels }} + {{- toYaml . | nindent 4 }} + {{- end }} + namespace: {{ include "alertmanager.namespace" . }} +spec: + clusterIP: None + ports: + - port: {{ .Values.service.port }} + targetPort: http + protocol: TCP + name: http + {{- if or (gt (int .Values.replicaCount) 1) (.Values.additionalPeers) }} + - port: {{ .Values.service.clusterPort }} + targetPort: clusterpeer-tcp + protocol: TCP + name: cluster-tcp + - port: {{ .Values.service.clusterPort }} + targetPort: clusterpeer-udp + protocol: UDP + name: cluster-udp + {{- end }} + {{- with .Values.service.extraPorts }} + {{- toYaml . | nindent 4 }} + {{- end }} + selector: + {{- include "alertmanager.selectorLabels" . | nindent 4 }} diff --git a/charts/kasten/k10/7.5.401/charts/prometheus/charts/alertmanager/templates/statefulset.yaml b/charts/kasten/k10/7.5.401/charts/prometheus/charts/alertmanager/templates/statefulset.yaml new file mode 100644 index 0000000000..6d973543ce --- /dev/null +++ b/charts/kasten/k10/7.5.401/charts/prometheus/charts/alertmanager/templates/statefulset.yaml @@ -0,0 +1,256 @@ +{{- $svcClusterPort := .Values.service.clusterPort }} +apiVersion: apps/v1 +kind: StatefulSet +metadata: + name: {{ include "alertmanager.fullname" . }} + labels: + {{- include "alertmanager.labels" . | nindent 4 }} + {{- with .Values.statefulSet.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} + namespace: {{ include "alertmanager.namespace" . }} +spec: + replicas: {{ .Values.replicaCount }} + minReadySeconds: {{ .Values.minReadySeconds }} + revisionHistoryLimit: {{ .Values.revisionHistoryLimit }} + selector: + matchLabels: + {{- include "alertmanager.selectorLabels" . | nindent 6 }} + serviceName: {{ include "alertmanager.fullname" . }}-headless + template: + metadata: + labels: + {{- include "alertmanager.selectorLabels" . | nindent 8 }} + {{- with .Values.podLabels }} + {{- toYaml . | nindent 8 }} + {{- end }} + annotations: + {{- if not .Values.configmapReload.enabled }} + checksum/config: {{ include (print $.Template.BasePath "/configmap.yaml") . | sha256sum }} + {{- end }} + {{- with .Values.podAnnotations }} + {{- toYaml . | nindent 8 }} + {{- end }} + spec: + automountServiceAccountToken: {{ .Values.automountServiceAccountToken }} + {{- with .Values.imagePullSecrets }} + imagePullSecrets: + {{- toYaml . | nindent 8 }} + {{- end }} + serviceAccountName: {{ include "alertmanager.serviceAccountName" . }} + {{- with .Values.dnsConfig }} + dnsConfig: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.hostAliases }} + hostAliases: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.nodeSelector }} + nodeSelector: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.schedulerName }} + schedulerName: {{ . }} + {{- end }} + {{- if or .Values.podAntiAffinity .Values.affinity }} + affinity: + {{- end }} + {{- with .Values.affinity }} + {{- toYaml . | nindent 8 }} + {{- end }} + {{- if eq .Values.podAntiAffinity "hard" }} + podAntiAffinity: + requiredDuringSchedulingIgnoredDuringExecution: + - topologyKey: {{ .Values.podAntiAffinityTopologyKey }} + labelSelector: + matchExpressions: + - {key: app.kubernetes.io/name, operator: In, values: [{{ include "alertmanager.name" . }}]} + {{- else if eq .Values.podAntiAffinity "soft" }} + podAntiAffinity: + preferredDuringSchedulingIgnoredDuringExecution: + - weight: 100 + podAffinityTerm: + topologyKey: {{ .Values.podAntiAffinityTopologyKey }} + labelSelector: + matchExpressions: + - {key: app.kubernetes.io/name, operator: In, values: [{{ include "alertmanager.name" . }}]} + {{- end }} + {{- with .Values.priorityClassName }} + priorityClassName: {{ . }} + {{- end }} + {{- with .Values.topologySpreadConstraints }} + topologySpreadConstraints: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.tolerations }} + tolerations: + {{- toYaml . | nindent 8 }} + {{- end }} + securityContext: + {{- toYaml .Values.podSecurityContext | nindent 8 }} + {{- with .Values.extraInitContainers }} + initContainers: + {{- toYaml . | nindent 8 }} + {{- end }} + containers: + {{- if .Values.configmapReload.enabled }} + - name: {{ .Chart.Name }}-{{ .Values.configmapReload.name }} + image: "{{ .Values.configmapReload.image.repository }}:{{ .Values.configmapReload.image.tag }}" + imagePullPolicy: "{{ .Values.configmapReload.image.pullPolicy }}" + {{- with .Values.configmapReload.extraEnv }} + env: + {{- toYaml . | nindent 12 }} + {{- end }} + args: + {{- if and (hasKey .Values.configmapReload.extraArgs "config-file" | not) (hasKey .Values.configmapReload.extraArgs "watched-dir" | not) }} + - --watched-dir=/etc/alertmanager + {{- end }} + {{- if not (hasKey .Values.configmapReload.extraArgs "reload-url") }} + - --reload-url=http://127.0.0.1:9093/-/reload + {{- end }} + {{- range $key, $value := .Values.configmapReload.extraArgs }} + - --{{ $key }}={{ $value }} + {{- end }} + resources: + {{- toYaml .Values.configmapReload.resources | nindent 12 }} + {{- with .Values.configmapReload.containerPort }} + ports: + - containerPort: {{ . }} + {{- end }} + {{- with .Values.configmapReload.securityContext }} + securityContext: + {{- toYaml . | nindent 12 }} + {{- end }} + volumeMounts: + {{- if .Values.config.enabled }} + - name: config + mountPath: /etc/alertmanager + {{- end }} + {{- if .Values.configmapReload.extraVolumeMounts }} + {{- toYaml .Values.configmapReload.extraVolumeMounts | nindent 12 }} + {{- end }} + {{- end }} + - name: {{ .Chart.Name }} + securityContext: + {{- toYaml .Values.securityContext | nindent 12 }} + image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}" + imagePullPolicy: {{ .Values.image.pullPolicy }} + env: + - name: POD_IP + valueFrom: + fieldRef: + apiVersion: v1 + fieldPath: status.podIP + {{- if .Values.extraEnv }} + {{- toYaml .Values.extraEnv | nindent 12 }} + {{- end }} + {{- with .Values.command }} + command: + {{- toYaml . | nindent 12 }} + {{- end }} + args: + - --storage.path=/alertmanager + {{- if not (hasKey .Values.extraArgs "config.file") }} + - --config.file=/etc/alertmanager/alertmanager.yml + {{- end }} + {{- if or (gt (int .Values.replicaCount) 1) (.Values.additionalPeers) }} + - --cluster.advertise-address=[$(POD_IP)]:{{ $svcClusterPort }} + - --cluster.listen-address=0.0.0.0:{{ $svcClusterPort }} + {{- end }} + {{- if gt (int .Values.replicaCount) 1}} + {{- $fullName := include "alertmanager.fullname" . }} + {{- range $i := until (int .Values.replicaCount) }} + - --cluster.peer={{ $fullName }}-{{ $i }}.{{ $fullName }}-headless:{{ $svcClusterPort }} + {{- end }} + {{- end }} + {{- if .Values.additionalPeers }} + {{- range $item := .Values.additionalPeers }} + - --cluster.peer={{ $item }} + {{- end }} + {{- end }} + {{- range $key, $value := .Values.extraArgs }} + - --{{ $key }}={{ $value }} + {{- end }} + {{- if .Values.baseURL }} + - --web.external-url={{ .Values.baseURL }} + {{- end }} + ports: + - name: http + containerPort: 9093 + protocol: TCP + {{- if or (gt (int .Values.replicaCount) 1) (.Values.additionalPeers) }} + - name: clusterpeer-tcp + containerPort: {{ $svcClusterPort }} + protocol: TCP + - name: clusterpeer-udp + containerPort: {{ $svcClusterPort }} + protocol: UDP + {{- end }} + livenessProbe: + {{- toYaml .Values.livenessProbe | nindent 12 }} + readinessProbe: + {{- toYaml .Values.readinessProbe | nindent 12 }} + resources: + {{- toYaml .Values.resources | nindent 12 }} + volumeMounts: + {{- if .Values.config.enabled }} + - name: config + mountPath: /etc/alertmanager + {{- end }} + {{- range .Values.extraSecretMounts }} + - name: {{ .name }} + mountPath: {{ .mountPath }} + subPath: {{ .subPath }} + readOnly: {{ .readOnly }} + {{- end }} + - name: storage + mountPath: /alertmanager + {{- if .Values.extraVolumeMounts }} + {{- toYaml .Values.extraVolumeMounts | nindent 12 }} + {{- end }} + {{- with .Values.extraContainers }} + {{- toYaml . | nindent 8 }} + {{- end }} + volumes: + {{- if .Values.config.enabled }} + - name: config + configMap: + name: {{ include "alertmanager.fullname" . }} + {{- end }} + {{- range .Values.extraSecretMounts }} + - name: {{ .name }} + secret: + secretName: {{ .secretName }} + {{- with .optional }} + optional: {{ . }} + {{- end }} + {{- end }} + {{- if .Values.extraVolumes }} + {{- toYaml .Values.extraVolumes | nindent 8 }} + {{- end }} + {{- if .Values.extraPodConfigs }} + {{- toYaml .Values.extraPodConfigs | nindent 6 }} + {{- end }} + {{- if .Values.persistence.enabled }} + volumeClaimTemplates: + - metadata: + name: storage + spec: + accessModes: + {{- toYaml .Values.persistence.accessModes | nindent 10 }} + resources: + requests: + storage: {{ .Values.persistence.size }} + {{- if .Values.persistence.storageClass }} + {{- if (eq "-" .Values.persistence.storageClass) }} + storageClassName: "" + {{- else }} + storageClassName: {{ .Values.persistence.storageClass }} + {{- end }} + {{- end }} + {{- else }} + - name: storage + emptyDir: {} + {{- end }} diff --git a/charts/kasten/k10/7.5.401/charts/prometheus/charts/alertmanager/templates/tests/test-connection.yaml b/charts/kasten/k10/7.5.401/charts/prometheus/charts/alertmanager/templates/tests/test-connection.yaml new file mode 100644 index 0000000000..410eba5bd8 --- /dev/null +++ b/charts/kasten/k10/7.5.401/charts/prometheus/charts/alertmanager/templates/tests/test-connection.yaml @@ -0,0 +1,20 @@ +{{- if .Values.testFramework.enabled }} +apiVersion: v1 +kind: Pod +metadata: + name: "{{ include "alertmanager.fullname" . }}-test-connection" + labels: + {{- include "alertmanager.labels" . | nindent 4 }} + {{- with .Values.testFramework.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} + namespace: {{ include "alertmanager.namespace" . }} +spec: + containers: + - name: wget + image: busybox + command: ['wget'] + args: ['{{ include "alertmanager.fullname" . }}:{{ .Values.service.port }}'] + restartPolicy: Never +{{- end }} diff --git a/charts/kasten/k10/7.5.401/charts/prometheus/charts/alertmanager/templates/vpa.yaml b/charts/kasten/k10/7.5.401/charts/prometheus/charts/alertmanager/templates/vpa.yaml new file mode 100644 index 0000000000..53f70a28e3 --- /dev/null +++ b/charts/kasten/k10/7.5.401/charts/prometheus/charts/alertmanager/templates/vpa.yaml @@ -0,0 +1,26 @@ +{{- if .Values.verticalPodAutoscaler.enabled }} +apiVersion: autoscaling.k8s.io/v1 +kind: VerticalPodAutoscaler +metadata: + name: {{ include "alertmanager.fullname" . }} + namespace: {{ .Release.Namespace }} +spec: + {{- if .Values.verticalPodAutoscaler.recommenders }} + recommenders: + {{- range .Values.verticalPodAutoscaler.recommenders }} + - name: {{ .name }} + {{- end }} + {{- end }} + targetRef: + apiVersion: apps/v1 + kind: StatefulSet + name: {{ include "alertmanager.fullname" . }} + {{- if .Values.verticalPodAutoscaler.updatePolicy }} + updatePolicy: + {{- toYaml .Values.verticalPodAutoscaler.updatePolicy | nindent 4 }} + {{- end }} + {{- if .Values.verticalPodAutoscaler.resourcePolicy }} + resourcePolicy: + {{- toYaml .Values.verticalPodAutoscaler.resourcePolicy | nindent 4 }} + {{- end }} +{{- end }} diff --git a/charts/kasten/k10/7.5.401/charts/prometheus/charts/alertmanager/values.schema.json b/charts/kasten/k10/7.5.401/charts/prometheus/charts/alertmanager/values.schema.json new file mode 100644 index 0000000000..d3be015cfd --- /dev/null +++ b/charts/kasten/k10/7.5.401/charts/prometheus/charts/alertmanager/values.schema.json @@ -0,0 +1,946 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema", + "title": "alertmanager", + "description": "The Alertmanager handles alerts sent by client applications such as the Prometheus server.", + "type": "object", + "required": [ + "replicaCount", + "image", + "serviceAccount", + "service", + "persistence", + "config" + ], + "definitions": { + "image": { + "description": "Container image parameters.", + "type": "object", + "required": ["repository"], + "additionalProperties": false, + "properties": { + "repository": { + "description": "Image repository. Path to the image with registry(quay.io) or without(prometheus/alertmanager) for docker.io.", + "type": "string" + }, + "pullPolicy": { + "description": "Image pull policy. Defaults to Always if :latest tag is specified, or IfNotPresent otherwise. Cannot be updated.", + "type": "string", + "enum": [ + "Never", + "IfNotPresent", + "Always" + ], + "default": "IfNotPresent" + }, + "tag": { + "description": "Use chart appVersion by default.", + "type": "string", + "default": "" + } + } + }, + "resources": { + "description": "Resource limits and requests for the Container.", + "type": "object", + "properties": { + "limits": { + "description": "Resource limits for the Container.", + "type": "object", + "properties": { + "cpu": { + "description": "CPU request for the Container.", + "type": "string" + }, + "memory": { + "description": "Memory request for the Container.", + "type": "string" + } + } + }, + "requests": { + "description": "Resource requests for the Container.", + "type": "object", + "properties": { + "cpu": { + "description": "CPU request for the Container.", + "type": "string" + }, + "memory": { + "description": "Memory request for the Container.", + "type": "string" + } + } + } + } + }, + "securityContext": { + "description": "Security context for the container.", + "type": "object", + "properties": { + "capabilities": { + "description": "Specifies the capabilities to be dropped by the container.", + "type": "object", + "properties": { + "drop": { + "description": "List of capabilities to be dropped.", + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "readOnlyRootFilesystem": { + "description": "Specifies whether the root file system should be mounted as read-only.", + "type": "boolean" + }, + "runAsUser": { + "description": "Specifies the UID (User ID) to run the container as.", + "type": "integer" + }, + "runAsNonRoot": { + "description": "Specifies whether to run the container as a non-root user.", + "type": "boolean" + }, + "runAsGroup": { + "description": "Specifies the GID (Group ID) to run the container as.", + "type": "integer" + } + } + }, + "volumeMounts": { + "description": "List of volume mounts for the Container.", + "type": "array", + "items": { + "description": "Volume mounts for the Container.", + "type": "object", + "required": ["name", "mountPath"], + "properties": { + "name": { + "description": "The name of the volume to mount.", + "type": "string" + }, + "mountPath": { + "description": "The mount path for the volume.", + "type": "string" + }, + "readOnly": { + "description": "Specifies if the volume should be mounted in read-only mode.", + "type": "boolean" + } + } + } + }, + "env": { + "description": "List of environment variables for the Container.", + "type": "array", + "items": { + "description": "Environment variables for the Container.", + "type": "object", + "required": ["name"], + "properties": { + "name": { + "description": "The name of the environment variable.", + "type": "string" + }, + "value": { + "description": "The value of the environment variable.", + "type": "string" + } + } + } + }, + "config": { + "description": "https://prometheus.io/docs/alerting/latest/configuration/", + "duration": { + "type": "string", + "pattern": "^((([0-9]+)y)?(([0-9]+)w)?(([0-9]+)d)?(([0-9]+)h)?(([0-9]+)m)?(([0-9]+)s)?(([0-9]+)ms)?|0)$" + }, + "labelname": { + "type": "string", + "pattern": "^[a-zA-Z_][a-zA-Z0-9_]*$|^...$" + }, + "route": { + "description": "Alert routing configuration.", + "type": "object", + "properties": { + "receiver": { + "description": "The default receiver to send alerts to.", + "type": "string" + }, + "group_by": { + "description": "The labels by which incoming alerts are grouped together.", + "type": "array", + "items": { + "type": "string", + "$ref": "#/definitions/config/labelname" + } + }, + "continue": { + "description": "Whether an alert should continue matching subsequent sibling nodes.", + "type": "boolean", + "default": false + }, + "matchers": { + "description": "A list of matchers that an alert has to fulfill to match the node.", + "type": "array", + "items": { + "type": "string" + } + }, + "group_wait": { + "description": "How long to initially wait to send a notification for a group of alerts.", + "$ref": "#/definitions/config/duration" + }, + "group_interval": { + "description": "How long to wait before sending a notification about new alerts that are added to a group of alerts for which an initial notification has already been sent.", + "$ref": "#/definitions/config/duration" + }, + "repeat_interval": { + "description": "How long to wait before sending a notification again if it has already been sent successfully for an alert.", + "$ref": "#/definitions/config/duration" + }, + "mute_time_intervals": { + "description": "Times when the route should be muted.", + "type": "array", + "items": { + "type": "string" + } + }, + "active_time_intervals": { + "description": "Times when the route should be active.", + "type": "array", + "items": { + "type": "string" + } + }, + "routes": { + "description": "Zero or more child routes.", + "type": "array", + "items": { + "type": "object", + "$ref": "#/definitions/config/route" + } + } + } + } + } + }, + "properties": { + "replicaCount": { + "description": "Number of desired pods.", + "type": "integer", + "default": 1, + "minimum": 0 + }, + "image": { + "description": "Container image parameters.", + "$ref": "#/definitions/image" + }, + "baseURL": { + "description": "External URL where alertmanager is reachable.", + "type": "string", + "default": "", + "examples": [ + "https://alertmanager.example.com" + ] + }, + "extraArgs": { + "description": "Additional alertmanager container arguments. Use args without '--', only 'key: value' syntax.", + "type": "object", + "default": {} + }, + "extraSecretMounts": { + "description": "Additional Alertmanager Secret mounts.", + "type": "array", + "default": [], + "items": { + "type": "object", + "required": ["name", "mountPath", "secretName"], + "properties": { + "name": { + "type": "string" + }, + "mountPath": { + "type": "string" + }, + "subPath": { + "type": "string", + "default": "" + }, + "secretName": { + "type": "string" + }, + "readOnly": { + "type": "boolean", + "default": false + } + } + } + }, + "imagePullSecrets": { + "description": "The property allows you to configure multiple image pull secrets.", + "type": "array", + "default": [], + "items": { + "type": "object", + "required": ["name"], + "properties": { + "name": { + "description": "Specifies the Secret name of the image pull secret.", + "type": "string" + } + } + } + }, + "nameOverride": { + "description": "Override value for the name of the Helm chart.", + "type": "string", + "default": "" + }, + "fullnameOverride": { + "description": "Override value for the fully qualified app name.", + "type": "string", + "default": "" + }, + "namespaceOverride": { + "description": "Override deployment namespace.", + "type": "string", + "default": "" + }, + "automountServiceAccountToken": { + "description": "Specifies whether to automatically mount the ServiceAccount token into the Pod's filesystem.", + "type": "boolean", + "default": true + }, + "serviceAccount": { + "description": "Contains properties related to the service account configuration.", + "type": "object", + "required": ["create"], + "properties": { + "create": { + "description": "Specifies whether a service account should be created.", + "type": "boolean", + "default": true + }, + "annotations": { + "description": "Annotations to add to the service account.", + "type": "object", + "default": {} + }, + "name": { + "description": "The name of the service account to use. If not set and create is true, a name is generated using the fullname template.", + "type": "string", + "default": "" + } + } + }, + "schedulerName": { + "description": "Sets the schedulerName in the alertmanager pod.", + "type": "string", + "default": "" + }, + "priorityClassName": { + "description": "Sets the priorityClassName in the alertmanager pod.", + "type": "string", + "default": "" + }, + "podSecurityContext": { + "description": "Pod security context configuration.", + "type": "object", + "properties": { + "fsGroup": { + "description": "The fsGroup value for the pod's security context.", + "type": "integer", + "default": 65534 + }, + "runAsUser": { + "description": "The UID to run the pod's containers as.", + "type": "integer" + }, + "runAsGroup": { + "description": "The GID to run the pod's containers as.", + "type": "integer" + } + } + }, + "dnsConfig": { + "description": "DNS configuration for the pod.", + "type": "object", + "properties": { + "nameservers": { + "description": "List of DNS server IP addresses.", + "type": "array", + "items": { + "type": "string" + } + }, + "searches": { + "description": "List of DNS search domains.", + "type": "array", + "items": { + "type": "string" + } + }, + "options": { + "description": "List of DNS options.", + "type": "array", + "items": { + "description": "DNS options.", + "type": "object", + "required": ["name"], + "properties": { + "name": { + "description": "The name of the DNS option.", + "type": "string" + }, + "value": { + "description": "The value of the DNS option.", + "type": "string" + } + } + } + } + } + }, + "hostAliases": { + "description": "List of host aliases.", + "type": "array", + "items": { + "description": "Host aliases configuration.", + "type": "object", + "required": ["ip", "hostnames"], + "properties": { + "ip": { + "description": "IP address associated with the host alias.", + "type": "string" + }, + "hostnames": { + "description": "List of hostnames associated with the IP address.", + "type": "array", + "items": { + "type": "string" + } + } + } + } + }, + "securityContext": { + "description": "Security context for the container.", + "$ref": "#/definitions/securityContext" + }, + "additionalPeers": { + "description": "Additional peers for a alertmanager.", + "type": "array", + "items": { + "type": "string" + } + }, + "extraInitContainers": { + "description": "Additional InitContainers to initialize the pod.", + "type": "array", + "default": [], + "items": { + "required": ["name", "image"], + "properties": { + "name": { + "description": "The name of the InitContainer.", + "type": "string" + }, + "image": { + "description": "The container image to use for the InitContainer.", + "type": "string" + }, + "pullPolicy": { + "description": "Image pull policy. Defaults to Always if :latest tag is specified, or IfNotPresent otherwise. Cannot be updated.", + "type": "string", + "enum": [ + "Never", + "IfNotPresent", + "Always" + ], + "default": "IfNotPresent" + }, + "command": { + "description": "The command to run in the InitContainer.", + "type": "array", + "items": { + "type": "string" + } + }, + "args": { + "description": "Additional command arguments for the InitContainer.", + "type": "array", + "items": { + "type": "string" + } + }, + "ports": { + "description": "List of ports to expose from the container.", + "type": "array", + "items": { + "type": "object" + } + }, + "env": { + "description": "List of environment variables for the InitContainer.", + "$ref": "#/definitions/env" + }, + "envFrom": { + "description": "List of sources to populate environment variables in the container.", + "type": "array", + "items": { + "type": "object" + } + }, + "volumeMounts": { + "description": "List of volume mounts for the InitContainer.", + "$ref": "#/definitions/volumeMounts" + }, + "resources": { + "description": "Resource requirements for the InitContainer.", + "$ref": "#/definitions/resources" + }, + "securityContext": { + "$ref": "#/definitions/securityContext", + "description": "The security context for the InitContainer." + } + } + } + }, + "extraContainers": { + "description": "Additional containers to add to the stateful set.", + "type": "array", + "default": [], + "items": { + "required": ["name", "image"], + "properties": { + "name": { + "description": "The name of the InitContainer.", + "type": "string" + }, + "image": { + "description": "The container image to use for the InitContainer.", + "type": "string" + }, + "pullPolicy": { + "description": "Image pull policy. Defaults to Always if :latest tag is specified, or IfNotPresent otherwise. Cannot be updated.", + "type": "string", + "enum": [ + "Never", + "IfNotPresent", + "Always" + ], + "default": "IfNotPresent" + }, + "command": { + "description": "The command to run in the InitContainer.", + "type": "array", + "items": { + "type": "string" + } + }, + "args": { + "description": "Additional command arguments for the InitContainer.", + "type": "array", + "items": { + "type": "string" + } + }, + "ports": { + "description": "List of ports to expose from the container.", + "type": "array", + "items": { + "type": "object" + } + }, + "env": { + "description": "List of environment variables for the InitContainer.", + "$ref": "#/definitions/env" + }, + "envFrom": { + "description": "List of sources to populate environment variables in the container.", + "type": "array", + "items": { + "type": "object" + } + }, + "volumeMounts": { + "description": "List of volume mounts for the InitContainer.", + "$ref": "#/definitions/volumeMounts" + }, + "resources": { + "description": "Resource requirements for the InitContainer.", + "$ref": "#/definitions/resources" + }, + "securityContext": { + "$ref": "#/definitions/securityContext", + "description": "The security context for the InitContainer." + } + } + } + }, + "resources": { + "description": "Resource limits and requests for the pod.", + "$ref": "#/definitions/resources" + }, + "livenessProbe": { + "description": "Liveness probe configuration.", + "type": "object" + }, + "readinessProbe": { + "description": "Readiness probe configuration.", + "type": "object" + }, + "service": { + "description": "Service configuration.", + "type": "object", + "required": ["type", "port"], + "properties": { + "annotations": { + "description": "Annotations to add to the service.", + "type": "object" + }, + "type": { + "description": "Service type.", + "type": "string" + }, + "port": { + "description": "Port number for the service.", + "type": "integer" + }, + "clusterPort": { + "description": "Port number for the cluster.", + "type": "integer" + }, + "loadBalancerIP": { + "description": "External IP to assign when the service type is LoadBalancer.", + "type": "string" + }, + "loadBalancerSourceRanges": { + "description": "IP ranges to allow access to the loadBalancerIP.", + "type": "array", + "items": { + "type": "string" + } + }, + "nodePort": { + "description": "Specific nodePort to force when service type is NodePort.", + "type": "integer" + } + } + }, + "ingress": { + "description": "Ingress configuration.", + "type": "object", + "properties": { + "enabled": { + "description": "Indicates if Ingress is enabled.", + "type": "boolean" + }, + "className": { + "description": "Ingress class name.", + "type": "string" + }, + "annotations": { + "description": "Annotations to add to the Ingress.", + "type": "object" + }, + "hosts": { + "description": "Host and path configuration for the Ingress.", + "type": "array", + "items": { + "type": "object", + "properties": { + "host": { + "description": "Host name for the Ingress.", + "type": "string" + }, + "paths": { + "description": "Path configuration for the Ingress.", + "type": "array", + "items": { + "type": "object", + "properties": { + "path": { + "description": "Path for the Ingress.", + "type": "string" + }, + "pathType": { + "description": "Path type for the Ingress.", + "type": "string" + } + } + } + } + } + } + }, + "tls": { + "description": "TLS configuration for the Ingress.", + "type": "array", + "items": { + "type": "object", + "properties": { + "secretName": { + "description": "Name of the secret for TLS.", + "type": "string" + }, + "hosts": { + "description": "Host names for the TLS configuration.", + "type": "array", + "items": { + "type": "string" + } + } + } + } + } + } + }, + "nodeSelector": { + "description": "Node selector for pod assignment.", + "type": "object" + }, + "tolerations": { + "description": "Tolerations for pod assignment.", + "type": "array" + }, + "affinity": { + "description": "Affinity rules for pod assignment.", + "type": "object" + }, + "podAntiAffinity": { + "description": "Pod anti-affinity configuration.", + "type": "string", + "enum": ["", "soft", "hard"], + "default": "" + }, + "podAntiAffinityTopologyKey": { + "description": "Topology key to use for pod anti-affinity.", + "type": "string" + }, + "topologySpreadConstraints": { + "description": "Topology spread constraints for pod assignment.", + "type": "array", + "items": { + "type": "object", + "required": ["maxSkew", "topologyKey", "whenUnsatisfiable", "labelSelector"], + "properties": { + "maxSkew": { + "type": "integer" + }, + "topologyKey": { + "type": "string" + }, + "whenUnsatisfiable": { + "type": "string", + "enum": ["DoNotSchedule", "ScheduleAnyway"] + }, + "labelSelector": { + "type": "object", + "required": ["matchLabels"], + "properties": { + "matchLabels": { + "type": "object" + } + } + } + } + } + }, + "statefulSet": { + "description": "StatefulSet configuration for managing pods.", + "type": "object", + "properties": { + "annotations": { + "type": "object" + } + } + }, + "podAnnotations": { + "description": "Annotations to add to the pods.", + "type": "object" + }, + "podLabels": { + "description": "Labels to add to the pods.", + "type": "object" + }, + "podDisruptionBudget": { + "description": "Pod disruption budget configuration.", + "type": "object", + "properties": { + "maxUnavailable": { + "type": "integer" + }, + "minAvailable": { + "type": "integer" + } + } + }, + "command": { + "description": "The command to be executed in the container.", + "type": "array", + "items": { + "type": "string" + } + }, + "persistence": { + "description": "Persistence configuration for storing data.", + "type": "object", + "required": ["enabled", "size"], + "properties": { + "enabled": { + "type": "boolean" + }, + "storageClass": { + "type": "string" + }, + "accessModes": { + "type": "array", + "items": { + "type": "string" + } + }, + "size": { + "type": "string" + } + } + }, + "configAnnotations": { + "description": "Annotations to be added to the Alertmanager configuration.", + "type": "object" + }, + "config": { + "description": "Alertmanager configuration.", + "type": "object", + "properties": { + "enabled": { + "description": "Whether to create alermanager configmap or not.", + "type": "boolean" + }, + "global": { + "description": "Global configuration options.", + "type": "object" + }, + "templates": { + "description": "Alertmanager template files.", + "type": "array", + "items": { + "type": "string" + } + }, + "receivers": { + "description": "Alert receivers configuration.", + "type": "array", + "items": { + "type": "object", + "required": ["name"], + "properties": { + "name": { + "description": "The unique name of the receiver.", + "type": "string" + } + } + } + }, + "route": { + "description": "Alert routing configuration.", + "type": "object", + "$ref": "#/definitions/config/route" + } + } + }, + "configmapReload": { + "description": "Monitors ConfigMap changes and POSTs to a URL.", + "type": "object", + "properties": { + "enabled": { + "description": "Specifies whether the configmap-reload container should be deployed.", + "type": "boolean", + "default": false + }, + "name": { + "description": "The name of the configmap-reload container.", + "type": "string" + }, + "image": { + "description": "The container image for the configmap-reload container.", + "$ref": "#/definitions/image" + }, + "containerPort": { + "description": "Port number for the configmap-reload container.", + "type": "integer" + }, + "resources": { + "description": "Resource requests and limits for the configmap-reload container.", + "$ref": "#/definitions/resources" + } + } + }, + "templates": { + "description": "Custom templates used by Alertmanager.", + "type": "object" + }, + "extraVolumeMounts": { + "description": "List of volume mounts for the Container.", + "$ref": "#/definitions/volumeMounts" + }, + "extraVolumes": { + "description": "Additional volumes to be mounted in the Alertmanager pod.", + "type": "array", + "default": [], + "items": { + "type": "object", + "required": ["name"], + "properties": { + "name": { + "type": "string" + } + } + } + }, + "extraEnv": { + "description": "List of environment variables for the Container.", + "$ref": "#/definitions/env" + }, + "testFramework": { + "description": "Configuration for the test Pod.", + "type": "object", + "properties": { + "enabled": { + "description": "Specifies whether the test Pod is enabled.", + "type": "boolean", + "default": false + }, + "annotations": { + "description": "Annotations to be added to the test Pod.", + "type": "object" + } + } + }, + "verticalPodAutoscaler": { + "description": "Vertical Pod Autoscaling configuration.", + "type": "object", + "properties": { + "enabled": { + "type": "boolean", + "default": false + }, + "recommenders": { + "type": "array" + }, + "updatePolicy": { + "type": "object" + }, + "resourcePolicy": { + "type": "object" + } + } + }, + "extraPodConfigs": { + "description": "Object to allow users to add additional Pod configuration like dnsPolicy or hostNetwork", + "type": "object" + } + } +} diff --git a/charts/kasten/k10/7.5.401/charts/prometheus/charts/alertmanager/values.yaml b/charts/kasten/k10/7.5.401/charts/prometheus/charts/alertmanager/values.yaml new file mode 100644 index 0000000000..133f438c94 --- /dev/null +++ b/charts/kasten/k10/7.5.401/charts/prometheus/charts/alertmanager/values.yaml @@ -0,0 +1,404 @@ +# yaml-language-server: $schema=values.schema.json +# Default values for alertmanager. +# This is a YAML-formatted file. +# Declare variables to be passed into your templates. + +replicaCount: 1 + +# Number of old history to retain to allow rollback +# Default Kubernetes value is set to 10 +revisionHistoryLimit: 10 + +image: + repository: quay.io/prometheus/alertmanager + pullPolicy: IfNotPresent + # Overrides the image tag whose default is the chart appVersion. + tag: "" + +# Full external URL where alertmanager is reachable, used for backlinks. +baseURL: "" + +extraArgs: {} + +## Additional Alertmanager Secret mounts +# Defines additional mounts with secrets. Secrets must be manually created in the namespace. +extraSecretMounts: [] + # - name: secret-files + # mountPath: /etc/secrets + # subPath: "" + # secretName: alertmanager-secret-files + # readOnly: true + +imagePullSecrets: [] +nameOverride: "" +fullnameOverride: "" +## namespaceOverride overrides the namespace which the resources will be deployed in +namespaceOverride: "" + +automountServiceAccountToken: true + +serviceAccount: + # Specifies whether a service account should be created + create: true + # Annotations to add to the service account + annotations: {} + # The name of the service account to use. + # If not set and create is true, a name is generated using the fullname template + name: "" + +# Sets priorityClassName in alertmanager pod +priorityClassName: "" + +# Sets schedulerName in alertmanager pod +schedulerName: "" + +podSecurityContext: + fsGroup: 65534 +dnsConfig: {} + # nameservers: + # - 1.2.3.4 + # searches: + # - ns1.svc.cluster-domain.example + # - my.dns.search.suffix + # options: + # - name: ndots + # value: "2" + # - name: edns0 +hostAliases: [] + # - ip: "127.0.0.1" + # hostnames: + # - "foo.local" + # - "bar.local" + # - ip: "10.1.2.3" + # hostnames: + # - "foo.remote" + # - "bar.remote" +securityContext: + # capabilities: + # drop: + # - ALL + # readOnlyRootFilesystem: true + runAsUser: 65534 + runAsNonRoot: true + runAsGroup: 65534 + +additionalPeers: [] + +## Additional InitContainers to initialize the pod +## +extraInitContainers: [] + +## Additional containers to add to the stateful set. This will allow to setup sidecarContainers like a proxy to integrate +## alertmanager with an external tool like teams that has not direct integration. +## +extraContainers: [] + +livenessProbe: + httpGet: + path: / + port: http + +readinessProbe: + httpGet: + path: / + port: http + +service: + annotations: {} + labels: {} + type: ClusterIP + port: 9093 + clusterPort: 9094 + loadBalancerIP: "" # Assign ext IP when Service type is LoadBalancer + loadBalancerSourceRanges: [] # Only allow access to loadBalancerIP from these IPs + # if you want to force a specific nodePort. Must be use with service.type=NodePort + # nodePort: + + # Optionally specify extra list of additional ports exposed on both services + extraPorts: [] + + # ip dual stack + ipDualStack: + enabled: false + ipFamilies: ["IPv6", "IPv4"] + ipFamilyPolicy: "PreferDualStack" + +# Configuration for creating a separate Service for each statefulset Alertmanager replica +# +servicePerReplica: + enabled: false + annotations: {} + + # Loadbalancer source IP ranges + # Only used if servicePerReplica.type is "LoadBalancer" + loadBalancerSourceRanges: [] + + # Denotes if this Service desires to route external traffic to node-local or cluster-wide endpoints + # + externalTrafficPolicy: Cluster + + # Service type + # + type: ClusterIP + +ingress: + enabled: false + className: "" + annotations: {} + # kubernetes.io/ingress.class: nginx + # kubernetes.io/tls-acme: "true" + hosts: + - host: alertmanager.domain.com + paths: + - path: / + pathType: ImplementationSpecific + tls: [] + # - secretName: chart-example-tls + # hosts: + # - alertmanager.domain.com + +# Configuration for creating an Ingress that will map to each Alertmanager replica service +# alertmanager.servicePerReplica must be enabled +# +ingressPerReplica: + enabled: false + + # className for the ingresses + # + className: "" + + annotations: {} + labels: {} + + # Final form of the hostname for each per replica ingress is + # {{ ingressPerReplica.hostPrefix }}-{{ $replicaNumber }}.{{ ingressPerReplica.hostDomain }} + # + # Prefix for the per replica ingress that will have `-$replicaNumber` + # appended to the end + hostPrefix: "alertmanager" + # Domain that will be used for the per replica ingress + hostDomain: "domain.com" + + # Paths to use for ingress rules + # + paths: + - / + + # PathType for ingress rules + # + pathType: ImplementationSpecific + + # Secret name containing the TLS certificate for alertmanager per replica ingress + # Secret must be manually created in the namespace + tlsSecretName: "" + + # Separated secret for each per replica Ingress. Can be used together with cert-manager + # + tlsSecretPerReplica: + enabled: false + # Final form of the secret for each per replica ingress is + # {{ tlsSecretPerReplica.prefix }}-{{ $replicaNumber }} + # + prefix: "alertmanager" + +resources: {} + # We usually recommend not to specify default resources and to leave this as a conscious + # choice for the user. This also increases chances charts run on environments with little + # resources, such as Minikube. If you do want to specify resources, uncomment the following + # lines, adjust them as necessary, and remove the curly braces after 'resources:'. + # limits: + # cpu: 100m + # memory: 128Mi + # requests: + # cpu: 10m + # memory: 32Mi + +nodeSelector: {} + +tolerations: [] + +affinity: {} + +## Pod anti-affinity can prevent the scheduler from placing Alertmanager replicas on the same node. +## The default value "soft" means that the scheduler should *prefer* to not schedule two replica pods onto the same node but no guarantee is provided. +## The value "hard" means that the scheduler is *required* to not schedule two replica pods onto the same node. +## The value "" will disable pod anti-affinity so that no anti-affinity rules will be configured. +## +podAntiAffinity: "" + +## If anti-affinity is enabled sets the topologyKey to use for anti-affinity. +## This can be changed to, for example, failure-domain.beta.kubernetes.io/zone +## +podAntiAffinityTopologyKey: kubernetes.io/hostname + +## Topology spread constraints rely on node labels to identify the topology domain(s) that each Node is in. +## Ref: https://kubernetes.io/docs/concepts/workloads/pods/pod-topology-spread-constraints/ +topologySpreadConstraints: [] + # - maxSkew: 1 + # topologyKey: failure-domain.beta.kubernetes.io/zone + # whenUnsatisfiable: DoNotSchedule + # labelSelector: + # matchLabels: + # app.kubernetes.io/instance: alertmanager + +statefulSet: + annotations: {} + +## Minimum number of seconds for which a newly created pod should be ready without any of its container crashing for it to +## be considered available. Defaults to 0 (pod will be considered available as soon as it is ready). +## This is an alpha field from kubernetes 1.22 until 1.24 which requires enabling the StatefulSetMinReadySeconds +## feature gate. +## Ref: https://kubernetes.io/docs/concepts/workloads/controllers/statefulset/#minimum-ready-seconds +minReadySeconds: 0 + +podAnnotations: {} +podLabels: {} + +# Ref: https://kubernetes.io/docs/tasks/run-application/configure-pdb/ +podDisruptionBudget: {} + # maxUnavailable: 1 + # minAvailable: 1 + +command: [] + +persistence: + enabled: true + ## Persistent Volume Storage Class + ## If defined, storageClassName: + ## If set to "-", storageClassName: "", which disables dynamic provisioning + ## If undefined (the default) or set to null, no storageClassName spec is + ## set, choosing the default provisioner. + ## + # storageClass: "-" + accessModes: + - ReadWriteOnce + size: 50Mi + +configAnnotations: {} + ## For example if you want to provide private data from a secret vault + ## https://github.com/banzaicloud/bank-vaults/tree/main/charts/vault-secrets-webhook + ## P.s.: Add option `configMapMutation: true` for vault-secrets-webhook + # vault.security.banzaicloud.io/vault-role: "admin" + # vault.security.banzaicloud.io/vault-addr: "https://vault.vault.svc.cluster.local:8200" + # vault.security.banzaicloud.io/vault-skip-verify: "true" + # vault.security.banzaicloud.io/vault-path: "kubernetes" + ## Example for inject secret + # slack_api_url: '${vault:secret/data/slack-hook-alerts#URL}' + +config: + enabled: true + global: {} + # slack_api_url: '' + + templates: + - '/etc/alertmanager/*.tmpl' + + receivers: + - name: default-receiver + # slack_configs: + # - channel: '@you' + # send_resolved: true + + route: + group_wait: 10s + group_interval: 5m + receiver: default-receiver + repeat_interval: 3h + +## Monitors ConfigMap changes and POSTs to a URL +## Ref: https://github.com/prometheus-operator/prometheus-operator/tree/main/cmd/prometheus-config-reloader +## +configmapReload: + ## If false, the configmap-reload container will not be deployed + ## + enabled: false + + ## configmap-reload container name + ## + name: configmap-reload + + ## configmap-reload container image + ## + image: + repository: quay.io/prometheus-operator/prometheus-config-reloader + tag: v0.66.0 + pullPolicy: IfNotPresent + + # containerPort: 9533 + + ## configmap-reload resource requests and limits + ## Ref: http://kubernetes.io/docs/user-guide/compute-resources/ + ## + resources: {} + + extraArgs: {} + + ## Optionally specify extra list of additional volumeMounts + extraVolumeMounts: [] + # - name: extras + # mountPath: /usr/share/extras + # readOnly: true + + ## Optionally specify extra environment variables to add to alertmanager container + extraEnv: [] + # - name: FOO + # value: BAR + + securityContext: {} + # capabilities: + # drop: + # - ALL + # readOnlyRootFilesystem: true + # runAsUser: 65534 + # runAsNonRoot: true + # runAsGroup: 65534 + +templates: {} +# alertmanager.tmpl: |- + +## Optionally specify extra list of additional volumeMounts +extraVolumeMounts: [] + # - name: extras + # mountPath: /usr/share/extras + # readOnly: true + +## Optionally specify extra list of additional volumes +extraVolumes: [] + # - name: extras + # emptyDir: {} + +## Optionally specify extra environment variables to add to alertmanager container +extraEnv: [] + # - name: FOO + # value: BAR + +testFramework: + enabled: false + annotations: + "helm.sh/hook": test-success + # "helm.sh/hook-delete-policy": "before-hook-creation,hook-succeeded" + +# --- Vertical Pod Autoscaler +verticalPodAutoscaler: + # -- Use VPA for alertmanager + enabled: false + # recommenders: + # - name: 'alternative' + # updatePolicy: + # updateMode: "Auto" + # minReplicas: 1 + # resourcePolicy: + # containerPolicies: + # - containerName: '*' + # minAllowed: + # cpu: 100m + # memory: 128Mi + # maxAllowed: + # cpu: 1 + # memory: 500Mi + # controlledResources: ["cpu", "memory"] + +# --- Extra Pod Configs +extraPodConfigs: {} + # dnsPolicy: ClusterFirstWithHostNet + # hostNetwork: true diff --git a/charts/kasten/k10/7.5.401/charts/prometheus/charts/kube-state-metrics/.helmignore b/charts/kasten/k10/7.5.401/charts/prometheus/charts/kube-state-metrics/.helmignore new file mode 100644 index 0000000000..f0c1319444 --- /dev/null +++ b/charts/kasten/k10/7.5.401/charts/prometheus/charts/kube-state-metrics/.helmignore @@ -0,0 +1,21 @@ +# 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 diff --git a/charts/kasten/k10/7.5.401/charts/prometheus/charts/kube-state-metrics/Chart.yaml b/charts/kasten/k10/7.5.401/charts/prometheus/charts/kube-state-metrics/Chart.yaml new file mode 100644 index 0000000000..de2d9977fb --- /dev/null +++ b/charts/kasten/k10/7.5.401/charts/prometheus/charts/kube-state-metrics/Chart.yaml @@ -0,0 +1,26 @@ +annotations: + artifacthub.io/license: Apache-2.0 + artifacthub.io/links: | + - name: Chart Source + url: https://github.com/prometheus-community/helm-charts +apiVersion: v2 +appVersion: 2.14.0 +description: Install kube-state-metrics to generate and expose cluster-level metrics +home: https://github.com/kubernetes/kube-state-metrics/ +keywords: +- metric +- monitoring +- prometheus +- kubernetes +maintainers: +- email: tariq.ibrahim@mulesoft.com + name: tariq1890 +- email: manuel@rueg.eu + name: mrueg +- email: david@0xdc.me + name: dotdc +name: kube-state-metrics +sources: +- https://github.com/kubernetes/kube-state-metrics/ +type: application +version: 5.28.0 diff --git a/charts/kasten/k10/7.5.401/charts/prometheus/charts/kube-state-metrics/README.md b/charts/kasten/k10/7.5.401/charts/prometheus/charts/kube-state-metrics/README.md new file mode 100644 index 0000000000..843be89e69 --- /dev/null +++ b/charts/kasten/k10/7.5.401/charts/prometheus/charts/kube-state-metrics/README.md @@ -0,0 +1,85 @@ +# kube-state-metrics Helm Chart + +Installs the [kube-state-metrics agent](https://github.com/kubernetes/kube-state-metrics). + +## Get Repository Info + +```console +helm repo add prometheus-community https://prometheus-community.github.io/helm-charts +helm repo update +``` + +_See [helm repo](https://helm.sh/docs/helm/helm_repo/) for command documentation._ + + +## Install Chart + +```console +helm install [RELEASE_NAME] prometheus-community/kube-state-metrics [flags] +``` + +_See [configuration](#configuration) below._ + +_See [helm install](https://helm.sh/docs/helm/helm_install/) for command documentation._ + +## Uninstall Chart + +```console +helm uninstall [RELEASE_NAME] +``` + +This removes all the Kubernetes components associated with the chart and deletes the release. + +_See [helm uninstall](https://helm.sh/docs/helm/helm_uninstall/) for command documentation._ + +## Upgrading Chart + +```console +helm upgrade [RELEASE_NAME] prometheus-community/kube-state-metrics [flags] +``` + +_See [helm upgrade](https://helm.sh/docs/helm/helm_upgrade/) for command documentation._ + +### Migrating from stable/kube-state-metrics and kubernetes/kube-state-metrics + +You can upgrade in-place: + +1. [get repository info](#get-repository-info) +1. [upgrade](#upgrading-chart) your existing release name using the new chart repository + +## Upgrading to v3.0.0 + +v3.0.0 includes kube-state-metrics v2.0, see the [changelog](https://github.com/kubernetes/kube-state-metrics/blob/release-2.0/CHANGELOG.md) for major changes on the application-side. + +The upgraded chart now the following changes: + +* Dropped support for helm v2 (helm v3 or later is required) +* collectors key was renamed to resources +* namespace key was renamed to namespaces + +## Configuration + +See [Customizing the Chart Before Installing](https://helm.sh/docs/intro/using_helm/#customizing-the-chart-before-installing). To see all configurable options with detailed comments: + +```console +helm show values prometheus-community/kube-state-metrics +``` + +### kube-rbac-proxy + +You can enable `kube-state-metrics` endpoint protection using `kube-rbac-proxy`. By setting `kubeRBACProxy.enabled: true`, this chart will deploy one RBAC proxy container per endpoint (metrics & telemetry). +To authorize access, authenticate your requests (via a `ServiceAccount` for example) with a `ClusterRole` attached such as: + +```yaml +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: kube-state-metrics-read +rules: + - apiGroups: [ "" ] + resources: ["services/kube-state-metrics"] + verbs: + - get +``` + +See [kube-rbac-proxy examples](https://github.com/brancz/kube-rbac-proxy/tree/master/examples/resource-attributes) for more details. diff --git a/charts/kasten/k10/7.5.401/charts/prometheus/charts/kube-state-metrics/templates/NOTES.txt b/charts/kasten/k10/7.5.401/charts/prometheus/charts/kube-state-metrics/templates/NOTES.txt new file mode 100644 index 0000000000..3589c24ec3 --- /dev/null +++ b/charts/kasten/k10/7.5.401/charts/prometheus/charts/kube-state-metrics/templates/NOTES.txt @@ -0,0 +1,23 @@ +kube-state-metrics is a simple service that listens to the Kubernetes API server and generates metrics about the state of the objects. +The exposed metrics can be found here: +https://github.com/kubernetes/kube-state-metrics/blob/master/docs/README.md#exposed-metrics + +The metrics are exported on the HTTP endpoint /metrics on the listening port. +In your case, {{ template "kube-state-metrics.fullname" . }}.{{ template "kube-state-metrics.namespace" . }}.svc.cluster.local:{{ .Values.service.port }}/metrics + +They are served either as plaintext or protobuf depending on the Accept header. +They are designed to be consumed either by Prometheus itself or by a scraper that is compatible with scraping a Prometheus client endpoint. + +{{- if .Values.kubeRBACProxy.enabled}} + +kube-rbac-proxy endpoint protections is enabled: +- Metrics endpoints are now HTTPS +- Ensure that the client authenticates the requests (e.g. via service account) with the following role permissions: +``` +rules: + - apiGroups: [ "" ] + resources: ["services/{{ template "kube-state-metrics.fullname" . }}"] + verbs: + - get +``` +{{- end }} diff --git a/charts/kasten/k10/7.5.401/charts/prometheus/charts/kube-state-metrics/templates/_helpers.tpl b/charts/kasten/k10/7.5.401/charts/prometheus/charts/kube-state-metrics/templates/_helpers.tpl new file mode 100644 index 0000000000..3dd326da4e --- /dev/null +++ b/charts/kasten/k10/7.5.401/charts/prometheus/charts/kube-state-metrics/templates/_helpers.tpl @@ -0,0 +1,156 @@ +{{/* vim: set filetype=mustache: */}} +{{/* +Expand the name of the chart. +*/}} +{{- define "kube-state-metrics.name" -}} +{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}} +{{- end -}} + +{{/* +Create a default fully qualified app name. +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +If release name contains chart name it will be used as a full name. +*/}} +{{- define "kube-state-metrics.fullname" -}} +{{- if .Values.fullnameOverride -}} +{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" -}} +{{- else -}} +{{- $name := default .Chart.Name .Values.nameOverride -}} +{{- if contains $name .Release.Name -}} +{{- .Release.Name | trunc 63 | trimSuffix "-" -}} +{{- else -}} +{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}} +{{- end -}} +{{- end -}} +{{- end -}} + +{{/* +Create the name of the service account to use +*/}} +{{- define "kube-state-metrics.serviceAccountName" -}} +{{- if .Values.serviceAccount.create -}} + {{ default (include "kube-state-metrics.fullname" .) .Values.serviceAccount.name }} +{{- else -}} + {{ default "default" .Values.serviceAccount.name }} +{{- end -}} +{{- end -}} + +{{/* +Allow the release namespace to be overridden for multi-namespace deployments in combined charts +*/}} +{{- define "kube-state-metrics.namespace" -}} + {{- if .Values.namespaceOverride -}} + {{- .Values.namespaceOverride -}} + {{- else -}} + {{- .Release.Namespace -}} + {{- end -}} +{{- end -}} + +{{/* +Create chart name and version as used by the chart label. +*/}} +{{- define "kube-state-metrics.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}} +{{- end -}} + +{{/* +Generate basic labels +*/}} +{{- define "kube-state-metrics.labels" }} +helm.sh/chart: {{ template "kube-state-metrics.chart" . }} +app.kubernetes.io/managed-by: {{ .Release.Service }} +app.kubernetes.io/component: metrics +app.kubernetes.io/part-of: {{ template "kube-state-metrics.name" . }} +{{- include "kube-state-metrics.selectorLabels" . }} +{{- if .Chart.AppVersion }} +app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} +{{- end }} +{{- if .Values.customLabels }} +{{ tpl (toYaml .Values.customLabels) . }} +{{- end }} +{{- if .Values.releaseLabel }} +release: {{ .Release.Name }} +{{- end }} +{{- end }} + +{{/* +Selector labels +*/}} +{{- define "kube-state-metrics.selectorLabels" }} +{{- if .Values.selectorOverride }} +{{ toYaml .Values.selectorOverride }} +{{- else }} +app.kubernetes.io/name: {{ include "kube-state-metrics.name" . }} +app.kubernetes.io/instance: {{ .Release.Name }} +{{- end }} +{{- end }} + +{{/* Sets default scrape limits for servicemonitor */}} +{{- define "servicemonitor.scrapeLimits" -}} +{{- with .sampleLimit }} +sampleLimit: {{ . }} +{{- end }} +{{- with .targetLimit }} +targetLimit: {{ . }} +{{- end }} +{{- with .labelLimit }} +labelLimit: {{ . }} +{{- end }} +{{- with .labelNameLengthLimit }} +labelNameLengthLimit: {{ . }} +{{- end }} +{{- with .labelValueLengthLimit }} +labelValueLengthLimit: {{ . }} +{{- end }} +{{- end -}} + +{{/* +Formats imagePullSecrets. Input is (dict "Values" .Values "imagePullSecrets" .{specific imagePullSecrets}) +*/}} +{{- define "kube-state-metrics.imagePullSecrets" -}} +{{- range (concat .Values.global.imagePullSecrets .imagePullSecrets) }} + {{- if eq (typeOf .) "map[string]interface {}" }} +- {{ toYaml . | trim }} + {{- else }} +- name: {{ . }} + {{- end }} +{{- end }} +{{- end -}} + +{{/* +The image to use for kube-state-metrics +*/}} +{{- define "kube-state-metrics.image" -}} +{{- if .Values.image.sha }} +{{- if .Values.global.imageRegistry }} +{{- printf "%s/%s:%s@%s" .Values.global.imageRegistry .Values.image.repository (default (printf "v%s" .Chart.AppVersion) .Values.image.tag) .Values.image.sha }} +{{- else }} +{{- printf "%s/%s:%s@%s" .Values.image.registry .Values.image.repository (default (printf "v%s" .Chart.AppVersion) .Values.image.tag) .Values.image.sha }} +{{- end }} +{{- else }} +{{- if .Values.global.imageRegistry }} +{{- printf "%s/%s:%s" .Values.global.imageRegistry .Values.image.repository (default (printf "v%s" .Chart.AppVersion) .Values.image.tag) }} +{{- else }} +{{- printf "%s/%s:%s" .Values.image.registry .Values.image.repository (default (printf "v%s" .Chart.AppVersion) .Values.image.tag) }} +{{- end }} +{{- end }} +{{- end }} + +{{/* +The image to use for kubeRBACProxy +*/}} +{{- define "kubeRBACProxy.image" -}} +{{- if .Values.kubeRBACProxy.image.sha }} +{{- if .Values.global.imageRegistry }} +{{- printf "%s/%s:%s@%s" .Values.global.imageRegistry .Values.kubeRBACProxy.image.repository (default (printf "v%s" .Chart.AppVersion) .Values.kubeRBACProxy.image.tag) .Values.kubeRBACProxy.image.sha }} +{{- else }} +{{- printf "%s/%s:%s@%s" .Values.kubeRBACProxy.image.registry .Values.kubeRBACProxy.image.repository (default (printf "v%s" .Chart.AppVersion) .Values.kubeRBACProxy.image.tag) .Values.kubeRBACProxy.image.sha }} +{{- end }} +{{- else }} +{{- if .Values.global.imageRegistry }} +{{- printf "%s/%s:%s" .Values.global.imageRegistry .Values.kubeRBACProxy.image.repository (default (printf "v%s" .Chart.AppVersion) .Values.kubeRBACProxy.image.tag) }} +{{- else }} +{{- printf "%s/%s:%s" .Values.kubeRBACProxy.image.registry .Values.kubeRBACProxy.image.repository (default (printf "v%s" .Chart.AppVersion) .Values.kubeRBACProxy.image.tag) }} +{{- end }} +{{- end }} +{{- end }} diff --git a/charts/kasten/k10/7.5.401/charts/prometheus/charts/kube-state-metrics/templates/ciliumnetworkpolicy.yaml b/charts/kasten/k10/7.5.401/charts/prometheus/charts/kube-state-metrics/templates/ciliumnetworkpolicy.yaml new file mode 100644 index 0000000000..025cd47a88 --- /dev/null +++ b/charts/kasten/k10/7.5.401/charts/prometheus/charts/kube-state-metrics/templates/ciliumnetworkpolicy.yaml @@ -0,0 +1,33 @@ +{{- if and .Values.networkPolicy.enabled (eq .Values.networkPolicy.flavor "cilium") }} +apiVersion: cilium.io/v2 +kind: CiliumNetworkPolicy +metadata: + {{- if .Values.annotations }} + annotations: + {{ toYaml .Values.annotations | nindent 4 }} + {{- end }} + labels: + {{- include "kube-state-metrics.labels" . | indent 4 }} + name: {{ template "kube-state-metrics.fullname" . }} + namespace: {{ template "kube-state-metrics.namespace" . }} +spec: + endpointSelector: + matchLabels: + {{- include "kube-state-metrics.selectorLabels" . | indent 6 }} + egress: + {{- if and .Values.networkPolicy.cilium .Values.networkPolicy.cilium.kubeApiServerSelector }} + {{ toYaml .Values.networkPolicy.cilium.kubeApiServerSelector | nindent 6 }} + {{- else }} + - toEntities: + - kube-apiserver + {{- end }} + ingress: + - toPorts: + - ports: + - port: {{ .Values.service.port | quote }} + protocol: TCP + {{- if .Values.selfMonitor.enabled }} + - port: {{ .Values.selfMonitor.telemetryPort | default 8081 | quote }} + protocol: TCP + {{ end }} +{{ end }} diff --git a/charts/kasten/k10/7.5.401/charts/prometheus/charts/kube-state-metrics/templates/clusterrolebinding.yaml b/charts/kasten/k10/7.5.401/charts/prometheus/charts/kube-state-metrics/templates/clusterrolebinding.yaml new file mode 100644 index 0000000000..cf9f628d04 --- /dev/null +++ b/charts/kasten/k10/7.5.401/charts/prometheus/charts/kube-state-metrics/templates/clusterrolebinding.yaml @@ -0,0 +1,20 @@ +{{- if and .Values.rbac.create .Values.rbac.useClusterRole -}} +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + labels: + {{- include "kube-state-metrics.labels" . | indent 4 }} + name: {{ template "kube-state-metrics.fullname" . }} +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole +{{- if .Values.rbac.useExistingRole }} + name: {{ .Values.rbac.useExistingRole }} +{{- else }} + name: {{ template "kube-state-metrics.fullname" . }} +{{- end }} +subjects: +- kind: ServiceAccount + name: {{ template "kube-state-metrics.serviceAccountName" . }} + namespace: {{ template "kube-state-metrics.namespace" . }} +{{- end -}} diff --git a/charts/kasten/k10/7.5.401/charts/prometheus/charts/kube-state-metrics/templates/crs-configmap.yaml b/charts/kasten/k10/7.5.401/charts/prometheus/charts/kube-state-metrics/templates/crs-configmap.yaml new file mode 100644 index 0000000000..d38a75a51d --- /dev/null +++ b/charts/kasten/k10/7.5.401/charts/prometheus/charts/kube-state-metrics/templates/crs-configmap.yaml @@ -0,0 +1,16 @@ +{{- if .Values.customResourceState.enabled}} +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ template "kube-state-metrics.fullname" . }}-customresourcestate-config + namespace: {{ template "kube-state-metrics.namespace" . }} + labels: + {{- include "kube-state-metrics.labels" . | indent 4 }} + {{- if .Values.annotations }} + annotations: + {{ toYaml .Values.annotations | nindent 4 }} + {{- end }} +data: + config.yaml: | + {{- toYaml .Values.customResourceState.config | nindent 4 }} +{{- end }} diff --git a/charts/kasten/k10/7.5.401/charts/prometheus/charts/kube-state-metrics/templates/deployment.yaml b/charts/kasten/k10/7.5.401/charts/prometheus/charts/kube-state-metrics/templates/deployment.yaml new file mode 100644 index 0000000000..bc93d42b71 --- /dev/null +++ b/charts/kasten/k10/7.5.401/charts/prometheus/charts/kube-state-metrics/templates/deployment.yaml @@ -0,0 +1,344 @@ +apiVersion: apps/v1 +{{- if .Values.autosharding.enabled }} +kind: StatefulSet +{{- else }} +kind: Deployment +{{- end }} +metadata: + name: {{ template "kube-state-metrics.fullname" . }} + namespace: {{ template "kube-state-metrics.namespace" . }} + labels: + {{- include "kube-state-metrics.labels" . | indent 4 }} + {{- if .Values.annotations }} + annotations: +{{ toYaml .Values.annotations | indent 4 }} + {{- end }} +spec: + selector: + matchLabels: + {{- include "kube-state-metrics.selectorLabels" . | indent 6 }} + replicas: {{ .Values.replicas }} + {{- if not .Values.autosharding.enabled }} + strategy: + type: {{ .Values.updateStrategy | default "RollingUpdate" }} + {{- end }} + revisionHistoryLimit: {{ .Values.revisionHistoryLimit }} + {{- if .Values.autosharding.enabled }} + serviceName: {{ template "kube-state-metrics.fullname" . }} + volumeClaimTemplates: [] + {{- end }} + template: + metadata: + labels: + {{- include "kube-state-metrics.labels" . | indent 8 }} + {{- with .Values.podLabels }} + {{- toYaml . | nindent 8 }} + {{- end }} + {{- if .Values.podAnnotations }} + annotations: + {{ toYaml .Values.podAnnotations | nindent 8 }} + {{- end }} + spec: + automountServiceAccountToken: {{ .Values.automountServiceAccountToken }} + hostNetwork: {{ .Values.hostNetwork }} + serviceAccountName: {{ template "kube-state-metrics.serviceAccountName" . }} + {{- if .Values.securityContext.enabled }} + securityContext: {{- omit .Values.securityContext "enabled" | toYaml | nindent 8 }} + {{- end }} + {{- if .Values.priorityClassName }} + priorityClassName: {{ .Values.priorityClassName }} + {{- end }} + {{- with .Values.initContainers }} + initContainers: + {{- toYaml . | nindent 6 }} + {{- end }} + containers: + {{- $servicePort := ternary 9090 (.Values.service.port | default 8080) .Values.kubeRBACProxy.enabled}} + {{- $telemetryPort := ternary 9091 (.Values.selfMonitor.telemetryPort | default 8081) .Values.kubeRBACProxy.enabled}} + - name: {{ template "kube-state-metrics.name" . }} + {{- if .Values.autosharding.enabled }} + env: + - name: POD_NAME + valueFrom: + fieldRef: + fieldPath: metadata.name + - name: POD_NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace + {{- if .Values.env }} + {{- toYaml .Values.env | nindent 8 }} + {{- end }} + {{ else }} + {{- if .Values.env }} + env: + {{- toYaml .Values.env | nindent 8 }} + {{- end }} + {{- end }} + args: + {{- if .Values.extraArgs }} + {{- .Values.extraArgs | toYaml | nindent 8 }} + {{- end }} + - --port={{ $servicePort }} + {{- if .Values.collectors }} + - --resources={{ .Values.collectors | join "," }} + {{- end }} + {{- if .Values.metricLabelsAllowlist }} + - --metric-labels-allowlist={{ .Values.metricLabelsAllowlist | join "," }} + {{- end }} + {{- if .Values.metricAnnotationsAllowList }} + - --metric-annotations-allowlist={{ .Values.metricAnnotationsAllowList | join "," }} + {{- end }} + {{- if .Values.metricAllowlist }} + - --metric-allowlist={{ .Values.metricAllowlist | join "," }} + {{- end }} + {{- if .Values.metricDenylist }} + - --metric-denylist={{ .Values.metricDenylist | join "," }} + {{- end }} + {{- $namespaces := list }} + {{- if .Values.namespaces }} + {{- range $ns := join "," .Values.namespaces | split "," }} + {{- $namespaces = append $namespaces (tpl $ns $) }} + {{- end }} + {{- end }} + {{- if .Values.releaseNamespace }} + {{- $namespaces = append $namespaces ( include "kube-state-metrics.namespace" . ) }} + {{- end }} + {{- if $namespaces }} + - --namespaces={{ $namespaces | mustUniq | join "," }} + {{- end }} + {{- if .Values.namespacesDenylist }} + - --namespaces-denylist={{ tpl (.Values.namespacesDenylist | join ",") $ }} + {{- end }} + {{- if .Values.autosharding.enabled }} + - --pod=$(POD_NAME) + - --pod-namespace=$(POD_NAMESPACE) + {{- end }} + {{- if .Values.kubeconfig.enabled }} + - --kubeconfig=/opt/k8s/.kube/config + {{- end }} + {{- if .Values.kubeRBACProxy.enabled }} + - --telemetry-host=127.0.0.1 + - --telemetry-port={{ $telemetryPort }} + {{- else }} + {{- if .Values.selfMonitor.telemetryHost }} + - --telemetry-host={{ .Values.selfMonitor.telemetryHost }} + {{- end }} + {{- if .Values.selfMonitor.telemetryPort }} + - --telemetry-port={{ $telemetryPort }} + {{- end }} + {{- end }} + {{- if .Values.customResourceState.enabled }} + - --custom-resource-state-config-file=/etc/customresourcestate/config.yaml + {{- end }} + {{- if or (.Values.kubeconfig.enabled) (.Values.customResourceState.enabled) (.Values.volumeMounts) }} + volumeMounts: + {{- if .Values.kubeconfig.enabled }} + - name: kubeconfig + mountPath: /opt/k8s/.kube/ + readOnly: true + {{- end }} + {{- if .Values.customResourceState.enabled }} + - name: customresourcestate-config + mountPath: /etc/customresourcestate + readOnly: true + {{- end }} + {{- if .Values.volumeMounts }} +{{ toYaml .Values.volumeMounts | indent 8 }} + {{- end }} + {{- end }} + imagePullPolicy: {{ .Values.image.pullPolicy }} + image: {{ include "kube-state-metrics.image" . }} + {{- if eq .Values.kubeRBACProxy.enabled false }} + ports: + - containerPort: {{ .Values.service.port | default 8080}} + name: "http" + {{- if .Values.selfMonitor.enabled }} + - containerPort: {{ $telemetryPort }} + name: "metrics" + {{- end }} + {{- end }} + {{- if .Values.startupProbe.enabled }} + startupProbe: + failureThreshold: {{ .Values.startupProbe.failureThreshold }} + httpGet: + {{- if .Values.hostNetwork }} + host: 127.0.0.1 + {{- end }} + httpHeaders: + {{- range $_, $header := .Values.startupProbe.httpGet.httpHeaders }} + - name: {{ $header.name }} + value: {{ $header.value }} + {{- end }} + path: /healthz + port: {{ $servicePort }} + scheme: {{ upper .Values.startupProbe.httpGet.scheme }} + initialDelaySeconds: {{ .Values.startupProbe.initialDelaySeconds }} + periodSeconds: {{ .Values.startupProbe.periodSeconds }} + successThreshold: {{ .Values.startupProbe.successThreshold }} + timeoutSeconds: {{ .Values.startupProbe.timeoutSeconds }} + {{- end }} + livenessProbe: + failureThreshold: {{ .Values.livenessProbe.failureThreshold }} + httpGet: + {{- if .Values.hostNetwork }} + host: 127.0.0.1 + {{- end }} + httpHeaders: + {{- range $_, $header := .Values.livenessProbe.httpGet.httpHeaders }} + - name: {{ $header.name }} + value: {{ $header.value }} + {{- end }} + path: /livez + port: {{ $servicePort }} + scheme: {{ upper .Values.livenessProbe.httpGet.scheme }} + initialDelaySeconds: {{ .Values.livenessProbe.initialDelaySeconds }} + periodSeconds: {{ .Values.livenessProbe.periodSeconds }} + successThreshold: {{ .Values.livenessProbe.successThreshold }} + timeoutSeconds: {{ .Values.livenessProbe.timeoutSeconds }} + readinessProbe: + failureThreshold: {{ .Values.readinessProbe.failureThreshold }} + httpGet: + {{- if .Values.hostNetwork }} + host: 127.0.0.1 + {{- end }} + httpHeaders: + {{- range $_, $header := .Values.readinessProbe.httpGet.httpHeaders }} + - name: {{ $header.name }} + value: {{ $header.value }} + {{- end }} + path: /readyz + port: {{ $telemetryPort }} + scheme: {{ upper .Values.readinessProbe.httpGet.scheme }} + initialDelaySeconds: {{ .Values.readinessProbe.initialDelaySeconds }} + periodSeconds: {{ .Values.readinessProbe.periodSeconds }} + successThreshold: {{ .Values.readinessProbe.successThreshold }} + timeoutSeconds: {{ .Values.readinessProbe.timeoutSeconds }} + resources: +{{ toYaml .Values.resources | indent 10 }} +{{- if .Values.containerSecurityContext }} + securityContext: +{{ toYaml .Values.containerSecurityContext | indent 10 }} +{{- end }} + {{- if .Values.kubeRBACProxy.enabled }} + - name: kube-rbac-proxy-http + args: + {{- if .Values.kubeRBACProxy.extraArgs }} + {{- .Values.kubeRBACProxy.extraArgs | toYaml | nindent 8 }} + {{- end }} + - --secure-listen-address=:{{ .Values.service.port | default 8080}} + - --upstream=http://127.0.0.1:{{ $servicePort }}/ + - --proxy-endpoints-port=8888 + - --config-file=/etc/kube-rbac-proxy-config/config-file.yaml + volumeMounts: + - name: kube-rbac-proxy-config + mountPath: /etc/kube-rbac-proxy-config + {{- with .Values.kubeRBACProxy.volumeMounts }} + {{- toYaml . | nindent 10 }} + {{- end }} + imagePullPolicy: {{ .Values.kubeRBACProxy.image.pullPolicy }} + image: {{ include "kubeRBACProxy.image" . }} + ports: + - containerPort: {{ .Values.service.port | default 8080}} + name: "http" + - containerPort: 8888 + name: "http-healthz" + readinessProbe: + httpGet: + scheme: HTTPS + port: 8888 + path: healthz + initialDelaySeconds: 5 + timeoutSeconds: 5 + {{- if .Values.kubeRBACProxy.resources }} + resources: +{{ toYaml .Values.kubeRBACProxy.resources | indent 10 }} +{{- end }} +{{- if .Values.kubeRBACProxy.containerSecurityContext }} + securityContext: +{{ toYaml .Values.kubeRBACProxy.containerSecurityContext | indent 10 }} +{{- end }} + {{- if .Values.selfMonitor.enabled }} + - name: kube-rbac-proxy-telemetry + args: + {{- if .Values.kubeRBACProxy.extraArgs }} + {{- .Values.kubeRBACProxy.extraArgs | toYaml | nindent 8 }} + {{- end }} + - --secure-listen-address=:{{ .Values.selfMonitor.telemetryPort | default 8081 }} + - --upstream=http://127.0.0.1:{{ $telemetryPort }}/ + - --proxy-endpoints-port=8889 + - --config-file=/etc/kube-rbac-proxy-config/config-file.yaml + volumeMounts: + - name: kube-rbac-proxy-config + mountPath: /etc/kube-rbac-proxy-config + {{- with .Values.kubeRBACProxy.volumeMounts }} + {{- toYaml . | nindent 10 }} + {{- end }} + imagePullPolicy: {{ .Values.kubeRBACProxy.image.pullPolicy }} + image: {{ include "kubeRBACProxy.image" . }} + ports: + - containerPort: {{ .Values.selfMonitor.telemetryPort | default 8081 }} + name: "metrics" + - containerPort: 8889 + name: "metrics-healthz" + readinessProbe: + httpGet: + scheme: HTTPS + port: 8889 + path: healthz + initialDelaySeconds: 5 + timeoutSeconds: 5 + {{- if .Values.kubeRBACProxy.resources }} + resources: +{{ toYaml .Values.kubeRBACProxy.resources | indent 10 }} +{{- end }} +{{- if .Values.kubeRBACProxy.containerSecurityContext }} + securityContext: +{{ toYaml .Values.kubeRBACProxy.containerSecurityContext | indent 10 }} +{{- end }} + {{- end }} + {{- end }} + {{- with .Values.containers }} + {{- toYaml . | nindent 6 }} + {{- end }} +{{- if or .Values.imagePullSecrets .Values.global.imagePullSecrets }} + imagePullSecrets: + {{- include "kube-state-metrics.imagePullSecrets" (dict "Values" .Values "imagePullSecrets" .Values.imagePullSecrets) | indent 8 }} + {{- end }} + {{- if .Values.affinity }} + affinity: +{{ toYaml .Values.affinity | indent 8 }} + {{- end }} + {{- with .Values.nodeSelector }} + nodeSelector: +{{ tpl (toYaml .) $ | indent 8 }} + {{- end }} + {{- with .Values.tolerations }} + tolerations: +{{ tpl (toYaml .) $ | indent 8 }} + {{- end }} + {{- if .Values.topologySpreadConstraints }} + topologySpreadConstraints: +{{ toYaml .Values.topologySpreadConstraints | indent 8 }} + {{- end }} + {{- if or (.Values.kubeconfig.enabled) (.Values.customResourceState.enabled) (.Values.volumes) (.Values.kubeRBACProxy.enabled) }} + volumes: + {{- if .Values.kubeconfig.enabled}} + - name: kubeconfig + secret: + secretName: {{ template "kube-state-metrics.fullname" . }}-kubeconfig + {{- end }} + {{- if .Values.kubeRBACProxy.enabled}} + - name: kube-rbac-proxy-config + configMap: + name: {{ template "kube-state-metrics.fullname" . }}-rbac-config + {{- end }} + {{- if .Values.customResourceState.enabled}} + - name: customresourcestate-config + configMap: + name: {{ template "kube-state-metrics.fullname" . }}-customresourcestate-config + {{- end }} + {{- if .Values.volumes }} +{{ toYaml .Values.volumes | indent 8 }} + {{- end }} + {{- end }} diff --git a/charts/kasten/k10/7.5.401/charts/prometheus/charts/kube-state-metrics/templates/extra-manifests.yaml b/charts/kasten/k10/7.5.401/charts/prometheus/charts/kube-state-metrics/templates/extra-manifests.yaml new file mode 100644 index 0000000000..567f7bf329 --- /dev/null +++ b/charts/kasten/k10/7.5.401/charts/prometheus/charts/kube-state-metrics/templates/extra-manifests.yaml @@ -0,0 +1,4 @@ +{{ range .Values.extraManifests }} +--- +{{ tpl (toYaml .) $ }} +{{ end }} diff --git a/charts/kasten/k10/7.5.401/charts/prometheus/charts/kube-state-metrics/templates/kubeconfig-secret.yaml b/charts/kasten/k10/7.5.401/charts/prometheus/charts/kube-state-metrics/templates/kubeconfig-secret.yaml new file mode 100644 index 0000000000..6af0084502 --- /dev/null +++ b/charts/kasten/k10/7.5.401/charts/prometheus/charts/kube-state-metrics/templates/kubeconfig-secret.yaml @@ -0,0 +1,12 @@ +{{- if .Values.kubeconfig.enabled -}} +apiVersion: v1 +kind: Secret +metadata: + name: {{ template "kube-state-metrics.fullname" . }}-kubeconfig + namespace: {{ template "kube-state-metrics.namespace" . }} + labels: + {{- include "kube-state-metrics.labels" . | indent 4 }} +type: Opaque +data: + config: '{{ .Values.kubeconfig.secret }}' +{{- end -}} diff --git a/charts/kasten/k10/7.5.401/charts/prometheus/charts/kube-state-metrics/templates/networkpolicy.yaml b/charts/kasten/k10/7.5.401/charts/prometheus/charts/kube-state-metrics/templates/networkpolicy.yaml new file mode 100644 index 0000000000..309b38ec54 --- /dev/null +++ b/charts/kasten/k10/7.5.401/charts/prometheus/charts/kube-state-metrics/templates/networkpolicy.yaml @@ -0,0 +1,43 @@ +{{- if and .Values.networkPolicy.enabled (eq .Values.networkPolicy.flavor "kubernetes") }} +kind: NetworkPolicy +apiVersion: networking.k8s.io/v1 +metadata: + {{- if .Values.annotations }} + annotations: + {{ toYaml .Values.annotations | nindent 4 }} + {{- end }} + labels: + {{- include "kube-state-metrics.labels" . | indent 4 }} + name: {{ template "kube-state-metrics.fullname" . }} + namespace: {{ template "kube-state-metrics.namespace" . }} +spec: + {{- if .Values.networkPolicy.egress }} + ## Deny all egress by default + egress: + {{- toYaml .Values.networkPolicy.egress | nindent 4 }} + {{- end }} + ingress: + {{- if .Values.networkPolicy.ingress }} + {{- toYaml .Values.networkPolicy.ingress | nindent 4 }} + {{- else }} + ## Allow ingress on default ports by default + - ports: + - port: {{ .Values.service.port | default 8080 }} + protocol: TCP + {{- if .Values.selfMonitor.enabled }} + {{- $telemetryPort := ternary 9091 (.Values.selfMonitor.telemetryPort | default 8081) .Values.kubeRBACProxy.enabled}} + - port: {{ $telemetryPort }} + protocol: TCP + {{- end }} + {{- end }} + podSelector: + {{- if .Values.networkPolicy.podSelector }} + {{- toYaml .Values.networkPolicy.podSelector | nindent 4 }} + {{- else }} + matchLabels: + {{- include "kube-state-metrics.selectorLabels" . | indent 6 }} + {{- end }} + policyTypes: + - Ingress + - Egress +{{- end }} diff --git a/charts/kasten/k10/7.5.401/charts/prometheus/charts/kube-state-metrics/templates/pdb.yaml b/charts/kasten/k10/7.5.401/charts/prometheus/charts/kube-state-metrics/templates/pdb.yaml new file mode 100644 index 0000000000..3771b511de --- /dev/null +++ b/charts/kasten/k10/7.5.401/charts/prometheus/charts/kube-state-metrics/templates/pdb.yaml @@ -0,0 +1,18 @@ +{{- if .Values.podDisruptionBudget -}} +{{ if $.Capabilities.APIVersions.Has "policy/v1/PodDisruptionBudget" -}} +apiVersion: policy/v1 +{{- else -}} +apiVersion: policy/v1beta1 +{{- end }} +kind: PodDisruptionBudget +metadata: + name: {{ template "kube-state-metrics.fullname" . }} + namespace: {{ template "kube-state-metrics.namespace" . }} + labels: + {{- include "kube-state-metrics.labels" . | indent 4 }} +spec: + selector: + matchLabels: + app.kubernetes.io/name: {{ template "kube-state-metrics.name" . }} +{{ toYaml .Values.podDisruptionBudget | indent 2 }} +{{- end -}} diff --git a/charts/kasten/k10/7.5.401/charts/prometheus/charts/kube-state-metrics/templates/podsecuritypolicy.yaml b/charts/kasten/k10/7.5.401/charts/prometheus/charts/kube-state-metrics/templates/podsecuritypolicy.yaml new file mode 100644 index 0000000000..8905e113e8 --- /dev/null +++ b/charts/kasten/k10/7.5.401/charts/prometheus/charts/kube-state-metrics/templates/podsecuritypolicy.yaml @@ -0,0 +1,39 @@ +{{- if and .Values.podSecurityPolicy.enabled (.Capabilities.APIVersions.Has "policy/v1beta1/PodSecurityPolicy") }} +apiVersion: policy/v1beta1 +kind: PodSecurityPolicy +metadata: + name: {{ template "kube-state-metrics.fullname" . }} + labels: + {{- include "kube-state-metrics.labels" . | indent 4 }} +{{- if .Values.podSecurityPolicy.annotations }} + annotations: +{{ toYaml .Values.podSecurityPolicy.annotations | indent 4 }} +{{- end }} +spec: + privileged: false + volumes: + - 'secret' +{{- if .Values.podSecurityPolicy.additionalVolumes }} +{{ toYaml .Values.podSecurityPolicy.additionalVolumes | indent 4 }} +{{- end }} + hostNetwork: false + hostIPC: false + hostPID: false + runAsUser: + rule: 'MustRunAsNonRoot' + seLinux: + rule: 'RunAsAny' + supplementalGroups: + rule: 'MustRunAs' + ranges: + # Forbid adding the root group. + - min: 1 + max: 65535 + fsGroup: + rule: 'MustRunAs' + ranges: + # Forbid adding the root group. + - min: 1 + max: 65535 + readOnlyRootFilesystem: false +{{- end }} diff --git a/charts/kasten/k10/7.5.401/charts/prometheus/charts/kube-state-metrics/templates/psp-clusterrole.yaml b/charts/kasten/k10/7.5.401/charts/prometheus/charts/kube-state-metrics/templates/psp-clusterrole.yaml new file mode 100644 index 0000000000..654e4a3d57 --- /dev/null +++ b/charts/kasten/k10/7.5.401/charts/prometheus/charts/kube-state-metrics/templates/psp-clusterrole.yaml @@ -0,0 +1,19 @@ +{{- if and .Values.podSecurityPolicy.enabled (.Capabilities.APIVersions.Has "policy/v1beta1/PodSecurityPolicy") }} +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + labels: + {{- include "kube-state-metrics.labels" . | indent 4 }} + name: psp-{{ template "kube-state-metrics.fullname" . }} +rules: +{{- $kubeTargetVersion := default .Capabilities.KubeVersion.GitVersion .Values.kubeTargetVersionOverride }} +{{- if semverCompare "> 1.15.0-0" $kubeTargetVersion }} +- apiGroups: ['policy'] +{{- else }} +- apiGroups: ['extensions'] +{{- end }} + resources: ['podsecuritypolicies'] + verbs: ['use'] + resourceNames: + - {{ template "kube-state-metrics.fullname" . }} +{{- end }} diff --git a/charts/kasten/k10/7.5.401/charts/prometheus/charts/kube-state-metrics/templates/psp-clusterrolebinding.yaml b/charts/kasten/k10/7.5.401/charts/prometheus/charts/kube-state-metrics/templates/psp-clusterrolebinding.yaml new file mode 100644 index 0000000000..5b62a18bdf --- /dev/null +++ b/charts/kasten/k10/7.5.401/charts/prometheus/charts/kube-state-metrics/templates/psp-clusterrolebinding.yaml @@ -0,0 +1,16 @@ +{{- if and .Values.podSecurityPolicy.enabled (.Capabilities.APIVersions.Has "policy/v1beta1/PodSecurityPolicy") }} +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + labels: + {{- include "kube-state-metrics.labels" . | indent 4 }} + name: psp-{{ template "kube-state-metrics.fullname" . }} +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: psp-{{ template "kube-state-metrics.fullname" . }} +subjects: + - kind: ServiceAccount + name: {{ template "kube-state-metrics.serviceAccountName" . }} + namespace: {{ template "kube-state-metrics.namespace" . }} +{{- end }} diff --git a/charts/kasten/k10/7.5.401/charts/prometheus/charts/kube-state-metrics/templates/rbac-configmap.yaml b/charts/kasten/k10/7.5.401/charts/prometheus/charts/kube-state-metrics/templates/rbac-configmap.yaml new file mode 100644 index 0000000000..671dc9d660 --- /dev/null +++ b/charts/kasten/k10/7.5.401/charts/prometheus/charts/kube-state-metrics/templates/rbac-configmap.yaml @@ -0,0 +1,22 @@ +{{- if .Values.kubeRBACProxy.enabled}} +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ template "kube-state-metrics.fullname" . }}-rbac-config + namespace: {{ template "kube-state-metrics.namespace" . }} + labels: + {{- include "kube-state-metrics.labels" . | indent 4 }} + {{- if .Values.annotations }} + annotations: + {{ toYaml .Values.annotations | nindent 4 }} + {{- end }} +data: + config-file.yaml: |+ + authorization: + resourceAttributes: + namespace: {{ template "kube-state-metrics.namespace" . }} + apiVersion: v1 + resource: services + subresource: {{ template "kube-state-metrics.fullname" . }} + name: {{ template "kube-state-metrics.fullname" . }} +{{- end }} diff --git a/charts/kasten/k10/7.5.401/charts/prometheus/charts/kube-state-metrics/templates/role.yaml b/charts/kasten/k10/7.5.401/charts/prometheus/charts/kube-state-metrics/templates/role.yaml new file mode 100644 index 0000000000..d33687f2d1 --- /dev/null +++ b/charts/kasten/k10/7.5.401/charts/prometheus/charts/kube-state-metrics/templates/role.yaml @@ -0,0 +1,212 @@ +{{- if and (eq .Values.rbac.create true) (not .Values.rbac.useExistingRole) -}} +{{- range (ternary (join "," .Values.namespaces | split "," ) (list "") (eq $.Values.rbac.useClusterRole false)) }} +--- +apiVersion: rbac.authorization.k8s.io/v1 +{{- if eq $.Values.rbac.useClusterRole false }} +kind: Role +{{- else }} +kind: ClusterRole +{{- end }} +metadata: + labels: + {{- include "kube-state-metrics.labels" $ | indent 4 }} + name: {{ template "kube-state-metrics.fullname" $ }} +{{- if eq $.Values.rbac.useClusterRole false }} + namespace: {{ . }} +{{- end }} +rules: +{{ if has "certificatesigningrequests" $.Values.collectors }} +- apiGroups: ["certificates.k8s.io"] + resources: + - certificatesigningrequests + verbs: ["list", "watch"] +{{ end -}} +{{ if has "configmaps" $.Values.collectors }} +- apiGroups: [""] + resources: + - configmaps + verbs: ["list", "watch"] +{{ end -}} +{{ if has "cronjobs" $.Values.collectors }} +- apiGroups: ["batch"] + resources: + - cronjobs + verbs: ["list", "watch"] +{{ end -}} +{{ if has "daemonsets" $.Values.collectors }} +- apiGroups: ["extensions", "apps"] + resources: + - daemonsets + verbs: ["list", "watch"] +{{ end -}} +{{ if has "deployments" $.Values.collectors }} +- apiGroups: ["extensions", "apps"] + resources: + - deployments + verbs: ["list", "watch"] +{{ end -}} +{{ if has "endpoints" $.Values.collectors }} +- apiGroups: [""] + resources: + - endpoints + verbs: ["list", "watch"] +{{ end -}} +{{ if has "endpointslices" $.Values.collectors }} +- apiGroups: ["discovery.k8s.io"] + resources: + - endpointslices + verbs: ["list", "watch"] +{{ end -}} +{{ if has "horizontalpodautoscalers" $.Values.collectors }} +- apiGroups: ["autoscaling"] + resources: + - horizontalpodautoscalers + verbs: ["list", "watch"] +{{ end -}} +{{ if has "ingresses" $.Values.collectors }} +- apiGroups: ["extensions", "networking.k8s.io"] + resources: + - ingresses + verbs: ["list", "watch"] +{{ end -}} +{{ if has "jobs" $.Values.collectors }} +- apiGroups: ["batch"] + resources: + - jobs + verbs: ["list", "watch"] +{{ end -}} +{{ if has "leases" $.Values.collectors }} +- apiGroups: ["coordination.k8s.io"] + resources: + - leases + verbs: ["list", "watch"] +{{ end -}} +{{ if has "limitranges" $.Values.collectors }} +- apiGroups: [""] + resources: + - limitranges + verbs: ["list", "watch"] +{{ end -}} +{{ if has "mutatingwebhookconfigurations" $.Values.collectors }} +- apiGroups: ["admissionregistration.k8s.io"] + resources: + - mutatingwebhookconfigurations + verbs: ["list", "watch"] +{{ end -}} +{{ if has "namespaces" $.Values.collectors }} +- apiGroups: [""] + resources: + - namespaces + verbs: ["list", "watch"] +{{ end -}} +{{ if has "networkpolicies" $.Values.collectors }} +- apiGroups: ["networking.k8s.io"] + resources: + - networkpolicies + verbs: ["list", "watch"] +{{ end -}} +{{ if has "nodes" $.Values.collectors }} +- apiGroups: [""] + resources: + - nodes + verbs: ["list", "watch"] +{{ end -}} +{{ if has "persistentvolumeclaims" $.Values.collectors }} +- apiGroups: [""] + resources: + - persistentvolumeclaims + verbs: ["list", "watch"] +{{ end -}} +{{ if has "persistentvolumes" $.Values.collectors }} +- apiGroups: [""] + resources: + - persistentvolumes + verbs: ["list", "watch"] +{{ end -}} +{{ if has "poddisruptionbudgets" $.Values.collectors }} +- apiGroups: ["policy"] + resources: + - poddisruptionbudgets + verbs: ["list", "watch"] +{{ end -}} +{{ if has "pods" $.Values.collectors }} +- apiGroups: [""] + resources: + - pods + verbs: ["list", "watch"] +{{ end -}} +{{ if has "replicasets" $.Values.collectors }} +- apiGroups: ["extensions", "apps"] + resources: + - replicasets + verbs: ["list", "watch"] +{{ end -}} +{{ if has "replicationcontrollers" $.Values.collectors }} +- apiGroups: [""] + resources: + - replicationcontrollers + verbs: ["list", "watch"] +{{ end -}} +{{ if has "resourcequotas" $.Values.collectors }} +- apiGroups: [""] + resources: + - resourcequotas + verbs: ["list", "watch"] +{{ end -}} +{{ if has "secrets" $.Values.collectors }} +- apiGroups: [""] + resources: + - secrets + verbs: ["list", "watch"] +{{ end -}} +{{ if has "services" $.Values.collectors }} +- apiGroups: [""] + resources: + - services + verbs: ["list", "watch"] +{{ end -}} +{{ if has "statefulsets" $.Values.collectors }} +- apiGroups: ["apps"] + resources: + - statefulsets + verbs: ["list", "watch"] +{{ end -}} +{{ if has "storageclasses" $.Values.collectors }} +- apiGroups: ["storage.k8s.io"] + resources: + - storageclasses + verbs: ["list", "watch"] +{{ end -}} +{{ if has "validatingwebhookconfigurations" $.Values.collectors }} +- apiGroups: ["admissionregistration.k8s.io"] + resources: + - validatingwebhookconfigurations + verbs: ["list", "watch"] +{{ end -}} +{{ if has "volumeattachments" $.Values.collectors }} +- apiGroups: ["storage.k8s.io"] + resources: + - volumeattachments + verbs: ["list", "watch"] +{{ end -}} +{{- if $.Values.kubeRBACProxy.enabled }} +- apiGroups: ["authentication.k8s.io"] + resources: + - tokenreviews + verbs: ["create"] +- apiGroups: ["authorization.k8s.io"] + resources: + - subjectaccessreviews + verbs: ["create"] +{{- end }} +{{- if $.Values.customResourceState.enabled }} +- apiGroups: ["apiextensions.k8s.io"] + resources: + - customresourcedefinitions + verbs: ["list", "watch"] +{{- end }} +{{ if $.Values.rbac.extraRules }} +{{ toYaml $.Values.rbac.extraRules }} +{{ end }} +{{- end -}} +{{- end -}} diff --git a/charts/kasten/k10/7.5.401/charts/prometheus/charts/kube-state-metrics/templates/rolebinding.yaml b/charts/kasten/k10/7.5.401/charts/prometheus/charts/kube-state-metrics/templates/rolebinding.yaml new file mode 100644 index 0000000000..330651b73f --- /dev/null +++ b/charts/kasten/k10/7.5.401/charts/prometheus/charts/kube-state-metrics/templates/rolebinding.yaml @@ -0,0 +1,24 @@ +{{- if and (eq .Values.rbac.create true) (eq .Values.rbac.useClusterRole false) -}} +{{- range (join "," $.Values.namespaces) | split "," }} +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + labels: + {{- include "kube-state-metrics.labels" $ | indent 4 }} + name: {{ template "kube-state-metrics.fullname" $ }} + namespace: {{ . }} +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role +{{- if (not $.Values.rbac.useExistingRole) }} + name: {{ template "kube-state-metrics.fullname" $ }} +{{- else }} + name: {{ $.Values.rbac.useExistingRole }} +{{- end }} +subjects: +- kind: ServiceAccount + name: {{ template "kube-state-metrics.serviceAccountName" $ }} + namespace: {{ template "kube-state-metrics.namespace" $ }} +{{- end -}} +{{- end -}} diff --git a/charts/kasten/k10/7.5.401/charts/prometheus/charts/kube-state-metrics/templates/service.yaml b/charts/kasten/k10/7.5.401/charts/prometheus/charts/kube-state-metrics/templates/service.yaml new file mode 100644 index 0000000000..90c235148f --- /dev/null +++ b/charts/kasten/k10/7.5.401/charts/prometheus/charts/kube-state-metrics/templates/service.yaml @@ -0,0 +1,53 @@ +apiVersion: v1 +kind: Service +metadata: + name: {{ template "kube-state-metrics.fullname" . }} + namespace: {{ template "kube-state-metrics.namespace" . }} + labels: + {{- include "kube-state-metrics.labels" . | indent 4 }} + annotations: + {{- if .Values.prometheusScrape }} + prometheus.io/scrape: '{{ .Values.prometheusScrape }}' + {{- end }} + {{- if .Values.service.annotations }} + {{- toYaml .Values.service.annotations | nindent 4 }} + {{- end }} +spec: + type: "{{ .Values.service.type }}" + {{- if .Values.service.ipDualStack.enabled }} + ipFamilies: {{ toYaml .Values.service.ipDualStack.ipFamilies | nindent 4 }} + ipFamilyPolicy: {{ .Values.service.ipDualStack.ipFamilyPolicy }} + {{- end }} + ports: + - name: "http" + protocol: TCP + port: {{ .Values.service.port | default 8080}} + {{- if .Values.service.nodePort }} + nodePort: {{ .Values.service.nodePort }} + {{- end }} + targetPort: {{ .Values.service.port | default 8080}} + {{ if .Values.selfMonitor.enabled }} + - name: "metrics" + protocol: TCP + port: {{ .Values.selfMonitor.telemetryPort | default 8081 }} + targetPort: {{ .Values.selfMonitor.telemetryPort | default 8081 }} + {{- if .Values.selfMonitor.telemetryNodePort }} + nodePort: {{ .Values.selfMonitor.telemetryNodePort }} + {{- end }} + {{ end }} +{{- if .Values.service.loadBalancerIP }} + loadBalancerIP: "{{ .Values.service.loadBalancerIP }}" +{{- end }} +{{- if .Values.service.loadBalancerSourceRanges }} + loadBalancerSourceRanges: + {{- range $cidr := .Values.service.loadBalancerSourceRanges }} + - {{ $cidr }} + {{- end }} +{{- end }} +{{- if .Values.autosharding.enabled }} + clusterIP: None +{{- else if .Values.service.clusterIP }} + clusterIP: "{{ .Values.service.clusterIP }}" +{{- end }} + selector: + {{- include "kube-state-metrics.selectorLabels" . | indent 4 }} diff --git a/charts/kasten/k10/7.5.401/charts/prometheus/charts/kube-state-metrics/templates/serviceaccount.yaml b/charts/kasten/k10/7.5.401/charts/prometheus/charts/kube-state-metrics/templates/serviceaccount.yaml new file mode 100644 index 0000000000..c302bc7ca0 --- /dev/null +++ b/charts/kasten/k10/7.5.401/charts/prometheus/charts/kube-state-metrics/templates/serviceaccount.yaml @@ -0,0 +1,18 @@ +{{- if .Values.serviceAccount.create -}} +apiVersion: v1 +kind: ServiceAccount +automountServiceAccountToken: {{ .Values.serviceAccount.automountServiceAccountToken }} +metadata: + labels: + {{- include "kube-state-metrics.labels" . | indent 4 }} + name: {{ template "kube-state-metrics.serviceAccountName" . }} + namespace: {{ template "kube-state-metrics.namespace" . }} +{{- if .Values.serviceAccount.annotations }} + annotations: +{{ toYaml .Values.serviceAccount.annotations | indent 4 }} +{{- end }} +{{- if or .Values.serviceAccount.imagePullSecrets .Values.global.imagePullSecrets }} +imagePullSecrets: + {{- include "kube-state-metrics.imagePullSecrets" (dict "Values" .Values "imagePullSecrets" .Values.serviceAccount.imagePullSecrets) | indent 2 }} +{{- end }} +{{- end -}} diff --git a/charts/kasten/k10/7.5.401/charts/prometheus/charts/kube-state-metrics/templates/servicemonitor.yaml b/charts/kasten/k10/7.5.401/charts/prometheus/charts/kube-state-metrics/templates/servicemonitor.yaml new file mode 100644 index 0000000000..99d7fa9246 --- /dev/null +++ b/charts/kasten/k10/7.5.401/charts/prometheus/charts/kube-state-metrics/templates/servicemonitor.yaml @@ -0,0 +1,120 @@ +{{- if .Values.prometheus.monitor.enabled }} +apiVersion: monitoring.coreos.com/v1 +kind: ServiceMonitor +metadata: + name: {{ template "kube-state-metrics.fullname" . }} + namespace: {{ template "kube-state-metrics.namespace" . }} + labels: + {{- include "kube-state-metrics.labels" . | indent 4 }} + {{- with .Values.prometheus.monitor.additionalLabels }} + {{- tpl (toYaml . | nindent 4) $ }} + {{- end }} + {{- with .Values.prometheus.monitor.annotations }} + annotations: + {{- tpl (toYaml . | nindent 4) $ }} + {{- end }} +spec: + jobLabel: {{ default "app.kubernetes.io/name" .Values.prometheus.monitor.jobLabel }} + {{- with .Values.prometheus.monitor.targetLabels }} + targetLabels: + {{- toYaml . | trim | nindent 4 }} + {{- end }} + {{- with .Values.prometheus.monitor.podTargetLabels }} + podTargetLabels: + {{- toYaml . | trim | nindent 4 }} + {{- end }} + {{- include "servicemonitor.scrapeLimits" .Values.prometheus.monitor | indent 2 }} + {{- if .Values.prometheus.monitor.namespaceSelector }} + namespaceSelector: + matchNames: + {{- with .Values.prometheus.monitor.namespaceSelector }} + {{- toYaml . | nindent 6 }} + {{- end }} + {{- end }} + selector: + matchLabels: + {{- with .Values.prometheus.monitor.selectorOverride }} + {{- toYaml . | nindent 6 }} + {{- else }} + {{- include "kube-state-metrics.selectorLabels" . | indent 6 }} + {{- end }} + endpoints: + - port: http + {{- if or .Values.prometheus.monitor.http.interval .Values.prometheus.monitor.interval }} + interval: {{ .Values.prometheus.monitor.http.interval | default .Values.prometheus.monitor.interval }} + {{- end }} + {{- if or .Values.prometheus.monitor.http.scrapeTimeout .Values.prometheus.monitor.scrapeTimeout }} + scrapeTimeout: {{ .Values.prometheus.monitor.http.scrapeTimeout | default .Values.prometheus.monitor.scrapeTimeout }} + {{- end }} + {{- if or .Values.prometheus.monitor.http.proxyUrl .Values.prometheus.monitor.proxyUrl }} + proxyUrl: {{ .Values.prometheus.monitor.http.proxyUrl | default .Values.prometheus.monitor.proxyUrl }} + {{- end }} + {{- if or .Values.prometheus.monitor.http.enableHttp2 .Values.prometheus.monitor.enableHttp2 }} + enableHttp2: {{ .Values.prometheus.monitor.http.enableHttp2 | default .Values.prometheus.monitor.enableHttp2 }} + {{- end }} + {{- if or .Values.prometheus.monitor.http.honorLabels .Values.prometheus.monitor.honorLabels }} + honorLabels: true + {{- end }} + {{- if or .Values.prometheus.monitor.http.metricRelabelings .Values.prometheus.monitor.metricRelabelings }} + metricRelabelings: + {{- toYaml (.Values.prometheus.monitor.http.metricRelabelings | default .Values.prometheus.monitor.metricRelabelings) | nindent 8 }} + {{- end }} + {{- if or .Values.prometheus.monitor.http.relabelings .Values.prometheus.monitor.relabelings }} + relabelings: + {{- toYaml (.Values.prometheus.monitor.http.relabelings | default .Values.prometheus.monitor.relabelings) | nindent 8 }} + {{- end }} + {{- if or .Values.prometheus.monitor.http.scheme .Values.prometheus.monitor.scheme }} + scheme: {{ .Values.prometheus.monitor.http.scheme | default .Values.prometheus.monitor.scheme }} + {{- end }} + {{- if or .Values.prometheus.monitor.http.tlsConfig .Values.prometheus.monitor.tlsConfig }} + tlsConfig: + {{- toYaml (.Values.prometheus.monitor.http.tlsConfig | default .Values.prometheus.monitor.tlsConfig) | nindent 8 }} + {{- end }} + {{- if or .Values.prometheus.monitor.http.bearerTokenFile .Values.prometheus.monitor.bearerTokenFile }} + bearerTokenFile: {{ .Values.prometheus.monitor.http.bearerTokenFile | default .Values.prometheus.monitor.bearerTokenFile }} + {{- end }} + {{- with (.Values.prometheus.monitor.http.bearerTokenSecret | default .Values.prometheus.monitor.bearerTokenSecret) }} + bearerTokenSecret: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- if .Values.selfMonitor.enabled }} + - port: metrics + {{- if or .Values.prometheus.monitor.metrics.interval .Values.prometheus.monitor.interval }} + interval: {{ .Values.prometheus.monitor.metrics.interval | default .Values.prometheus.monitor.interval }} + {{- end }} + {{- if or .Values.prometheus.monitor.metrics.scrapeTimeout .Values.prometheus.monitor.scrapeTimeout }} + scrapeTimeout: {{ .Values.prometheus.monitor.metrics.scrapeTimeout | default .Values.prometheus.monitor.scrapeTimeout }} + {{- end }} + {{- if or .Values.prometheus.monitor.metrics.proxyUrl .Values.prometheus.monitor.proxyUrl }} + proxyUrl: {{ .Values.prometheus.monitor.metrics.proxyUrl | default .Values.prometheus.monitor.proxyUrl }} + {{- end }} + {{- if or .Values.prometheus.monitor.metrics.enableHttp2 .Values.prometheus.monitor.enableHttp2 }} + enableHttp2: {{ .Values.prometheus.monitor.metrics.enableHttp2 | default .Values.prometheus.monitor.enableHttp2 }} + {{- end }} + {{- if or .Values.prometheus.monitor.metrics.honorLabels .Values.prometheus.monitor.honorLabels }} + honorLabels: true + {{- end }} + {{- if or .Values.prometheus.monitor.metrics.metricRelabelings .Values.prometheus.monitor.metricRelabelings }} + metricRelabelings: + {{- toYaml (.Values.prometheus.monitor.metrics.metricRelabelings | default .Values.prometheus.monitor.metricRelabelings) | nindent 8 }} + {{- end }} + {{- if or .Values.prometheus.monitor.metrics.relabelings .Values.prometheus.monitor.relabelings }} + relabelings: + {{- toYaml (.Values.prometheus.monitor.metrics.relabelings | default .Values.prometheus.monitor.relabelings) | nindent 8 }} + {{- end }} + {{- if or .Values.prometheus.monitor.metrics.scheme .Values.prometheus.monitor.scheme }} + scheme: {{ .Values.prometheus.monitor.metrics.scheme | default .Values.prometheus.monitor.scheme }} + {{- end }} + {{- if or .Values.prometheus.monitor.metrics.tlsConfig .Values.prometheus.monitor.tlsConfig }} + tlsConfig: + {{- toYaml (.Values.prometheus.monitor.metrics.tlsConfig | default .Values.prometheus.monitor.tlsConfig) | nindent 8 }} + {{- end }} + {{- if or .Values.prometheus.monitor.metrics.bearerTokenFile .Values.prometheus.monitor.bearerTokenFile }} + bearerTokenFile: {{ .Values.prometheus.monitor.metrics.bearerTokenFile | default .Values.prometheus.monitor.bearerTokenFile }} + {{- end }} + {{- with (.Values.prometheus.monitor.metrics.bearerTokenSecret | default .Values.prometheus.monitor.bearerTokenSecret) }} + bearerTokenSecret: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- end }} +{{- end }} diff --git a/charts/kasten/k10/7.5.401/charts/prometheus/charts/kube-state-metrics/templates/stsdiscovery-role.yaml b/charts/kasten/k10/7.5.401/charts/prometheus/charts/kube-state-metrics/templates/stsdiscovery-role.yaml new file mode 100644 index 0000000000..489de147c1 --- /dev/null +++ b/charts/kasten/k10/7.5.401/charts/prometheus/charts/kube-state-metrics/templates/stsdiscovery-role.yaml @@ -0,0 +1,26 @@ +{{- if and .Values.autosharding.enabled .Values.rbac.create -}} +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: stsdiscovery-{{ template "kube-state-metrics.fullname" . }} + namespace: {{ template "kube-state-metrics.namespace" . }} + labels: + {{- include "kube-state-metrics.labels" . | indent 4 }} +rules: +- apiGroups: + - "" + resources: + - pods + verbs: + - get +- apiGroups: + - apps + resourceNames: + - {{ template "kube-state-metrics.fullname" . }} + resources: + - statefulsets + verbs: + - get + - list + - watch +{{- end }} diff --git a/charts/kasten/k10/7.5.401/charts/prometheus/charts/kube-state-metrics/templates/stsdiscovery-rolebinding.yaml b/charts/kasten/k10/7.5.401/charts/prometheus/charts/kube-state-metrics/templates/stsdiscovery-rolebinding.yaml new file mode 100644 index 0000000000..73b37a4f64 --- /dev/null +++ b/charts/kasten/k10/7.5.401/charts/prometheus/charts/kube-state-metrics/templates/stsdiscovery-rolebinding.yaml @@ -0,0 +1,17 @@ +{{- if and .Values.autosharding.enabled .Values.rbac.create -}} +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: stsdiscovery-{{ template "kube-state-metrics.fullname" . }} + namespace: {{ template "kube-state-metrics.namespace" . }} + labels: + {{- include "kube-state-metrics.labels" . | indent 4 }} +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: stsdiscovery-{{ template "kube-state-metrics.fullname" . }} +subjects: + - kind: ServiceAccount + name: {{ template "kube-state-metrics.serviceAccountName" . }} + namespace: {{ template "kube-state-metrics.namespace" . }} +{{- end }} diff --git a/charts/kasten/k10/7.5.401/charts/prometheus/charts/kube-state-metrics/templates/verticalpodautoscaler.yaml b/charts/kasten/k10/7.5.401/charts/prometheus/charts/kube-state-metrics/templates/verticalpodautoscaler.yaml new file mode 100644 index 0000000000..f46305b517 --- /dev/null +++ b/charts/kasten/k10/7.5.401/charts/prometheus/charts/kube-state-metrics/templates/verticalpodautoscaler.yaml @@ -0,0 +1,44 @@ +{{- if and (.Capabilities.APIVersions.Has "autoscaling.k8s.io/v1") (.Values.verticalPodAutoscaler.enabled) }} +apiVersion: autoscaling.k8s.io/v1 +kind: VerticalPodAutoscaler +metadata: + name: {{ template "kube-state-metrics.fullname" . }} + namespace: {{ template "kube-state-metrics.namespace" . }} + labels: + {{- include "kube-state-metrics.labels" . | indent 4 }} +spec: + {{- with .Values.verticalPodAutoscaler.recommenders }} + recommenders: + {{- toYaml . | nindent 4 }} + {{- end }} + resourcePolicy: + containerPolicies: + - containerName: {{ template "kube-state-metrics.name" . }} + {{- with .Values.verticalPodAutoscaler.controlledResources }} + controlledResources: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- if .Values.verticalPodAutoscaler.controlledValues }} + controlledValues: {{ .Values.verticalPodAutoscaler.controlledValues }} + {{- end }} + {{- if .Values.verticalPodAutoscaler.maxAllowed }} + maxAllowed: + {{ toYaml .Values.verticalPodAutoscaler.maxAllowed | nindent 8 }} + {{- end }} + {{- if .Values.verticalPodAutoscaler.minAllowed }} + minAllowed: + {{ toYaml .Values.verticalPodAutoscaler.minAllowed | nindent 8 }} + {{- end }} + targetRef: + apiVersion: apps/v1 + {{- if .Values.autosharding.enabled }} + kind: StatefulSet + {{- else }} + kind: Deployment + {{- end }} + name: {{ template "kube-state-metrics.fullname" . }} + {{- with .Values.verticalPodAutoscaler.updatePolicy }} + updatePolicy: + {{- toYaml . | nindent 4 }} + {{- end }} +{{- end }} diff --git a/charts/kasten/k10/7.5.401/charts/prometheus/charts/kube-state-metrics/values.yaml b/charts/kasten/k10/7.5.401/charts/prometheus/charts/kube-state-metrics/values.yaml new file mode 100644 index 0000000000..948eb58726 --- /dev/null +++ b/charts/kasten/k10/7.5.401/charts/prometheus/charts/kube-state-metrics/values.yaml @@ -0,0 +1,549 @@ +# Default values for kube-state-metrics. +prometheusScrape: true +image: + registry: registry.k8s.io + repository: kube-state-metrics/kube-state-metrics + # If unset use v + .Charts.appVersion + tag: "" + sha: "" + pullPolicy: IfNotPresent + +imagePullSecrets: [] +# - name: "image-pull-secret" + +global: + # To help compatibility with other charts which use global.imagePullSecrets. + # Allow either an array of {name: pullSecret} maps (k8s-style), or an array of strings (more common helm-style). + # global: + # imagePullSecrets: + # - name: pullSecret1 + # - name: pullSecret2 + # or + # global: + # imagePullSecrets: + # - pullSecret1 + # - pullSecret2 + imagePullSecrets: [] + # + # Allow parent charts to override registry hostname + imageRegistry: "" + +# If set to true, this will deploy kube-state-metrics as a StatefulSet and the data +# will be automatically sharded across <.Values.replicas> pods using the built-in +# autodiscovery feature: https://github.com/kubernetes/kube-state-metrics#automated-sharding +# This is an experimental feature and there are no stability guarantees. +autosharding: + enabled: false + +replicas: 1 + +# Change the deployment strategy when autosharding is disabled. +# ref: https://kubernetes.io/docs/concepts/workloads/controllers/deployment/#strategy +# The default is "RollingUpdate" as per Kubernetes defaults. +# During a release, 'RollingUpdate' can lead to two running instances for a short period of time while 'Recreate' can create a small gap in data. +# updateStrategy: Recreate + +# Number of old history to retain to allow rollback +# Default Kubernetes value is set to 10 +revisionHistoryLimit: 10 + +# List of additional cli arguments to configure kube-state-metrics +# for example: --enable-gzip-encoding, --log-file, etc. +# all the possible args can be found here: https://github.com/kubernetes/kube-state-metrics/blob/master/docs/cli-arguments.md +extraArgs: [] + +# If false then the user will opt out of automounting API credentials. +automountServiceAccountToken: true + +service: + port: 8080 + # Default to clusterIP for backward compatibility + type: ClusterIP + ipDualStack: + enabled: false + ipFamilies: ["IPv6", "IPv4"] + ipFamilyPolicy: "PreferDualStack" + nodePort: 0 + loadBalancerIP: "" + # Only allow access to the loadBalancerIP from these IPs + loadBalancerSourceRanges: [] + clusterIP: "" + annotations: {} + +## Additional labels to add to all resources +customLabels: {} + # app: kube-state-metrics + +## Override selector labels +selectorOverride: {} + +## set to true to add the release label so scraping of the servicemonitor with kube-prometheus-stack works out of the box +releaseLabel: false + +hostNetwork: false + +rbac: + # If true, create & use RBAC resources + create: true + + # Set to a rolename to use existing role - skipping role creating - but still doing serviceaccount and rolebinding to it, rolename set here. + # useExistingRole: your-existing-role + + # If set to false - Run without Cluteradmin privs needed - ONLY works if namespace is also set (if useExistingRole is set this name is used as ClusterRole or Role to bind to) + useClusterRole: true + + # Add permissions for CustomResources' apiGroups in Role/ClusterRole. Should be used in conjunction with Custom Resource State Metrics configuration + # Example: + # - apiGroups: ["monitoring.coreos.com"] + # resources: ["prometheuses"] + # verbs: ["list", "watch"] + extraRules: [] + +# Configure kube-rbac-proxy. When enabled, creates one kube-rbac-proxy container per exposed HTTP endpoint (metrics and telemetry if enabled). +# The requests are served through the same service but requests are then HTTPS. +kubeRBACProxy: + enabled: false + image: + registry: quay.io + repository: brancz/kube-rbac-proxy + tag: v0.18.2 + sha: "" + pullPolicy: IfNotPresent + + # List of additional cli arguments to configure kube-rbac-prxy + # for example: --tls-cipher-suites, --log-file, etc. + # all the possible args can be found here: https://github.com/brancz/kube-rbac-proxy#usage + extraArgs: [] + + ## Specify security settings for a Container + ## Allows overrides and additional options compared to (Pod) securityContext + ## Ref: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/#set-the-security-context-for-a-container + containerSecurityContext: + readOnlyRootFilesystem: true + allowPrivilegeEscalation: false + capabilities: + drop: + - ALL + + resources: {} + # We usually recommend not to specify default resources and to leave this as a conscious + # choice for the user. This also increases chances charts run on environments with little + # resources, such as Minikube. If you do want to specify resources, uncomment the following + # lines, adjust them as necessary, and remove the curly braces after 'resources:'. + # limits: + # cpu: 100m + # memory: 64Mi + # requests: + # cpu: 10m + # memory: 32Mi + + ## volumeMounts enables mounting custom volumes in rbac-proxy containers + ## Useful for TLS certificates and keys + volumeMounts: [] + # - mountPath: /etc/tls + # name: kube-rbac-proxy-tls + # readOnly: true + +serviceAccount: + # Specifies whether a ServiceAccount should be created, require rbac true + create: true + # The name of the ServiceAccount to use. + # If not set and create is true, a name is generated using the fullname template + name: + # Reference to one or more secrets to be used when pulling images + # ref: https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry/ + imagePullSecrets: [] + # ServiceAccount annotations. + # Use case: AWS EKS IAM roles for service accounts + # ref: https://docs.aws.amazon.com/eks/latest/userguide/specify-service-account-role.html + annotations: {} + # If false then the user will opt out of automounting API credentials. + automountServiceAccountToken: true + +# Additional Environment variables +env: {} + # - name: GOMAXPROCS + # valueFrom: + # resourceFieldRef: + # resource: limits.cpu + +prometheus: + monitor: + enabled: false + annotations: {} + additionalLabels: {} + namespace: "" + namespaceSelector: [] + jobLabel: "" + targetLabels: [] + podTargetLabels: [] + ## SampleLimit defines per-scrape limit on number of scraped samples that will be accepted. + ## + sampleLimit: 0 + + ## TargetLimit defines a limit on the number of scraped targets that will be accepted. + ## + targetLimit: 0 + + ## Per-scrape limit on number of labels that will be accepted for a sample. Only valid in Prometheus versions 2.27.0 and newer. + ## + labelLimit: 0 + + ## Per-scrape limit on length of labels name that will be accepted for a sample. Only valid in Prometheus versions 2.27.0 and newer. + ## + labelNameLengthLimit: 0 + + ## Per-scrape limit on length of labels value that will be accepted for a sample. Only valid in Prometheus versions 2.27.0 and newer. + ## + labelValueLengthLimit: 0 + selectorOverride: {} + + ## kube-state-metrics endpoint + http: + interval: "" + scrapeTimeout: "" + proxyUrl: "" + ## Whether to enable HTTP2 for servicemonitor + enableHttp2: false + honorLabels: false + metricRelabelings: [] + relabelings: [] + scheme: "" + ## File to read bearer token for scraping targets + bearerTokenFile: "" + ## Secret to mount to read bearer token for scraping targets. The secret needs + ## to be in the same namespace as the service monitor and accessible by the + ## Prometheus Operator + bearerTokenSecret: {} + # name: secret-name + # key: key-name + tlsConfig: {} + + ## selfMonitor endpoint + metrics: + interval: "" + scrapeTimeout: "" + proxyUrl: "" + ## Whether to enable HTTP2 for servicemonitor + enableHttp2: false + honorLabels: false + metricRelabelings: [] + relabelings: [] + scheme: "" + ## File to read bearer token for scraping targets + bearerTokenFile: "" + ## Secret to mount to read bearer token for scraping targets. The secret needs + ## to be in the same namespace as the service monitor and accessible by the + ## Prometheus Operator + bearerTokenSecret: {} + # name: secret-name + # key: key-name + tlsConfig: {} + +## Specify if a Pod Security Policy for kube-state-metrics must be created +## Ref: https://kubernetes.io/docs/concepts/policy/pod-security-policy/ +## +podSecurityPolicy: + enabled: false + annotations: {} + ## Specify pod annotations + ## Ref: https://kubernetes.io/docs/concepts/policy/pod-security-policy/#apparmor + ## Ref: https://kubernetes.io/docs/concepts/policy/pod-security-policy/#seccomp + ## Ref: https://kubernetes.io/docs/concepts/policy/pod-security-policy/#sysctl + ## + # seccomp.security.alpha.kubernetes.io/allowedProfileNames: '*' + # seccomp.security.alpha.kubernetes.io/defaultProfileName: 'docker/default' + # apparmor.security.beta.kubernetes.io/defaultProfileName: 'runtime/default' + + additionalVolumes: [] + +## Configure network policy for kube-state-metrics +networkPolicy: + enabled: false + # networkPolicy.flavor -- Flavor of the network policy to use. + # Can be: + # * kubernetes for networking.k8s.io/v1/NetworkPolicy + # * cilium for cilium.io/v2/CiliumNetworkPolicy + flavor: kubernetes + + ## Configure the cilium network policy kube-apiserver selector + # cilium: + # kubeApiServerSelector: + # - toEntities: + # - kube-apiserver + + # egress: + # - {} + # ingress: + # - {} + # podSelector: + # matchLabels: + # app.kubernetes.io/name: kube-state-metrics + +securityContext: + enabled: true + runAsGroup: 65534 + runAsUser: 65534 + fsGroup: 65534 + runAsNonRoot: true + seccompProfile: + type: RuntimeDefault + +## Specify security settings for a Container +## Allows overrides and additional options compared to (Pod) securityContext +## Ref: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/#set-the-security-context-for-a-container +containerSecurityContext: + readOnlyRootFilesystem: true + allowPrivilegeEscalation: false + capabilities: + drop: + - ALL + +## Node labels for pod assignment +## Ref: https://kubernetes.io/docs/user-guide/node-selection/ +nodeSelector: {} + +## Affinity settings for pod assignment +## Ref: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/ +affinity: {} + +## Tolerations for pod assignment +## Ref: https://kubernetes.io/docs/concepts/configuration/taint-and-toleration/ +tolerations: [] + +## Topology spread constraints for pod assignment +## Ref: https://kubernetes.io/docs/concepts/workloads/pods/pod-topology-spread-constraints/ +topologySpreadConstraints: [] + +# Annotations to be added to the deployment/statefulset +annotations: {} + +# Annotations to be added to the pod +podAnnotations: {} + +# Labels to be added to the pod +podLabels: {} + +## Assign a PriorityClassName to pods if set +# priorityClassName: "" + +# Ref: https://kubernetes.io/docs/tasks/run-application/configure-pdb/ +podDisruptionBudget: {} + +# Comma-separated list of metrics to be exposed. +# This list comprises of exact metric names and/or regex patterns. +# The allowlist and denylist are mutually exclusive. +metricAllowlist: [] + +# Comma-separated list of metrics not to be enabled. +# This list comprises of exact metric names and/or regex patterns. +# The allowlist and denylist are mutually exclusive. +metricDenylist: [] + +# Comma-separated list of additional Kubernetes label keys that will be used in the resource's +# labels metric. By default the metric contains only name and namespace labels. +# To include additional labels, provide a list of resource names in their plural form and Kubernetes +# label keys you would like to allow for them (Example: '=namespaces=[k8s-label-1,k8s-label-n,...],pods=[app],...)'. +# A single '*' can be provided per resource instead to allow any labels, but that has +# severe performance implications (Example: '=pods=[*]'). +metricLabelsAllowlist: [] + # - namespaces=[k8s-label-1,k8s-label-n] + +# Comma-separated list of Kubernetes annotations keys that will be used in the resource' +# labels metric. By default the metric contains only name and namespace labels. +# To include additional annotations provide a list of resource names in their plural form and Kubernetes +# annotation keys you would like to allow for them (Example: '=namespaces=[kubernetes.io/team,...],pods=[kubernetes.io/team],...)'. +# A single '*' can be provided per resource instead to allow any annotations, but that has +# severe performance implications (Example: '=pods=[*]'). +metricAnnotationsAllowList: [] + # - pods=[k8s-annotation-1,k8s-annotation-n] + +# Available collectors for kube-state-metrics. +# By default, all available resources are enabled, comment out to disable. +collectors: + - certificatesigningrequests + - configmaps + - cronjobs + - daemonsets + - deployments + - endpoints + - horizontalpodautoscalers + - ingresses + - jobs + - leases + - limitranges + - mutatingwebhookconfigurations + - namespaces + - networkpolicies + - nodes + - persistentvolumeclaims + - persistentvolumes + - poddisruptionbudgets + - pods + - replicasets + - replicationcontrollers + - resourcequotas + - secrets + - services + - statefulsets + - storageclasses + - validatingwebhookconfigurations + - volumeattachments + +# Enabling kubeconfig will pass the --kubeconfig argument to the container +kubeconfig: + enabled: false + # base64 encoded kube-config file + secret: + +# Enabling support for customResourceState, will create a configMap including your config that will be read from kube-state-metrics +customResourceState: + enabled: false + # Add (Cluster)Role permissions to list/watch the customResources defined in the config to rbac.extraRules + config: {} + +# Enable only the release namespace for collecting resources. By default all namespaces are collected. +# If releaseNamespace and namespaces are both set a merged list will be collected. +releaseNamespace: false + +# Comma-separated list(string) or yaml list of namespaces to be enabled for collecting resources. By default all namespaces are collected. +namespaces: "" + +# Comma-separated list of namespaces not to be enabled. If namespaces and namespaces-denylist are both set, +# only namespaces that are excluded in namespaces-denylist will be used. +namespacesDenylist: "" + +## Override the deployment namespace +## +namespaceOverride: "" + +resources: {} + # We usually recommend not to specify default resources and to leave this as a conscious + # choice for the user. This also increases chances charts run on environments with little + # resources, such as Minikube. If you do want to specify resources, uncomment the following + # lines, adjust them as necessary, and remove the curly braces after 'resources:'. + # limits: + # cpu: 100m + # memory: 64Mi + # requests: + # cpu: 10m + # memory: 32Mi + +## Provide a k8s version to define apiGroups for podSecurityPolicy Cluster Role. +## For example: kubeTargetVersionOverride: 1.14.9 +## +kubeTargetVersionOverride: "" + +# Enable self metrics configuration for service and Service Monitor +# Default values for telemetry configuration can be overridden +# If you set telemetryNodePort, you must also set service.type to NodePort +selfMonitor: + enabled: false + # telemetryHost: 0.0.0.0 + # telemetryPort: 8081 + # telemetryNodePort: 0 + +# Enable vertical pod autoscaler support for kube-state-metrics +verticalPodAutoscaler: + enabled: false + + # Recommender responsible for generating recommendation for the object. + # List should be empty (then the default recommender will generate the recommendation) + # or contain exactly one recommender. + # recommenders: [] + # - name: custom-recommender-performance + + # List of resources that the vertical pod autoscaler can control. Defaults to cpu and memory + controlledResources: [] + # Specifies which resource values should be controlled: RequestsOnly or RequestsAndLimits. + # controlledValues: RequestsAndLimits + + # Define the max allowed resources for the pod + maxAllowed: {} + # cpu: 200m + # memory: 100Mi + # Define the min allowed resources for the pod + minAllowed: {} + # cpu: 200m + # memory: 100Mi + + # updatePolicy: + # Specifies minimal number of replicas which need to be alive for VPA Updater to attempt pod eviction + # minReplicas: 1 + # Specifies whether recommended updates are applied when a Pod is started and whether recommended updates + # are applied during the life of a Pod. Possible values are "Off", "Initial", "Recreate", and "Auto". + # updateMode: Auto + +# volumeMounts are used to add custom volume mounts to deployment. +# See example below +volumeMounts: [] +# - mountPath: /etc/config +# name: config-volume + +# volumes are used to add custom volumes to deployment +# See example below +volumes: [] +# - configMap: +# name: cm-for-volume +# name: config-volume + +# Extra manifests to deploy as an array +extraManifests: [] + # - apiVersion: v1 + # kind: ConfigMap + # metadata: + # labels: + # name: prometheus-extra + # data: + # extra-data: "value" + +## Containers allows injecting additional containers. +containers: [] + # - name: crd-init + # image: kiwigrid/k8s-sidecar:latest + +## InitContainers allows injecting additional initContainers. +initContainers: [] + # - name: crd-sidecar + # image: kiwigrid/k8s-sidecar:latest + +## Settings for startup, liveness and readiness probes +## Ref: https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/ +## + +## Startup probe can optionally be enabled. +## +startupProbe: + enabled: false + failureThreshold: 3 + httpGet: + httpHeaders: [] + scheme: http + initialDelaySeconds: 0 + periodSeconds: 10 + successThreshold: 1 + timeoutSeconds: 5 + +## Liveness probe +## +livenessProbe: + failureThreshold: 3 + httpGet: + httpHeaders: [] + scheme: http + initialDelaySeconds: 5 + periodSeconds: 10 + successThreshold: 1 + timeoutSeconds: 5 + +## Readiness probe +## +readinessProbe: + failureThreshold: 3 + httpGet: + httpHeaders: [] + scheme: http + initialDelaySeconds: 5 + periodSeconds: 10 + successThreshold: 1 + timeoutSeconds: 5 diff --git a/charts/kasten/k10/7.5.401/charts/prometheus/charts/prometheus-node-exporter/.helmignore b/charts/kasten/k10/7.5.401/charts/prometheus/charts/prometheus-node-exporter/.helmignore new file mode 100644 index 0000000000..f0c1319444 --- /dev/null +++ b/charts/kasten/k10/7.5.401/charts/prometheus/charts/prometheus-node-exporter/.helmignore @@ -0,0 +1,21 @@ +# 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 diff --git a/charts/kasten/k10/7.5.401/charts/prometheus/charts/prometheus-node-exporter/Chart.yaml b/charts/kasten/k10/7.5.401/charts/prometheus/charts/prometheus-node-exporter/Chart.yaml new file mode 100644 index 0000000000..7e3399e62c --- /dev/null +++ b/charts/kasten/k10/7.5.401/charts/prometheus/charts/prometheus-node-exporter/Chart.yaml @@ -0,0 +1,25 @@ +annotations: + artifacthub.io/license: Apache-2.0 + artifacthub.io/links: | + - name: Chart Source + url: https://github.com/prometheus-community/helm-charts +apiVersion: v2 +appVersion: 1.8.2 +description: A Helm chart for prometheus node-exporter +home: https://github.com/prometheus/node_exporter/ +keywords: +- node-exporter +- prometheus +- exporter +maintainers: +- email: gianrubio@gmail.com + name: gianrubio +- email: zanhsieh@gmail.com + name: zanhsieh +- email: rootsandtrees@posteo.de + name: zeritti +name: prometheus-node-exporter +sources: +- https://github.com/prometheus/node_exporter/ +type: application +version: 4.43.1 diff --git a/charts/kasten/k10/7.5.401/charts/prometheus/charts/prometheus-node-exporter/README.md b/charts/kasten/k10/7.5.401/charts/prometheus/charts/prometheus-node-exporter/README.md new file mode 100644 index 0000000000..ef83844102 --- /dev/null +++ b/charts/kasten/k10/7.5.401/charts/prometheus/charts/prometheus-node-exporter/README.md @@ -0,0 +1,96 @@ +# Prometheus Node Exporter + +Prometheus exporter for hardware and OS metrics exposed by *NIX kernels, written in Go with pluggable metric collectors. + +This chart bootstraps a Prometheus [Node Exporter](http://github.com/prometheus/node_exporter) daemonset on a [Kubernetes](http://kubernetes.io) cluster using the [Helm](https://helm.sh) package manager. + +## Get Repository Info + +```console +helm repo add prometheus-community https://prometheus-community.github.io/helm-charts +helm repo update +``` + +_See [helm repo](https://helm.sh/docs/helm/helm_repo/) for command documentation._ + +## Install Chart + +```console +helm install [RELEASE_NAME] prometheus-community/prometheus-node-exporter +``` + +_See [configuration](#configuring) below._ + +_See [helm install](https://helm.sh/docs/helm/helm_install/) for command documentation._ + +## Uninstall Chart + +```console +helm uninstall [RELEASE_NAME] +``` + +This removes all the Kubernetes components associated with the chart and deletes the release. + +_See [helm uninstall](https://helm.sh/docs/helm/helm_uninstall/) for command documentation._ + +## Upgrading Chart + +```console +helm upgrade [RELEASE_NAME] prometheus-community/prometheus-node-exporter --install +``` + +_See [helm upgrade](https://helm.sh/docs/helm/helm_upgrade/) for command documentation._ + +### 3.x to 4.x + +Starting from version 4.0.0, the `node exporter` chart is using the [Kubernetes recommended labels](https://kubernetes.io/docs/concepts/overview/working-with-objects/common-labels/). Therefore you have to delete the daemonset before you upgrade. + +```console +kubectl delete daemonset -l app=prometheus-node-exporter +helm upgrade -i prometheus-node-exporter prometheus-community/prometheus-node-exporter +``` + +If you use your own custom [ServiceMonitor](https://github.com/prometheus-operator/prometheus-operator/blob/main/Documentation/api.md#servicemonitor) or [PodMonitor](https://github.com/prometheus-operator/prometheus-operator/blob/main/Documentation/api.md#podmonitor), please ensure to upgrade their `selector` fields accordingly to the new labels. + +### From 2.x to 3.x + +Change the following: + +```yaml +hostRootFsMount: true +``` + +to: + +```yaml +hostRootFsMount: + enabled: true + mountPropagation: HostToContainer +``` + +## Configuring + +See [Customizing the Chart Before Installing](https://helm.sh/docs/intro/using_helm/#customizing-the-chart-before-installing). To see all configurable options with detailed comments, visit the chart's [values.yaml](./values.yaml), or run these configuration commands: + +```console +helm show values prometheus-community/prometheus-node-exporter +``` + +### kube-rbac-proxy + +You can enable `prometheus-node-exporter` endpoint protection using `kube-rbac-proxy`. By setting `kubeRBACProxy.enabled: true`, this chart will deploy a RBAC proxy container protecting the node-exporter endpoint. +To authorize access, authenticate your requests (via a `ServiceAccount` for example) with a `ClusterRole` attached such as: + +```yaml +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: prometheus-node-exporter-read +rules: + - apiGroups: [ "" ] + resources: ["services/node-exporter-prometheus-node-exporter"] + verbs: + - get +``` + +See [kube-rbac-proxy examples](https://github.com/brancz/kube-rbac-proxy/tree/master/examples/resource-attributes) for more details. diff --git a/charts/kasten/k10/7.5.401/charts/prometheus/charts/prometheus-node-exporter/templates/NOTES.txt b/charts/kasten/k10/7.5.401/charts/prometheus/charts/prometheus-node-exporter/templates/NOTES.txt new file mode 100644 index 0000000000..db8584def8 --- /dev/null +++ b/charts/kasten/k10/7.5.401/charts/prometheus/charts/prometheus-node-exporter/templates/NOTES.txt @@ -0,0 +1,29 @@ +1. Get the application URL by running these commands: +{{- if contains "NodePort" .Values.service.type }} + export NODE_PORT=$(kubectl get --namespace {{ template "prometheus-node-exporter.namespace" . }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ template "prometheus-node-exporter.fullname" . }}) + export NODE_IP=$(kubectl get nodes --namespace {{ template "prometheus-node-exporter.namespace" . }} -o jsonpath="{.items[0].status.addresses[0].address}") + echo http://$NODE_IP:$NODE_PORT +{{- else if contains "LoadBalancer" .Values.service.type }} + NOTE: It may take a few minutes for the LoadBalancer IP to be available. + You can watch the status of by running 'kubectl get svc -w {{ template "prometheus-node-exporter.fullname" . }}' + export SERVICE_IP=$(kubectl get svc --namespace {{ template "prometheus-node-exporter.namespace" . }} {{ template "prometheus-node-exporter.fullname" . }} -o jsonpath='{.status.loadBalancer.ingress[0].ip}') + echo http://$SERVICE_IP:{{ .Values.service.port }} +{{- else if contains "ClusterIP" .Values.service.type }} + export POD_NAME=$(kubectl get pods --namespace {{ template "prometheus-node-exporter.namespace" . }} -l "app.kubernetes.io/name={{ template "prometheus-node-exporter.name" . }},app.kubernetes.io/instance={{ .Release.Name }}" -o jsonpath="{.items[0].metadata.name}") + echo "Visit http://127.0.0.1:9100 to use your application" + kubectl port-forward --namespace {{ template "prometheus-node-exporter.namespace" . }} $POD_NAME 9100 +{{- end }} + +{{- if .Values.kubeRBACProxy.enabled}} + +kube-rbac-proxy endpoint protections is enabled: +- Metrics endpoints is now HTTPS +- Ensure that the client authenticates the requests (e.g. via service account) with the following role permissions: +``` +rules: + - apiGroups: [ "" ] + resources: ["services/{{ template "prometheus-node-exporter.fullname" . }}"] + verbs: + - get +``` +{{- end }} \ No newline at end of file diff --git a/charts/kasten/k10/7.5.401/charts/prometheus/charts/prometheus-node-exporter/templates/_helpers.tpl b/charts/kasten/k10/7.5.401/charts/prometheus/charts/prometheus-node-exporter/templates/_helpers.tpl new file mode 100644 index 0000000000..890c487a8f --- /dev/null +++ b/charts/kasten/k10/7.5.401/charts/prometheus/charts/prometheus-node-exporter/templates/_helpers.tpl @@ -0,0 +1,237 @@ +{{/* vim: set filetype=mustache: */}} +{{/* +Expand the name of the chart. +*/}} +{{- define "prometheus-node-exporter.name" -}} +{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Create a default fully qualified app name. +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +If release name contains chart name it will be used as a full name. +*/}} +{{- define "prometheus-node-exporter.fullname" -}} +{{- if .Values.fullnameOverride }} +{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- $name := default .Chart.Name .Values.nameOverride }} +{{- if contains $name .Release.Name }} +{{- .Release.Name | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }} +{{- end }} +{{- end }} +{{- end }} + +{{/* +Create chart name and version as used by the chart label. +*/}} +{{- define "prometheus-node-exporter.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Common labels +*/}} +{{- define "prometheus-node-exporter.labels" -}} +helm.sh/chart: {{ include "prometheus-node-exporter.chart" . }} +app.kubernetes.io/managed-by: {{ .Release.Service }} +app.kubernetes.io/component: metrics +app.kubernetes.io/part-of: {{ include "prometheus-node-exporter.name" . }} +{{ include "prometheus-node-exporter.selectorLabels" . }} +{{- with .Chart.AppVersion }} +app.kubernetes.io/version: {{ . | quote }} +{{- end }} +{{- with .Values.commonLabels }} +{{ tpl (toYaml .) $ }} +{{- end }} +{{- if .Values.releaseLabel }} +release: {{ .Release.Name }} +{{- end }} +{{- end }} + +{{/* +Selector labels +*/}} +{{- define "prometheus-node-exporter.selectorLabels" -}} +app.kubernetes.io/name: {{ include "prometheus-node-exporter.name" . }} +app.kubernetes.io/instance: {{ .Release.Name }} +{{- end }} + + +{{/* +Create the name of the service account to use +*/}} +{{- define "prometheus-node-exporter.serviceAccountName" -}} +{{- if .Values.serviceAccount.create }} +{{- default (include "prometheus-node-exporter.fullname" .) .Values.serviceAccount.name }} +{{- else }} +{{- default "default" .Values.serviceAccount.name }} +{{- end }} +{{- end }} + +{{/* +The image to use +*/}} +{{- define "prometheus-node-exporter.image" -}} +{{- if .Values.image.sha }} +{{- fail "image.sha forbidden. Use image.digest instead" }} +{{- else if .Values.image.digest }} +{{- if .Values.global.imageRegistry }} +{{- printf "%s/%s:%s@%s" .Values.global.imageRegistry .Values.image.repository (default (printf "v%s" .Chart.AppVersion) .Values.image.tag) .Values.image.digest }} +{{- else }} +{{- printf "%s/%s:%s@%s" .Values.image.registry .Values.image.repository (default (printf "v%s" .Chart.AppVersion) .Values.image.tag) .Values.image.digest }} +{{- end }} +{{- else }} +{{- if .Values.global.imageRegistry }} +{{- printf "%s/%s:%s" .Values.global.imageRegistry .Values.image.repository (default (printf "v%s" .Chart.AppVersion) .Values.image.tag) }} +{{- else }} +{{- printf "%s/%s:%s" .Values.image.registry .Values.image.repository (default (printf "v%s" .Chart.AppVersion) .Values.image.tag) }} +{{- end }} +{{- end }} +{{- end }} + +{{/* +Allow the release namespace to be overridden for multi-namespace deployments in combined charts +*/}} +{{- define "prometheus-node-exporter.namespace" -}} +{{- if .Values.namespaceOverride }} +{{- .Values.namespaceOverride }} +{{- else }} +{{- .Release.Namespace }} +{{- end }} +{{- end }} + +{{/* +Create the namespace name of the service monitor +*/}} +{{- define "prometheus-node-exporter.monitor-namespace" -}} +{{- if .Values.namespaceOverride }} +{{- .Values.namespaceOverride }} +{{- else }} +{{- if .Values.prometheus.monitor.namespace }} +{{- .Values.prometheus.monitor.namespace }} +{{- else }} +{{- .Release.Namespace }} +{{- end }} +{{- end }} +{{- end }} + +{{/* Sets default scrape limits for servicemonitor */}} +{{- define "servicemonitor.scrapeLimits" -}} +{{- with .sampleLimit }} +sampleLimit: {{ . }} +{{- end }} +{{- with .targetLimit }} +targetLimit: {{ . }} +{{- end }} +{{- with .labelLimit }} +labelLimit: {{ . }} +{{- end }} +{{- with .labelNameLengthLimit }} +labelNameLengthLimit: {{ . }} +{{- end }} +{{- with .labelValueLengthLimit }} +labelValueLengthLimit: {{ . }} +{{- end }} +{{- end }} + +{{/* +Formats imagePullSecrets. Input is (dict "Values" .Values "imagePullSecrets" .{specific imagePullSecrets}) +*/}} +{{- define "prometheus-node-exporter.imagePullSecrets" -}} +{{- range (concat .Values.global.imagePullSecrets .imagePullSecrets) }} + {{- if eq (typeOf .) "map[string]interface {}" }} +- {{ toYaml . | trim }} + {{- else }} +- name: {{ . }} + {{- end }} +{{- end }} +{{- end -}} + +{{/* +Create the namespace name of the pod monitor +*/}} +{{- define "prometheus-node-exporter.podmonitor-namespace" -}} +{{- if .Values.namespaceOverride }} +{{- .Values.namespaceOverride }} +{{- else }} +{{- if .Values.prometheus.podMonitor.namespace }} +{{- .Values.prometheus.podMonitor.namespace }} +{{- else }} +{{- .Release.Namespace }} +{{- end }} +{{- end }} +{{- end }} + +{{/* Sets default scrape limits for podmonitor */}} +{{- define "podmonitor.scrapeLimits" -}} +{{- with .sampleLimit }} +sampleLimit: {{ . }} +{{- end }} +{{- with .targetLimit }} +targetLimit: {{ . }} +{{- end }} +{{- with .labelLimit }} +labelLimit: {{ . }} +{{- end }} +{{- with .labelNameLengthLimit }} +labelNameLengthLimit: {{ . }} +{{- end }} +{{- with .labelValueLengthLimit }} +labelValueLengthLimit: {{ . }} +{{- end }} +{{- end }} + +{{/* Sets sidecar volumeMounts */}} +{{- define "prometheus-node-exporter.sidecarVolumeMounts" -}} +{{- range $_, $mount := $.Values.sidecarVolumeMount }} +- name: {{ $mount.name }} + mountPath: {{ $mount.mountPath }} + readOnly: {{ $mount.readOnly }} +{{- end }} +{{- range $_, $mount := $.Values.sidecarHostVolumeMounts }} +- name: {{ $mount.name }} + mountPath: {{ $mount.mountPath }} + readOnly: {{ $mount.readOnly }} +{{- if $mount.mountPropagation }} + mountPropagation: {{ $mount.mountPropagation }} +{{- end }} +{{- end }} +{{- end }} + +{{/* +The default node affinity to exclude +- AWS Fargate +- Azure virtual nodes +*/}} +{{- define "prometheus-node-exporter.defaultAffinity" -}} +nodeAffinity: + requiredDuringSchedulingIgnoredDuringExecution: + nodeSelectorTerms: + - matchExpressions: + - key: eks.amazonaws.com/compute-type + operator: NotIn + values: + - fargate + - key: type + operator: NotIn + values: + - virtual-kubelet +{{- end -}} +{{- define "prometheus-node-exporter.mergedAffinities" -}} +{{- $defaultAffinity := include "prometheus-node-exporter.defaultAffinity" . | fromYaml -}} +{{- with .Values.affinity -}} + {{- if .nodeAffinity -}} + {{- $_ := set $defaultAffinity "nodeAffinity" (mergeOverwrite $defaultAffinity.nodeAffinity .nodeAffinity) -}} + {{- end -}} + {{- if .podAffinity -}} + {{- $_ := set $defaultAffinity "podAffinity" .podAffinity -}} + {{- end -}} + {{- if .podAntiAffinity -}} + {{- $_ := set $defaultAffinity "podAntiAffinity" .podAntiAffinity -}} + {{- end -}} +{{- end -}} +{{- toYaml $defaultAffinity -}} +{{- end -}} diff --git a/charts/kasten/k10/7.5.401/charts/prometheus/charts/prometheus-node-exporter/templates/clusterrole.yaml b/charts/kasten/k10/7.5.401/charts/prometheus/charts/prometheus-node-exporter/templates/clusterrole.yaml new file mode 100644 index 0000000000..c256dba73d --- /dev/null +++ b/charts/kasten/k10/7.5.401/charts/prometheus/charts/prometheus-node-exporter/templates/clusterrole.yaml @@ -0,0 +1,19 @@ +{{- if and (eq .Values.rbac.create true) (eq .Values.kubeRBACProxy.enabled true) -}} +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: {{ include "prometheus-node-exporter.fullname" . }} + labels: + {{- include "prometheus-node-exporter.labels" . | nindent 4 }} +rules: + {{- if $.Values.kubeRBACProxy.enabled }} + - apiGroups: [ "authentication.k8s.io" ] + resources: + - tokenreviews + verbs: [ "create" ] + - apiGroups: [ "authorization.k8s.io" ] + resources: + - subjectaccessreviews + verbs: [ "create" ] + {{- end }} +{{- end -}} diff --git a/charts/kasten/k10/7.5.401/charts/prometheus/charts/prometheus-node-exporter/templates/clusterrolebinding.yaml b/charts/kasten/k10/7.5.401/charts/prometheus/charts/prometheus-node-exporter/templates/clusterrolebinding.yaml new file mode 100644 index 0000000000..653305ad9e --- /dev/null +++ b/charts/kasten/k10/7.5.401/charts/prometheus/charts/prometheus-node-exporter/templates/clusterrolebinding.yaml @@ -0,0 +1,20 @@ +{{- if and (eq .Values.rbac.create true) (eq .Values.kubeRBACProxy.enabled true) -}} +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + labels: + {{- include "prometheus-node-exporter.labels" . | nindent 4 }} + name: {{ template "prometheus-node-exporter.fullname" . }} +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole +{{- if .Values.rbac.useExistingRole }} + name: {{ .Values.rbac.useExistingRole }} +{{- else }} + name: {{ template "prometheus-node-exporter.fullname" . }} +{{- end }} +subjects: +- kind: ServiceAccount + name: {{ template "prometheus-node-exporter.serviceAccountName" . }} + namespace: {{ template "prometheus-node-exporter.namespace" . }} +{{- end -}} diff --git a/charts/kasten/k10/7.5.401/charts/prometheus/charts/prometheus-node-exporter/templates/daemonset.yaml b/charts/kasten/k10/7.5.401/charts/prometheus/charts/prometheus-node-exporter/templates/daemonset.yaml new file mode 100644 index 0000000000..e3ac2f1843 --- /dev/null +++ b/charts/kasten/k10/7.5.401/charts/prometheus/charts/prometheus-node-exporter/templates/daemonset.yaml @@ -0,0 +1,348 @@ +apiVersion: apps/v1 +kind: DaemonSet +metadata: + name: {{ include "prometheus-node-exporter.fullname" . }} + namespace: {{ include "prometheus-node-exporter.namespace" . }} + labels: + {{- include "prometheus-node-exporter.labels" . | nindent 4 }} + {{- with .Values.daemonsetAnnotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + selector: + matchLabels: + {{- include "prometheus-node-exporter.selectorLabels" . | nindent 6 }} + revisionHistoryLimit: {{ .Values.revisionHistoryLimit }} + {{- with .Values.updateStrategy }} + updateStrategy: + {{- toYaml . | nindent 4 }} + {{- end }} + template: + metadata: + {{- with .Values.podAnnotations }} + annotations: + {{- toYaml . | nindent 8 }} + {{- end }} + labels: + {{- include "prometheus-node-exporter.labels" . | nindent 8 }} + {{- with .Values.podLabels }} + {{- tpl (toYaml .) $ | nindent 8 }} + {{- end }} + spec: + automountServiceAccountToken: {{ ternary true false (or .Values.serviceAccount.automountServiceAccountToken .Values.kubeRBACProxy.enabled) }} + {{- with .Values.securityContext }} + securityContext: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.priorityClassName }} + priorityClassName: {{ . }} + {{- end }} + {{- with .Values.extraInitContainers }} + initContainers: + {{- toYaml . | nindent 8 }} + {{- end }} + serviceAccountName: {{ include "prometheus-node-exporter.serviceAccountName" . }} + {{- with .Values.terminationGracePeriodSeconds }} + terminationGracePeriodSeconds: {{ . }} + {{- end }} + containers: + {{- $servicePort := ternary .Values.kubeRBACProxy.port .Values.service.port .Values.kubeRBACProxy.enabled }} + - name: node-exporter + image: {{ include "prometheus-node-exporter.image" . }} + imagePullPolicy: {{ .Values.image.pullPolicy }} + args: + - --path.procfs=/host/proc + - --path.sysfs=/host/sys + {{- if .Values.hostRootFsMount.enabled }} + - --path.rootfs=/host/root + {{- if semverCompare ">=1.4.0-0" (coalesce .Values.version .Values.image.tag .Chart.AppVersion) }} + - --path.udev.data=/host/root/run/udev/data + {{- end }} + {{- end }} + - --web.listen-address=[$(HOST_IP)]:{{ $servicePort }} + {{- with .Values.extraArgs }} + {{- toYaml . | nindent 12 }} + {{- end }} + {{- with .Values.containerSecurityContext }} + securityContext: + {{- toYaml . | nindent 12 }} + {{- end }} + env: + - name: HOST_IP + {{- if .Values.kubeRBACProxy.enabled }} + value: 127.0.0.1 + {{- else if .Values.service.listenOnAllInterfaces }} + value: 0.0.0.0 + {{- else }} + valueFrom: + fieldRef: + apiVersion: v1 + fieldPath: status.hostIP + {{- end }} + {{- range $key, $value := .Values.env }} + - name: {{ $key }} + value: {{ $value | quote }} + {{- end }} + {{- if eq .Values.kubeRBACProxy.enabled false }} + ports: + - name: {{ .Values.service.portName }} + containerPort: {{ .Values.service.port }} + protocol: TCP + {{- end }} + livenessProbe: + failureThreshold: {{ .Values.livenessProbe.failureThreshold }} + httpGet: + {{- if .Values.kubeRBACProxy.enabled }} + host: 127.0.0.1 + {{- end }} + httpHeaders: + {{- range $_, $header := .Values.livenessProbe.httpGet.httpHeaders }} + - name: {{ $header.name }} + value: {{ $header.value }} + {{- end }} + path: / + port: {{ $servicePort }} + scheme: {{ upper .Values.livenessProbe.httpGet.scheme }} + initialDelaySeconds: {{ .Values.livenessProbe.initialDelaySeconds }} + periodSeconds: {{ .Values.livenessProbe.periodSeconds }} + successThreshold: {{ .Values.livenessProbe.successThreshold }} + timeoutSeconds: {{ .Values.livenessProbe.timeoutSeconds }} + readinessProbe: + failureThreshold: {{ .Values.readinessProbe.failureThreshold }} + httpGet: + {{- if .Values.kubeRBACProxy.enabled }} + host: 127.0.0.1 + {{- end }} + httpHeaders: + {{- range $_, $header := .Values.readinessProbe.httpGet.httpHeaders }} + - name: {{ $header.name }} + value: {{ $header.value }} + {{- end }} + path: / + port: {{ $servicePort }} + scheme: {{ upper .Values.readinessProbe.httpGet.scheme }} + initialDelaySeconds: {{ .Values.readinessProbe.initialDelaySeconds }} + periodSeconds: {{ .Values.readinessProbe.periodSeconds }} + successThreshold: {{ .Values.readinessProbe.successThreshold }} + timeoutSeconds: {{ .Values.readinessProbe.timeoutSeconds }} + {{- with .Values.resources }} + resources: + {{- toYaml . | nindent 12 }} + {{- end }} + {{- if .Values.terminationMessageParams.enabled }} + {{- with .Values.terminationMessageParams }} + terminationMessagePath: {{ .terminationMessagePath }} + terminationMessagePolicy: {{ .terminationMessagePolicy }} + {{- end }} + {{- end }} + volumeMounts: + - name: proc + mountPath: /host/proc + {{- with .Values.hostProcFsMount.mountPropagation }} + mountPropagation: {{ . }} + {{- end }} + readOnly: true + - name: sys + mountPath: /host/sys + {{- with .Values.hostSysFsMount.mountPropagation }} + mountPropagation: {{ . }} + {{- end }} + readOnly: true + {{- if .Values.hostRootFsMount.enabled }} + - name: root + mountPath: /host/root + {{- with .Values.hostRootFsMount.mountPropagation }} + mountPropagation: {{ . }} + {{- end }} + readOnly: true + {{- end }} + {{- range $_, $mount := .Values.extraHostVolumeMounts }} + - name: {{ $mount.name }} + mountPath: {{ $mount.mountPath }} + readOnly: {{ $mount.readOnly }} + {{- with $mount.mountPropagation }} + mountPropagation: {{ . }} + {{- end }} + {{- end }} + {{- range $_, $mount := .Values.sidecarVolumeMount }} + - name: {{ $mount.name }} + mountPath: {{ $mount.mountPath }} + readOnly: true + {{- end }} + {{- range $_, $mount := .Values.configmaps }} + - name: {{ $mount.name }} + mountPath: {{ $mount.mountPath }} + {{- end }} + {{- range $_, $mount := .Values.secrets }} + - name: {{ .name }} + mountPath: {{ .mountPath }} + {{- end }} + {{- with .Values.extraVolumeMounts }} + {{- toYaml . | nindent 12 }} + {{- end }} + {{- range .Values.sidecars }} + {{- $overwrites := dict "volumeMounts" (concat (include "prometheus-node-exporter.sidecarVolumeMounts" $ | fromYamlArray) (.volumeMounts | default list) | default list) }} + {{- $defaults := dict "image" (include "prometheus-node-exporter.image" $) "securityContext" $.Values.containerSecurityContext "imagePullPolicy" $.Values.image.pullPolicy }} + - {{- toYaml (merge $overwrites . $defaults) | nindent 10 }} + {{- end }} + {{- if .Values.kubeRBACProxy.enabled }} + - name: kube-rbac-proxy + args: + {{- if .Values.kubeRBACProxy.extraArgs }} + {{- .Values.kubeRBACProxy.extraArgs | toYaml | nindent 12 }} + {{- end }} + - --secure-listen-address=:{{ .Values.service.port}} + - --upstream=http://127.0.0.1:{{ $servicePort }}/ + - --proxy-endpoints-port={{ .Values.kubeRBACProxy.proxyEndpointsPort }} + - --config-file=/etc/kube-rbac-proxy-config/config-file.yaml + {{- if and .Values.kubeRBACProxy.tls.enabled .Values.tlsSecret.enabled }} + - --tls-cert-file=/tls/private/{{ .Values.tlsSecret.certItem }} + - --tls-private-key-file=/tls/private/{{ .Values.tlsSecret.keyItem }} + {{- if and .Values.kubeRBACProxy.tls.tlsClientAuth .Values.tlsSecret.caItem }} + - --client-ca-file=/tls/private/{{ .Values.tlsSecret.caItem }} + {{- end }} + {{- end }} + volumeMounts: + - name: kube-rbac-proxy-config + mountPath: /etc/kube-rbac-proxy-config + {{- if and .Values.kubeRBACProxy.tls.enabled .Values.tlsSecret.enabled }} + - name: {{ tpl .Values.tlsSecret.volumeName . | quote }} + mountPath: /tls/private + readOnly: true + {{- end }} + {{- with .Values.kubeRBACProxy.extraVolumeMounts }} + {{- toYaml . | nindent 12 }} + {{- end }} + imagePullPolicy: {{ .Values.kubeRBACProxy.image.pullPolicy }} + {{- if .Values.kubeRBACProxy.image.sha }} + image: "{{ .Values.global.imageRegistry | default .Values.kubeRBACProxy.image.registry}}/{{ .Values.kubeRBACProxy.image.repository }}:{{ .Values.kubeRBACProxy.image.tag }}@sha256:{{ .Values.kubeRBACProxy.image.sha }}" + {{- else }} + image: "{{ .Values.global.imageRegistry | default .Values.kubeRBACProxy.image.registry}}/{{ .Values.kubeRBACProxy.image.repository }}:{{ .Values.kubeRBACProxy.image.tag }}" + {{- end }} + ports: + - containerPort: {{ .Values.service.port}} + name: {{ .Values.kubeRBACProxy.portName }} + {{- if .Values.kubeRBACProxy.enableHostPort }} + hostPort: {{ .Values.service.port }} + {{- end }} + - containerPort: {{ .Values.kubeRBACProxy.proxyEndpointsPort }} + {{- if .Values.kubeRBACProxy.enableProxyEndpointsHostPort }} + hostPort: {{ .Values.kubeRBACProxy.proxyEndpointsPort }} + {{- end }} + name: "http-healthz" + readinessProbe: + httpGet: + scheme: HTTPS + port: {{ .Values.kubeRBACProxy.proxyEndpointsPort }} + path: healthz + initialDelaySeconds: 5 + timeoutSeconds: 5 + {{- if .Values.kubeRBACProxy.resources }} + resources: + {{- toYaml .Values.kubeRBACProxy.resources | nindent 12 }} + {{- end }} + {{- if .Values.terminationMessageParams.enabled }} + {{- with .Values.terminationMessageParams }} + terminationMessagePath: {{ .terminationMessagePath }} + terminationMessagePolicy: {{ .terminationMessagePolicy }} + {{- end }} + {{- end }} + {{- with .Values.kubeRBACProxy.env }} + env: + {{- range $key, $value := $.Values.kubeRBACProxy.env }} + - name: {{ $key }} + value: {{ $value | quote }} + {{- end }} + {{- end }} + {{- if .Values.kubeRBACProxy.containerSecurityContext }} + securityContext: + {{ toYaml .Values.kubeRBACProxy.containerSecurityContext | nindent 12 }} + {{- end }} + {{- end }} + {{- if or .Values.imagePullSecrets .Values.global.imagePullSecrets }} + imagePullSecrets: + {{- include "prometheus-node-exporter.imagePullSecrets" (dict "Values" .Values "imagePullSecrets" .Values.imagePullSecrets) | indent 8 }} + {{- end }} + hostNetwork: {{ .Values.hostNetwork }} + hostPID: {{ .Values.hostPID }} + hostIPC: {{ .Values.hostIPC }} + affinity: + {{- include "prometheus-node-exporter.mergedAffinities" . | nindent 8 }} + {{- with .Values.dnsConfig }} + dnsConfig: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.nodeSelector }} + nodeSelector: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.restartPolicy }} + restartPolicy: {{ . }} + {{- end }} + {{- with .Values.tolerations }} + tolerations: + {{- toYaml . | nindent 8 }} + {{- end }} + volumes: + - name: proc + hostPath: + path: /proc + - name: sys + hostPath: + path: /sys + {{- if .Values.hostRootFsMount.enabled }} + - name: root + hostPath: + path: / + {{- end }} + {{- range $_, $mount := .Values.extraHostVolumeMounts }} + - name: {{ $mount.name }} + hostPath: + path: {{ $mount.hostPath }} + {{- with $mount.type }} + type: {{ . }} + {{- end }} + {{- end }} + {{- range $_, $mount := .Values.sidecarVolumeMount }} + - name: {{ $mount.name }} + emptyDir: + medium: Memory + {{- end }} + {{- range $_, $mount := .Values.sidecarHostVolumeMounts }} + - name: {{ $mount.name }} + hostPath: + path: {{ $mount.hostPath }} + {{- end }} + {{- range $_, $mount := .Values.configmaps }} + - name: {{ $mount.name }} + configMap: + name: {{ $mount.name }} + {{- end }} + {{- range $_, $mount := .Values.secrets }} + - name: {{ $mount.name }} + secret: + secretName: {{ $mount.name }} + {{- end }} + {{- if .Values.kubeRBACProxy.enabled }} + - name: kube-rbac-proxy-config + configMap: + name: {{ template "prometheus-node-exporter.fullname" . }}-rbac-config + {{- end }} + {{- if .Values.tlsSecret.enabled }} + - name: {{ tpl .Values.tlsSecret.volumeName . | quote }} + secret: + secretName: {{ tpl .Values.tlsSecret.secretName . | quote }} + items: + - key: {{ required "Value tlsSecret.certItem must be set." .Values.tlsSecret.certItem | quote }} + path: {{ .Values.tlsSecret.certItem | quote }} + - key: {{ required "Value tlsSecret.keyItem must be set." .Values.tlsSecret.keyItem | quote }} + path: {{ .Values.tlsSecret.keyItem | quote }} + {{- if .Values.tlsSecret.caItem }} + - key: {{ .Values.tlsSecret.caItem | quote }} + path: {{ .Values.tlsSecret.caItem | quote }} + {{- end }} + {{- end }} + {{- with .Values.extraVolumes }} + {{- toYaml . | nindent 8 }} + {{- end }} diff --git a/charts/kasten/k10/7.5.401/charts/prometheus/charts/prometheus-node-exporter/templates/endpoints.yaml b/charts/kasten/k10/7.5.401/charts/prometheus/charts/prometheus-node-exporter/templates/endpoints.yaml new file mode 100644 index 0000000000..45eeb8d966 --- /dev/null +++ b/charts/kasten/k10/7.5.401/charts/prometheus/charts/prometheus-node-exporter/templates/endpoints.yaml @@ -0,0 +1,18 @@ +{{- if .Values.endpoints }} +apiVersion: v1 +kind: Endpoints +metadata: + name: {{ include "prometheus-node-exporter.fullname" . }} + namespace: {{ include "prometheus-node-exporter.namespace" . }} + labels: + {{- include "prometheus-node-exporter.labels" . | nindent 4 }} +subsets: + - addresses: + {{- range .Values.endpoints }} + - ip: {{ . }} + {{- end }} + ports: + - name: {{ .Values.service.portName }} + port: 9100 + protocol: TCP +{{- end }} diff --git a/charts/kasten/k10/7.5.401/charts/prometheus/charts/prometheus-node-exporter/templates/extra-manifests.yaml b/charts/kasten/k10/7.5.401/charts/prometheus/charts/prometheus-node-exporter/templates/extra-manifests.yaml new file mode 100644 index 0000000000..2b21b71062 --- /dev/null +++ b/charts/kasten/k10/7.5.401/charts/prometheus/charts/prometheus-node-exporter/templates/extra-manifests.yaml @@ -0,0 +1,4 @@ +{{ range .Values.extraManifests }} +--- +{{ tpl . $ }} +{{ end }} diff --git a/charts/kasten/k10/7.5.401/charts/prometheus/charts/prometheus-node-exporter/templates/networkpolicy.yaml b/charts/kasten/k10/7.5.401/charts/prometheus/charts/prometheus-node-exporter/templates/networkpolicy.yaml new file mode 100644 index 0000000000..ee40902102 --- /dev/null +++ b/charts/kasten/k10/7.5.401/charts/prometheus/charts/prometheus-node-exporter/templates/networkpolicy.yaml @@ -0,0 +1,27 @@ +{{- if .Values.networkPolicy.enabled }} +apiVersion: networking.k8s.io/v1 +kind: NetworkPolicy +metadata: + name: {{ include "prometheus-node-exporter.fullname" . }} + namespace: {{ include "prometheus-node-exporter.namespace" . }} + labels: + {{- include "prometheus-node-exporter.labels" $ | nindent 4 }} + {{- with .Values.service.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + ingress: + {{- if .Values.networkPolicy.ingress }} + {{- toYaml .Values.networkPolicy.ingress | nindent 4 }} + {{- else }} + - ports: + - port: {{ .Values.service.port }} + {{- end }} + policyTypes: + - Egress + - Ingress + podSelector: + matchLabels: + {{- include "prometheus-node-exporter.selectorLabels" . | nindent 6 }} +{{- end }} diff --git a/charts/kasten/k10/7.5.401/charts/prometheus/charts/prometheus-node-exporter/templates/podmonitor.yaml b/charts/kasten/k10/7.5.401/charts/prometheus/charts/prometheus-node-exporter/templates/podmonitor.yaml new file mode 100644 index 0000000000..f88da6a34e --- /dev/null +++ b/charts/kasten/k10/7.5.401/charts/prometheus/charts/prometheus-node-exporter/templates/podmonitor.yaml @@ -0,0 +1,91 @@ +{{- if .Values.prometheus.podMonitor.enabled }} +apiVersion: {{ .Values.prometheus.podMonitor.apiVersion | default "monitoring.coreos.com/v1" }} +kind: PodMonitor +metadata: + name: {{ include "prometheus-node-exporter.fullname" . }} + namespace: {{ include "prometheus-node-exporter.podmonitor-namespace" . }} + labels: + {{- include "prometheus-node-exporter.labels" . | nindent 4 }} + {{- with .Values.prometheus.podMonitor.additionalLabels }} + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + jobLabel: {{ default "app.kubernetes.io/name" .Values.prometheus.podMonitor.jobLabel }} + {{- include "podmonitor.scrapeLimits" .Values.prometheus.podMonitor | nindent 2 }} + selector: + matchLabels: + {{- with .Values.prometheus.podMonitor.selectorOverride }} + {{- toYaml . | nindent 6 }} + {{- else }} + {{- include "prometheus-node-exporter.selectorLabels" . | nindent 6 }} + {{- end }} + namespaceSelector: + matchNames: + - {{ include "prometheus-node-exporter.namespace" . }} + {{- with .Values.prometheus.podMonitor.attachMetadata }} + attachMetadata: + {{- toYaml . | nindent 4 }} + {{- end }} + {{- with .Values.prometheus.podMonitor.podTargetLabels }} + podTargetLabels: + {{- toYaml . | nindent 4 }} + {{- end }} + podMetricsEndpoints: + - port: {{ .Values.service.portName }} + {{- with .Values.prometheus.podMonitor.scheme }} + scheme: {{ . }} + {{- end }} + {{- with .Values.prometheus.podMonitor.path }} + path: {{ . }} + {{- end }} + {{- with .Values.prometheus.podMonitor.basicAuth }} + basicAuth: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.prometheus.podMonitor.bearerTokenSecret }} + bearerTokenSecret: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.prometheus.podMonitor.tlsConfig }} + tlsConfig: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.prometheus.podMonitor.authorization }} + authorization: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.prometheus.podMonitor.oauth2 }} + oauth2: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.prometheus.podMonitor.proxyUrl }} + proxyUrl: {{ . }} + {{- end }} + {{- with .Values.prometheus.podMonitor.interval }} + interval: {{ . }} + {{- end }} + {{- with .Values.prometheus.podMonitor.honorTimestamps }} + honorTimestamps: {{ . }} + {{- end }} + {{- with .Values.prometheus.podMonitor.honorLabels }} + honorLabels: {{ . }} + {{- end }} + {{- with .Values.prometheus.podMonitor.scrapeTimeout }} + scrapeTimeout: {{ . }} + {{- end }} + {{- with .Values.prometheus.podMonitor.relabelings }} + relabelings: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.prometheus.podMonitor.metricRelabelings }} + metricRelabelings: + {{- toYaml . | nindent 8 }} + {{- end }} + enableHttp2: {{ default false .Values.prometheus.podMonitor.enableHttp2 }} + filterRunning: {{ default true .Values.prometheus.podMonitor.filterRunning }} + followRedirects: {{ default false .Values.prometheus.podMonitor.followRedirects }} + {{- with .Values.prometheus.podMonitor.params }} + params: + {{- toYaml . | nindent 8 }} + {{- end }} +{{- end }} diff --git a/charts/kasten/k10/7.5.401/charts/prometheus/charts/prometheus-node-exporter/templates/psp-clusterrole.yaml b/charts/kasten/k10/7.5.401/charts/prometheus/charts/prometheus-node-exporter/templates/psp-clusterrole.yaml new file mode 100644 index 0000000000..8957317243 --- /dev/null +++ b/charts/kasten/k10/7.5.401/charts/prometheus/charts/prometheus-node-exporter/templates/psp-clusterrole.yaml @@ -0,0 +1,14 @@ +{{- if and .Values.rbac.create .Values.rbac.pspEnabled (.Capabilities.APIVersions.Has "policy/v1beta1/PodSecurityPolicy") }} +kind: ClusterRole +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: psp-{{ include "prometheus-node-exporter.fullname" . }} + labels: + {{- include "prometheus-node-exporter.labels" . | nindent 4 }} +rules: +- apiGroups: ['extensions'] + resources: ['podsecuritypolicies'] + verbs: ['use'] + resourceNames: + - {{ include "prometheus-node-exporter.fullname" . }} +{{- end }} diff --git a/charts/kasten/k10/7.5.401/charts/prometheus/charts/prometheus-node-exporter/templates/psp-clusterrolebinding.yaml b/charts/kasten/k10/7.5.401/charts/prometheus/charts/prometheus-node-exporter/templates/psp-clusterrolebinding.yaml new file mode 100644 index 0000000000..333370173b --- /dev/null +++ b/charts/kasten/k10/7.5.401/charts/prometheus/charts/prometheus-node-exporter/templates/psp-clusterrolebinding.yaml @@ -0,0 +1,16 @@ +{{- if and .Values.rbac.create .Values.rbac.pspEnabled (.Capabilities.APIVersions.Has "policy/v1beta1/PodSecurityPolicy") }} +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: psp-{{ include "prometheus-node-exporter.fullname" . }} + labels: + {{- include "prometheus-node-exporter.labels" . | nindent 4 }} +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: psp-{{ include "prometheus-node-exporter.fullname" . }} +subjects: + - kind: ServiceAccount + name: {{ include "prometheus-node-exporter.fullname" . }} + namespace: {{ include "prometheus-node-exporter.namespace" . }} +{{- end }} diff --git a/charts/kasten/k10/7.5.401/charts/prometheus/charts/prometheus-node-exporter/templates/psp.yaml b/charts/kasten/k10/7.5.401/charts/prometheus/charts/prometheus-node-exporter/templates/psp.yaml new file mode 100644 index 0000000000..4896c84daa --- /dev/null +++ b/charts/kasten/k10/7.5.401/charts/prometheus/charts/prometheus-node-exporter/templates/psp.yaml @@ -0,0 +1,49 @@ +{{- if and .Values.rbac.create .Values.rbac.pspEnabled (.Capabilities.APIVersions.Has "policy/v1beta1/PodSecurityPolicy") }} +apiVersion: policy/v1beta1 +kind: PodSecurityPolicy +metadata: + name: {{ include "prometheus-node-exporter.fullname" . }} + namespace: {{ include "prometheus-node-exporter.namespace" . }} + labels: + {{- include "prometheus-node-exporter.labels" . | nindent 4 }} + {{- with .Values.rbac.pspAnnotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + privileged: false + # Allow core volume types. + volumes: + - 'configMap' + - 'emptyDir' + - 'projected' + - 'secret' + - 'downwardAPI' + - 'persistentVolumeClaim' + - 'hostPath' + hostNetwork: true + hostIPC: false + hostPID: true + hostPorts: + - min: 0 + max: 65535 + runAsUser: + # Permits the container to run with root privileges as well. + rule: 'RunAsAny' + seLinux: + # This policy assumes the nodes are using AppArmor rather than SELinux. + rule: 'RunAsAny' + supplementalGroups: + rule: 'MustRunAs' + ranges: + # Allow adding the root group. + - min: 0 + max: 65535 + fsGroup: + rule: 'MustRunAs' + ranges: + # Allow adding the root group. + - min: 0 + max: 65535 + readOnlyRootFilesystem: false +{{- end }} diff --git a/charts/kasten/k10/7.5.401/charts/prometheus/charts/prometheus-node-exporter/templates/rbac-configmap.yaml b/charts/kasten/k10/7.5.401/charts/prometheus/charts/prometheus-node-exporter/templates/rbac-configmap.yaml new file mode 100644 index 0000000000..814e110337 --- /dev/null +++ b/charts/kasten/k10/7.5.401/charts/prometheus/charts/prometheus-node-exporter/templates/rbac-configmap.yaml @@ -0,0 +1,16 @@ +{{- if .Values.kubeRBACProxy.enabled}} +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ template "prometheus-node-exporter.fullname" . }}-rbac-config + namespace: {{ include "prometheus-node-exporter.namespace" . }} +data: + config-file.yaml: |+ + authorization: + resourceAttributes: + namespace: {{ template "prometheus-node-exporter.namespace" . }} + apiVersion: v1 + resource: services + subresource: {{ template "prometheus-node-exporter.fullname" . }} + name: {{ template "prometheus-node-exporter.fullname" . }} +{{- end }} \ No newline at end of file diff --git a/charts/kasten/k10/7.5.401/charts/prometheus/charts/prometheus-node-exporter/templates/service.yaml b/charts/kasten/k10/7.5.401/charts/prometheus/charts/prometheus-node-exporter/templates/service.yaml new file mode 100644 index 0000000000..abaa31b7f6 --- /dev/null +++ b/charts/kasten/k10/7.5.401/charts/prometheus/charts/prometheus-node-exporter/templates/service.yaml @@ -0,0 +1,38 @@ +{{- if .Values.service.enabled }} +apiVersion: v1 +kind: Service +metadata: + name: {{ include "prometheus-node-exporter.fullname" . }} + namespace: {{ include "prometheus-node-exporter.namespace" . }} + labels: + {{- include "prometheus-node-exporter.labels" $ | nindent 4 }} + {{- with .Values.service.labels }} + {{- toYaml . | nindent 4 }} + {{- end }} + {{- with .Values.service.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +spec: +{{- if .Values.service.ipDualStack.enabled }} + ipFamilies: {{ toYaml .Values.service.ipDualStack.ipFamilies | nindent 4 }} + ipFamilyPolicy: {{ .Values.service.ipDualStack.ipFamilyPolicy }} +{{- end }} +{{- if .Values.service.externalTrafficPolicy }} + externalTrafficPolicy: {{ .Values.service.externalTrafficPolicy }} +{{- end }} + type: {{ .Values.service.type }} +{{- if and (eq .Values.service.type "ClusterIP") .Values.service.clusterIP }} + clusterIP: "{{ .Values.service.clusterIP }}" +{{- end }} + ports: + - port: {{ .Values.service.servicePort | default .Values.service.port }} + {{- if ( and (eq .Values.service.type "NodePort" ) (not (empty .Values.service.nodePort)) ) }} + nodePort: {{ .Values.service.nodePort }} + {{- end }} + targetPort: {{ .Values.service.targetPort }} + protocol: TCP + name: {{ .Values.service.portName }} + selector: + {{- include "prometheus-node-exporter.selectorLabels" . | nindent 4 }} +{{- end }} diff --git a/charts/kasten/k10/7.5.401/charts/prometheus/charts/prometheus-node-exporter/templates/serviceaccount.yaml b/charts/kasten/k10/7.5.401/charts/prometheus/charts/prometheus-node-exporter/templates/serviceaccount.yaml new file mode 100644 index 0000000000..462b0cda4b --- /dev/null +++ b/charts/kasten/k10/7.5.401/charts/prometheus/charts/prometheus-node-exporter/templates/serviceaccount.yaml @@ -0,0 +1,18 @@ +{{- if and .Values.rbac.create .Values.serviceAccount.create -}} +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ include "prometheus-node-exporter.serviceAccountName" . }} + namespace: {{ include "prometheus-node-exporter.namespace" . }} + labels: + {{- include "prometheus-node-exporter.labels" . | nindent 4 }} + {{- with .Values.serviceAccount.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +automountServiceAccountToken: {{ .Values.serviceAccount.automountServiceAccountToken }} +{{- if or .Values.serviceAccount.imagePullSecrets .Values.global.imagePullSecrets }} +imagePullSecrets: + {{- include "prometheus-node-exporter.imagePullSecrets" (dict "Values" .Values "imagePullSecrets" .Values.serviceAccount.imagePullSecrets) | indent 2 }} +{{- end }} +{{- end -}} diff --git a/charts/kasten/k10/7.5.401/charts/prometheus/charts/prometheus-node-exporter/templates/servicemonitor.yaml b/charts/kasten/k10/7.5.401/charts/prometheus/charts/prometheus-node-exporter/templates/servicemonitor.yaml new file mode 100644 index 0000000000..96ec1af5a4 --- /dev/null +++ b/charts/kasten/k10/7.5.401/charts/prometheus/charts/prometheus-node-exporter/templates/servicemonitor.yaml @@ -0,0 +1,65 @@ +{{- if .Values.prometheus.monitor.enabled }} +apiVersion: {{ .Values.prometheus.monitor.apiVersion | default "monitoring.coreos.com/v1" }} +kind: ServiceMonitor +metadata: + name: {{ include "prometheus-node-exporter.fullname" . }} + namespace: {{ include "prometheus-node-exporter.monitor-namespace" . }} + labels: + {{- include "prometheus-node-exporter.labels" . | nindent 4 }} + {{- with .Values.prometheus.monitor.additionalLabels }} + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + jobLabel: {{ default "app.kubernetes.io/name" .Values.prometheus.monitor.jobLabel }} + {{- include "servicemonitor.scrapeLimits" .Values.prometheus.monitor | nindent 2 }} + {{- with .Values.prometheus.monitor.podTargetLabels }} + podTargetLabels: + {{- toYaml . | nindent 4 }} + {{- end }} + {{- with .Values.prometheus.monitor.targetLabels }} + targetLabels: + {{- toYaml . | nindent 4 }} + {{- end }} + selector: + matchLabels: + {{- with .Values.prometheus.monitor.selectorOverride }} + {{- toYaml . | nindent 6 }} + {{- else }} + {{- include "prometheus-node-exporter.selectorLabels" . | nindent 6 }} + {{- end }} + {{- with .Values.prometheus.monitor.attachMetadata }} + attachMetadata: + {{- toYaml . | nindent 4 }} + {{- end }} + endpoints: + - port: {{ .Values.service.portName }} + scheme: {{ .Values.prometheus.monitor.scheme }} + {{- with .Values.prometheus.monitor.basicAuth }} + basicAuth: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.prometheus.monitor.bearerTokenFile }} + bearerTokenFile: {{ . }} + {{- end }} + {{- with .Values.prometheus.monitor.tlsConfig }} + tlsConfig: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.prometheus.monitor.proxyUrl }} + proxyUrl: {{ . }} + {{- end }} + {{- with .Values.prometheus.monitor.interval }} + interval: {{ . }} + {{- end }} + {{- with .Values.prometheus.monitor.scrapeTimeout }} + scrapeTimeout: {{ . }} + {{- end }} + {{- with .Values.prometheus.monitor.relabelings }} + relabelings: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.prometheus.monitor.metricRelabelings }} + metricRelabelings: + {{- toYaml . | nindent 8 }} + {{- end }} +{{- end }} diff --git a/charts/kasten/k10/7.5.401/charts/prometheus/charts/prometheus-node-exporter/templates/verticalpodautoscaler.yaml b/charts/kasten/k10/7.5.401/charts/prometheus/charts/prometheus-node-exporter/templates/verticalpodautoscaler.yaml new file mode 100644 index 0000000000..2c2705f872 --- /dev/null +++ b/charts/kasten/k10/7.5.401/charts/prometheus/charts/prometheus-node-exporter/templates/verticalpodautoscaler.yaml @@ -0,0 +1,40 @@ +{{- if and (.Capabilities.APIVersions.Has "autoscaling.k8s.io/v1") (.Values.verticalPodAutoscaler.enabled) }} +apiVersion: autoscaling.k8s.io/v1 +kind: VerticalPodAutoscaler +metadata: + name: {{ include "prometheus-node-exporter.fullname" . }} + namespace: {{ include "prometheus-node-exporter.namespace" . }} + labels: + {{- include "prometheus-node-exporter.labels" . | nindent 4 }} +spec: + {{- with .Values.verticalPodAutoscaler.recommenders }} + recommenders: + {{- toYaml . | nindent 4 }} + {{- end }} + resourcePolicy: + containerPolicies: + - containerName: node-exporter + {{- with .Values.verticalPodAutoscaler.controlledResources }} + controlledResources: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.verticalPodAutoscaler.controlledValues }} + controlledValues: {{ . }} + {{- end }} + {{- with .Values.verticalPodAutoscaler.maxAllowed }} + maxAllowed: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.verticalPodAutoscaler.minAllowed }} + minAllowed: + {{- toYaml . | nindent 8 }} + {{- end }} + targetRef: + apiVersion: apps/v1 + kind: DaemonSet + name: {{ include "prometheus-node-exporter.fullname" . }} + {{- with .Values.verticalPodAutoscaler.updatePolicy }} + updatePolicy: + {{- toYaml . | nindent 4 }} + {{- end }} +{{- end }} diff --git a/charts/kasten/k10/7.5.401/charts/prometheus/charts/prometheus-node-exporter/values.yaml b/charts/kasten/k10/7.5.401/charts/prometheus/charts/prometheus-node-exporter/values.yaml new file mode 100644 index 0000000000..c63448699e --- /dev/null +++ b/charts/kasten/k10/7.5.401/charts/prometheus/charts/prometheus-node-exporter/values.yaml @@ -0,0 +1,618 @@ +# Default values for prometheus-node-exporter. +# This is a YAML-formatted file. +# Declare variables to be passed into your templates. +image: + registry: quay.io + repository: prometheus/node-exporter + # Overrides the image tag whose default is {{ printf "v%s" .Chart.AppVersion }} + tag: "" + pullPolicy: IfNotPresent + digest: "" + +imagePullSecrets: [] +# - name: "image-pull-secret" +nameOverride: "" +fullnameOverride: "" + +# Number of old history to retain to allow rollback +# Default Kubernetes value is set to 10 +revisionHistoryLimit: 10 + +global: + # To help compatibility with other charts which use global.imagePullSecrets. + # Allow either an array of {name: pullSecret} maps (k8s-style), or an array of strings (more common helm-style). + # global: + # imagePullSecrets: + # - name: pullSecret1 + # - name: pullSecret2 + # or + # global: + # imagePullSecrets: + # - pullSecret1 + # - pullSecret2 + imagePullSecrets: [] + # + # Allow parent charts to override registry hostname + imageRegistry: "" + +# Configure kube-rbac-proxy. When enabled, creates a kube-rbac-proxy to protect the node-exporter http endpoint. +# The requests are served through the same service but requests are HTTPS. +kubeRBACProxy: + enabled: false + ## Set environment variables as name/value pairs + env: {} + # VARIABLE: value + image: + registry: quay.io + repository: brancz/kube-rbac-proxy + tag: v0.18.2 + sha: "" + pullPolicy: IfNotPresent + + # List of additional cli arguments to configure kube-rbac-proxy + # for example: --tls-cipher-suites, --log-file, etc. + # all the possible args can be found here: https://github.com/brancz/kube-rbac-proxy#usage + extraArgs: [] + + ## Specify security settings for a Container + ## Allows overrides and additional options compared to (Pod) securityContext + ## Ref: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/#set-the-security-context-for-a-container + containerSecurityContext: {} + + # Specify the port used for the Node exporter container (upstream port) + port: 8100 + # Specify the name of the container port + portName: http + # Configure a hostPort. If true, hostPort will be enabled in the container and set to service.port. + enableHostPort: false + + # Configure Proxy Endpoints Port + # This is the port being probed for readiness + proxyEndpointsPort: 8888 + # Configure a hostPort. If true, hostPort will be enabled in the container and set to proxyEndpointsPort. + enableProxyEndpointsHostPort: false + + resources: {} + # We usually recommend not to specify default resources and to leave this as a conscious + # choice for the user. This also increases chances charts run on environments with little + # resources, such as Minikube. If you do want to specify resources, uncomment the following + # lines, adjust them as necessary, and remove the curly braces after 'resources:'. + # limits: + # cpu: 100m + # memory: 64Mi + # requests: + # cpu: 10m + # memory: 32Mi + + ## Additional volume mounts in the kube-rbac-proxy container + ## See extraVolumes below + extraVolumeMounts: [] + # - name: extra-volume + # mountPath: /extra + # readOnly: true + + ## tls enables using TLS resources from a volume on secret referred to in tlsSecret below. + ## When enabling tlsClientAuth, client CA certificate must be set in tlsSecret.caItem. + ## Ref. https://github.com/brancz/kube-rbac-proxy/issues/187 + tls: + enabled: false + tlsClientAuth: false + +## tlsSecret refers to an existing secret holding TLS items: client CA certificate, private key and certificate. +## secretName and volumeName can be templated. +## If enabled, volume volumeName gets created on secret secretName. +## The volume's resources will be used by kube-rbac-proxy if kubeRBACProxy.tls.enabled is set. +tlsSecret: + enabled: false + ## Key with client CA certificate (optional) + caItem: "" + ## Key with certificate + certItem: tls.crt + ## Key with private key + keyItem: tls.key + ## Name of an existing secret + secretName: prometheus-node-exporter-tls + ## Name of the volume to be created + volumeName: prometheus-node-exporter-tls + +## Service configuration +service: + ## Creating a service is enabled by default + enabled: true + + ## Service type + type: ClusterIP + ## IP address for type ClusterIP + clusterIP: "" + ## Default service port. Sets the port of the exposed container as well (NE or kubeRBACProxy). + ## Use "servicePort" below if changing the service port only is desired. + port: 9100 + ## Service port. Use this field if you wish to set a different service port + ## without changing the container port ("port" above). + servicePort: "" + ## Targeted port in the pod. Must refer to an open container port ("port" or "portName"). + ## (IntOrString) + targetPort: 9100 + ## Name of the service port. Sets the port name of the main container (NE) as well. + portName: metrics + ## Port number for service type NodePort + nodePort: null + + ## If true, node exporter will listen on all interfaces + listenOnAllInterfaces: true + + ## Additional annotations and labels for the service + annotations: + prometheus.io/scrape: "true" + labels: {} + + ## Dual stack settings for the service + ## https://kubernetes.io/docs/concepts/services-networking/dual-stack/#services + ipDualStack: + enabled: false + ipFamilies: ["IPv6", "IPv4"] + ipFamilyPolicy: "PreferDualStack" + + ## External traffic policy setting (Cluster, Local) + externalTrafficPolicy: "" + +# Set a NetworkPolicy with: +# ingress only on service.port or custom policy +# no egress permitted +networkPolicy: + enabled: false + + # ingress: + # - {} + +# Additional environment variables that will be passed to the daemonset +env: {} +## env: +## VARIABLE: value + +prometheus: + monitor: + enabled: false + additionalLabels: {} + namespace: "" + + jobLabel: "" + + # List of pod labels to add to node exporter metrics + # https://github.com/prometheus-operator/prometheus-operator/blob/main/Documentation/api.md#servicemonitor + podTargetLabels: [] + + # List of target labels to add to node exporter metrics + # https://github.com/prometheus-operator/prometheus-operator/blob/main/Documentation/api.md#servicemonitor + targetLabels: [] + + scheme: http + basicAuth: {} + bearerTokenFile: + tlsConfig: {} + + ## proxyUrl: URL of a proxy that should be used for scraping. + ## + proxyUrl: "" + + ## Override serviceMonitor selector + ## + selectorOverride: {} + + ## Attach node metadata to discovered targets. Requires Prometheus v2.35.0 and above. + ## + attachMetadata: + node: false + + relabelings: [] + metricRelabelings: [] + interval: "" + scrapeTimeout: 10s + ## prometheus.monitor.apiVersion ApiVersion for the serviceMonitor Resource(defaults to "monitoring.coreos.com/v1") + apiVersion: "" + + ## SampleLimit defines per-scrape limit on number of scraped samples that will be accepted. + ## + sampleLimit: 0 + + ## TargetLimit defines a limit on the number of scraped targets that will be accepted. + ## + targetLimit: 0 + + ## Per-scrape limit on number of labels that will be accepted for a sample. Only valid in Prometheus versions 2.27.0 and newer. + ## + labelLimit: 0 + + ## Per-scrape limit on length of labels name that will be accepted for a sample. Only valid in Prometheus versions 2.27.0 and newer. + ## + labelNameLengthLimit: 0 + + ## Per-scrape limit on length of labels value that will be accepted for a sample. Only valid in Prometheus versions 2.27.0 and newer. + ## + labelValueLengthLimit: 0 + + # PodMonitor defines monitoring for a set of pods. + # ref. https://github.com/prometheus-operator/prometheus-operator/blob/main/Documentation/api.md#monitoring.coreos.com/v1.PodMonitor + # Using a PodMonitor may be preferred in some environments where there is very large number + # of Node Exporter endpoints (1000+) behind a single service. + # The PodMonitor is disabled by default. When switching from ServiceMonitor to PodMonitor, + # the time series resulting from the configuration through PodMonitor may have different labels. + # For instance, there will not be the service label any longer which might + # affect PromQL queries selecting that label. + podMonitor: + enabled: false + # Namespace in which to deploy the pod monitor. Defaults to the release namespace. + namespace: "" + # Additional labels, e.g. setting a label for pod monitor selector as set in prometheus + additionalLabels: {} + # release: kube-prometheus-stack + # PodTargetLabels transfers labels of the Kubernetes Pod onto the target. + podTargetLabels: [] + # apiVersion defaults to monitoring.coreos.com/v1. + apiVersion: "" + # Override pod selector to select pod objects. + selectorOverride: {} + # Attach node metadata to discovered targets. Requires Prometheus v2.35.0 and above. + attachMetadata: + node: false + # The label to use to retrieve the job name from. Defaults to label app.kubernetes.io/name. + jobLabel: "" + + # Scheme/protocol to use for scraping. + scheme: "http" + # Path to scrape metrics at. + path: "/metrics" + + # BasicAuth allow an endpoint to authenticate over basic authentication. + # More info: https://prometheus.io/docs/operating/configuration/#endpoint + basicAuth: {} + # Secret to mount to read bearer token for scraping targets. + # The secret needs to be in the same namespace as the pod monitor and accessible by the Prometheus Operator. + # https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.24/#secretkeyselector-v1-core + bearerTokenSecret: {} + # TLS configuration to use when scraping the endpoint. + tlsConfig: {} + # Authorization section for this endpoint. + # https://github.com/prometheus-operator/prometheus-operator/blob/main/Documentation/api.md#monitoring.coreos.com/v1.SafeAuthorization + authorization: {} + # OAuth2 for the URL. Only valid in Prometheus versions 2.27.0 and newer. + # https://github.com/prometheus-operator/prometheus-operator/blob/main/Documentation/api.md#monitoring.coreos.com/v1.OAuth2 + oauth2: {} + + # ProxyURL eg http://proxyserver:2195. Directs scrapes through proxy to this endpoint. + proxyUrl: "" + # Interval at which endpoints should be scraped. If not specified Prometheus’ global scrape interval is used. + interval: "" + # Timeout after which the scrape is ended. If not specified, the Prometheus global scrape interval is used. + scrapeTimeout: "" + # HonorTimestamps controls whether Prometheus respects the timestamps present in scraped data. + honorTimestamps: true + # HonorLabels chooses the metric’s labels on collisions with target labels. + honorLabels: true + # Whether to enable HTTP2. Default false. + enableHttp2: "" + # Drop pods that are not running. (Failed, Succeeded). + # Enabled by default. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle/#pod-phase + filterRunning: "" + # FollowRedirects configures whether scrape requests follow HTTP 3xx redirects. Default false. + followRedirects: "" + # Optional HTTP URL parameters + params: {} + + # RelabelConfigs to apply to samples before scraping. Prometheus Operator automatically adds + # relabelings for a few standard Kubernetes fields. The original scrape job’s name + # is available via the __tmp_prometheus_job_name label. + # More info: https://prometheus.io/docs/prometheus/latest/configuration/configuration/#relabel_config + relabelings: [] + # MetricRelabelConfigs to apply to samples before ingestion. + metricRelabelings: [] + + # SampleLimit defines per-scrape limit on number of scraped samples that will be accepted. + sampleLimit: 0 + # TargetLimit defines a limit on the number of scraped targets that will be accepted. + targetLimit: 0 + # Per-scrape limit on number of labels that will be accepted for a sample. + # Only valid in Prometheus versions 2.27.0 and newer. + labelLimit: 0 + # Per-scrape limit on length of labels name that will be accepted for a sample. + # Only valid in Prometheus versions 2.27.0 and newer. + labelNameLengthLimit: 0 + # Per-scrape limit on length of labels value that will be accepted for a sample. + # Only valid in Prometheus versions 2.27.0 and newer. + labelValueLengthLimit: 0 + +## Customize the updateStrategy if set +updateStrategy: + type: RollingUpdate + rollingUpdate: + maxUnavailable: 1 + +resources: {} + # We usually recommend not to specify default resources and to leave this as a conscious + # choice for the user. This also increases chances charts run on environments with little + # resources, such as Minikube. If you do want to specify resources, uncomment the following + # lines, adjust them as necessary, and remove the curly braces after 'resources:'. + # limits: + # cpu: 200m + # memory: 50Mi + # requests: + # cpu: 100m + # memory: 30Mi + +# Specify the container restart policy passed to the Node Export container +# Possible Values: Always (default)|OnFailure|Never +restartPolicy: null + +serviceAccount: + # Specifies whether a ServiceAccount should be created + create: true + # The name of the ServiceAccount to use. + # If not set and create is true, a name is generated using the fullname template + name: + annotations: {} + imagePullSecrets: [] + automountServiceAccountToken: false + +securityContext: + fsGroup: 65534 + runAsGroup: 65534 + runAsNonRoot: true + runAsUser: 65534 + +containerSecurityContext: + readOnlyRootFilesystem: true + # capabilities: + # add: + # - SYS_TIME + +rbac: + ## If true, create & use RBAC resources + ## + create: true + ## If true, create & use Pod Security Policy resources + ## https://kubernetes.io/docs/concepts/policy/pod-security-policy/ + pspEnabled: true + pspAnnotations: {} + +# for deployments that have node_exporter deployed outside of the cluster, list +# their addresses here +endpoints: [] + +# Expose the service to the host network +hostNetwork: true + +# Share the host process ID namespace +hostPID: true + +# Share the host ipc namespace +hostIPC: false + +# Mount the node's root file system (/) at /host/root in the container +hostRootFsMount: + enabled: true + # Defines how new mounts in existing mounts on the node or in the container + # are propagated to the container or node, respectively. Possible values are + # None, HostToContainer, and Bidirectional. If this field is omitted, then + # None is used. More information on: + # https://kubernetes.io/docs/concepts/storage/volumes/#mount-propagation + mountPropagation: HostToContainer + +# Mount the node's proc file system (/proc) at /host/proc in the container +hostProcFsMount: + # Possible values are None, HostToContainer, and Bidirectional + mountPropagation: "" + +# Mount the node's sys file system (/sys) at /host/sys in the container +hostSysFsMount: + # Possible values are None, HostToContainer, and Bidirectional + mountPropagation: "" + +## Assign a group of affinity scheduling rules +## The default nodeAffinity excludes Fargate nodes and virtual kubelets from scheduling +## unless overriden by hard node affinity set in the field. +affinity: {} +# nodeAffinity: +# requiredDuringSchedulingIgnoredDuringExecution: +# nodeSelectorTerms: +# - matchFields: +# - key: metadata.name +# operator: In +# values: +# - target-host-name + +# Annotations to be added to node exporter pods +podAnnotations: + # Fix for very slow GKE cluster upgrades + cluster-autoscaler.kubernetes.io/safe-to-evict: "true" + +# Extra labels to add to node exporter pods (can be templated) +podLabels: {} + +## Extra labels to attach to all resources (can be templated) +commonLabels: {} + +# Annotations to be added to node exporter daemonset +daemonsetAnnotations: {} + +## set to true to add the release label so scraping of the servicemonitor with kube-prometheus-stack works out of the box +releaseLabel: false + +# Custom DNS configuration to be added to prometheus-node-exporter pods +dnsConfig: {} +# nameservers: +# - 1.2.3.4 +# searches: +# - ns1.svc.cluster-domain.example +# - my.dns.search.suffix +# options: +# - name: ndots +# value: "2" +# - name: edns0 + +## Assign a nodeSelector if operating a hybrid cluster +## +nodeSelector: + kubernetes.io/os: linux + # kubernetes.io/arch: amd64 + +# Specify grace period for graceful termination of pods. Defaults to 30 if null or not specified +terminationGracePeriodSeconds: null + +tolerations: + - effect: NoSchedule + operator: Exists + +# Enable or disable container termination message settings +# https://kubernetes.io/docs/tasks/debug/debug-application/determine-reason-pod-failure/ +terminationMessageParams: + enabled: false + # If enabled, specify the path for termination messages + terminationMessagePath: /dev/termination-log + # If enabled, specify the policy for termination messages + terminationMessagePolicy: File + + +## Assign a PriorityClassName to pods if set +# priorityClassName: "" + +## Additional container arguments +## +extraArgs: [] +# - --collector.diskstats.ignored-devices=^(ram|loop|fd|(h|s|v)d[a-z]|nvme\\d+n\\d+p)\\d+$ +# - --collector.textfile.directory=/run/prometheus + +## Additional mounts from the host to node-exporter container +## +extraHostVolumeMounts: [] +# - name: +# hostPath: +# https://kubernetes.io/docs/concepts/storage/volumes/#hostpath-volume-types +# type: "" (Default)|DirectoryOrCreate|Directory|FileOrCreate|File|Socket|CharDevice|BlockDevice +# mountPath: +# readOnly: true|false +# mountPropagation: None|HostToContainer|Bidirectional + +## Additional configmaps to be mounted. +## +configmaps: [] +# - name: +# mountPath: + +secrets: [] +# - name: +# mountPath: + +## Override the deployment namespace +## +namespaceOverride: "" + +## Additional containers for export metrics to text file; fields image,imagePullPolicy,securityContext take default value from main container +## +sidecars: [] +# - name: nvidia-dcgm-exporter +# image: nvidia/dcgm-exporter:1.4.3 +# volumeMounts: +# - name: tmp +# mountPath: /tmp + +## Volume for sidecar containers +## +sidecarVolumeMount: [] +# - name: collector-textfiles +# mountPath: /run/prometheus +# readOnly: false + +## Additional mounts from the host to sidecar containers +## +sidecarHostVolumeMounts: [] +# - name: +# hostPath: +# mountPath: +# readOnly: true|false +# mountPropagation: None|HostToContainer|Bidirectional + +## Additional InitContainers to initialize the pod +## +extraInitContainers: [] + +## Liveness probe +## +livenessProbe: + failureThreshold: 3 + httpGet: + httpHeaders: [] + scheme: http + initialDelaySeconds: 0 + periodSeconds: 10 + successThreshold: 1 + timeoutSeconds: 1 + +## Readiness probe +## +readinessProbe: + failureThreshold: 3 + httpGet: + httpHeaders: [] + scheme: http + initialDelaySeconds: 0 + periodSeconds: 10 + successThreshold: 1 + timeoutSeconds: 1 + +# Enable vertical pod autoscaler support for prometheus-node-exporter +verticalPodAutoscaler: + enabled: false + + # Recommender responsible for generating recommendation for the object. + # List should be empty (then the default recommender will generate the recommendation) + # or contain exactly one recommender. + # recommenders: + # - name: custom-recommender-performance + + # List of resources that the vertical pod autoscaler can control. Defaults to cpu and memory + controlledResources: [] + # Specifies which resource values should be controlled: RequestsOnly or RequestsAndLimits. + # controlledValues: RequestsAndLimits + + # Define the max allowed resources for the pod + maxAllowed: {} + # cpu: 200m + # memory: 100Mi + # Define the min allowed resources for the pod + minAllowed: {} + # cpu: 200m + # memory: 100Mi + + # updatePolicy: + # Specifies minimal number of replicas which need to be alive for VPA Updater to attempt pod eviction + # minReplicas: 1 + # Specifies whether recommended updates are applied when a Pod is started and whether recommended updates + # are applied during the life of a Pod. Possible values are "Off", "Initial", "Recreate", and "Auto". + # updateMode: Auto + +# Extra manifests to deploy as an array +extraManifests: [] + # - | + # apiVersion: v1 + # kind: ConfigMap + # metadata: + # name: prometheus-extra + # data: + # extra-data: "value" + +## Extra volumes to become available in the pod +extraVolumes: [] + # - name: extra-volume + # secret: + # defaultMode: 420 + # optional: false + # secretName: node-exporter-secret + +## Extra volume mounts in the node-exporter container +extraVolumeMounts: [] + # - name: extra-volume + # mountPath: /extra + # readOnly: true + +# Override version of app, required if image.tag is defined and does not follow semver +version: "" diff --git a/charts/kasten/k10/7.5.401/charts/prometheus/charts/prometheus-pushgateway/.helmignore b/charts/kasten/k10/7.5.401/charts/prometheus/charts/prometheus-pushgateway/.helmignore new file mode 100644 index 0000000000..e90c9f6d23 --- /dev/null +++ b/charts/kasten/k10/7.5.401/charts/prometheus/charts/prometheus-pushgateway/.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 + +# OWNERS file for Kubernetes +OWNERS \ No newline at end of file diff --git a/charts/kasten/k10/7.5.401/charts/prometheus/charts/prometheus-pushgateway/Chart.yaml b/charts/kasten/k10/7.5.401/charts/prometheus/charts/prometheus-pushgateway/Chart.yaml new file mode 100644 index 0000000000..c9f97c31fd --- /dev/null +++ b/charts/kasten/k10/7.5.401/charts/prometheus/charts/prometheus-pushgateway/Chart.yaml @@ -0,0 +1,24 @@ +annotations: + artifacthub.io/license: Apache-2.0 + artifacthub.io/links: | + - name: Chart Source + url: https://github.com/prometheus-community/helm-charts +apiVersion: v2 +appVersion: v1.10.0 +description: A Helm chart for prometheus pushgateway +home: https://github.com/prometheus/pushgateway +keywords: +- pushgateway +- prometheus +maintainers: +- email: gianrubio@gmail.com + name: gianrubio +- email: christian.staude@staffbase.com + name: cstaud +- email: rootsandtrees@posteo.de + name: zeritti +name: prometheus-pushgateway +sources: +- https://github.com/prometheus/pushgateway +type: application +version: 2.16.0 diff --git a/charts/kasten/k10/7.5.401/charts/prometheus/charts/prometheus-pushgateway/README.md b/charts/kasten/k10/7.5.401/charts/prometheus/charts/prometheus-pushgateway/README.md new file mode 100644 index 0000000000..cc6645fdf9 --- /dev/null +++ b/charts/kasten/k10/7.5.401/charts/prometheus/charts/prometheus-pushgateway/README.md @@ -0,0 +1,88 @@ +# Prometheus Pushgateway + +This chart bootstraps a Prometheus [Pushgateway](http://github.com/prometheus/pushgateway) deployment on a [Kubernetes](http://kubernetes.io) cluster using the [Helm](https://helm.sh) package manager. + +An optional prometheus `ServiceMonitor` can be enabled, should you wish to use this gateway with [Prometheus Operator](https://github.com/coreos/prometheus-operator). + +## Get Repository Info + +```console +helm repo add prometheus-community https://prometheus-community.github.io/helm-charts +helm repo update +``` + +_See [helm repo](https://helm.sh/docs/helm/helm_repo/) for command documentation._ + +## Install Chart + +```console +helm install [RELEASE_NAME] prometheus-community/prometheus-pushgateway +``` + +_See [configuration](#configuration) below._ + +_See [helm install](https://helm.sh/docs/helm/helm_install/) for command documentation._ + +## Uninstall Chart + +```console +helm uninstall [RELEASE_NAME] +``` + +This removes all the Kubernetes components associated with the chart and deletes the release. + +_See [helm uninstall](https://helm.sh/docs/helm/helm_uninstall/) for command documentation._ + +## Upgrading Chart + +```console +helm upgrade [RELEASE_NAME] prometheus-community/prometheus-pushgateway --install +``` + +_See [helm upgrade](https://helm.sh/docs/helm/helm_upgrade/) for command documentation._ + +### To 2.0.0 + +Chart API version has been upgraded to v2 so Helm 3 is needed from now on. + +Docker image tag is used from Chart.yaml appVersion field by default now. + +Version 2.0.0 also adapted [Helm label and annotation best practices](https://helm.sh/docs/chart_best_practices/labels/). Specifically, labels mapping is listed below: + +```console +OLD => NEW +---------------------------------------- +heritage => app.kubernetes.io/managed-by +chart => helm.sh/chart +[container version] => app.kubernetes.io/version +app => app.kubernetes.io/name +release => app.kubernetes.io/instance +``` + +Therefore, depending on the way you've configured the chart, the previous StatefulSet or Deployment need to be deleted before upgrade. + +If `runAsStatefulSet: false` (this is the default): + +```console +kubectl delete deploy -l app=prometheus-pushgateway +``` + +If `runAsStatefulSet: true`: + +```console +kubectl delete sts -l app=prometheus-pushgateway +``` + +After that do the actual upgrade: + +```console +helm upgrade -i prometheus-pushgateway prometheus-community/prometheus-pushgateway +``` + +## Configuration + +See [Customizing the Chart Before Installing](https://helm.sh/docs/intro/using_helm/#customizing-the-chart-before-installing). To see all configurable options with detailed comments, visit the chart's [values.yaml](./values.yaml), or run these configuration commands: + +```console +helm show values prometheus-community/prometheus-pushgateway +``` diff --git a/charts/kasten/k10/7.5.401/charts/prometheus/charts/prometheus-pushgateway/templates/NOTES.txt b/charts/kasten/k10/7.5.401/charts/prometheus/charts/prometheus-pushgateway/templates/NOTES.txt new file mode 100644 index 0000000000..263b1d8d49 --- /dev/null +++ b/charts/kasten/k10/7.5.401/charts/prometheus/charts/prometheus-pushgateway/templates/NOTES.txt @@ -0,0 +1,19 @@ +1. Get the application URL by running these commands: +{{- if .Values.ingress.enabled }} +{{- range .Values.ingress.hosts }} + http{{ if $.Values.ingress.tls }}s{{ end }}://{{ . }}{{ $.Values.ingress.path }} +{{- end }} +{{- else if contains "NodePort" .Values.service.type }} + export NODE_PORT=$(kubectl get --namespace {{ template "prometheus-pushgateway.namespace" . }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ template "prometheus-pushgateway.fullname" . }}) + export NODE_IP=$(kubectl get nodes --namespace {{ template "prometheus-pushgateway.namespace" . }} -o jsonpath="{.items[0].status.addresses[0].address}") + echo http://$NODE_IP:$NODE_PORT +{{- else if contains "LoadBalancer" .Values.service.type }} + NOTE: It may take a few minutes for the LoadBalancer IP to be available. + You can watch the status of by running 'kubectl get svc -w {{ template "prometheus-pushgateway.fullname" . }}' + export SERVICE_IP=$(kubectl get svc --namespace {{ template "prometheus-pushgateway.namespace" . }} {{ template "prometheus-pushgateway.fullname" . }} -o jsonpath='{.status.loadBalancer.ingress[0].ip}') + echo http://$SERVICE_IP:{{ .Values.service.port }} +{{- else if contains "ClusterIP" .Values.service.type }} + export POD_NAME=$(kubectl get pods --namespace {{ template "prometheus-pushgateway.namespace" . }} -l "app.kubernetes.io/name={{ template "prometheus-pushgateway.name" . }},app.kubernetes.io/instance={{ .Release.Name }}" -o jsonpath="{.items[0].metadata.name}") + kubectl port-forward $POD_NAME 9091 + echo "Visit http://127.0.0.1:9091 to use your application" +{{- end }} diff --git a/charts/kasten/k10/7.5.401/charts/prometheus/charts/prometheus-pushgateway/templates/_helpers.tpl b/charts/kasten/k10/7.5.401/charts/prometheus/charts/prometheus-pushgateway/templates/_helpers.tpl new file mode 100644 index 0000000000..2d1f84056a --- /dev/null +++ b/charts/kasten/k10/7.5.401/charts/prometheus/charts/prometheus-pushgateway/templates/_helpers.tpl @@ -0,0 +1,307 @@ +{{/* vim: set filetype=mustache: */}} +{{/* +Expand the name of the chart. +*/}} +{{- define "prometheus-pushgateway.name" -}} +{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Namespace to set on the resources +*/}} +{{- define "prometheus-pushgateway.namespace" -}} + {{- if .Values.namespaceOverride -}} + {{- .Values.namespaceOverride -}} + {{- else -}} + {{- .Release.Namespace -}} + {{- end -}} +{{- end -}} + +{{/* +Create a default fully qualified app name. +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +If release name contains chart name it will be used as a full name. +*/}} +{{- define "prometheus-pushgateway.fullname" -}} +{{- if .Values.fullnameOverride }} +{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- $name := default .Chart.Name .Values.nameOverride }} +{{- if contains $name .Release.Name }} +{{- .Release.Name | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }} +{{- end }} +{{- end }} +{{- end }} + +{{/* +Create chart name and version as used by the chart label. +*/}} +{{- define "prometheus-pushgateway.chart" -}} +{{ printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Create the name of the service account to use +*/}} +{{- define "prometheus-pushgateway.serviceAccountName" -}} +{{- if .Values.serviceAccount.create }} +{{- default (include "prometheus-pushgateway.fullname" .) .Values.serviceAccount.name }} +{{- else }} +{{- default "default" .Values.serviceAccount.name }} +{{- end }} +{{- end }} + +{{/* +Create default labels +*/}} +{{- define "prometheus-pushgateway.defaultLabels" -}} +helm.sh/chart: {{ include "prometheus-pushgateway.chart" . }} +{{ include "prometheus-pushgateway.selectorLabels" . }} +{{- if .Chart.AppVersion }} +app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} +{{- end }} +app.kubernetes.io/managed-by: {{ .Release.Service }} +{{- with .Values.podLabels }} +{{ toYaml . }} +{{- end }} +{{- end }} + +{{/* +Selector labels +*/}} +{{- define "prometheus-pushgateway.selectorLabels" -}} +app.kubernetes.io/name: {{ include "prometheus-pushgateway.name" . }} +app.kubernetes.io/instance: {{ .Release.Name }} +{{- end }} + +{{/* +Return the appropriate apiVersion for networkpolicy. +*/}} +{{- define "prometheus-pushgateway.networkPolicy.apiVersion" -}} +{{- if semverCompare ">=1.4-0, <1.7-0" .Capabilities.KubeVersion.GitVersion }} +{{- print "extensions/v1beta1" }} +{{- else if semverCompare "^1.7-0" .Capabilities.KubeVersion.GitVersion }} +{{- print "networking.k8s.io/v1" }} +{{- end }} +{{- end }} + +{{/* +Define PDB apiVersion +*/}} +{{- define "prometheus-pushgateway.pdb.apiVersion" -}} +{{- if $.Capabilities.APIVersions.Has "policy/v1/PodDisruptionBudget" }} +{{- print "policy/v1" }} +{{- else }} +{{- print "policy/v1beta1" }} +{{- end }} +{{- end }} + +{{/* +Define Ingress apiVersion +*/}} +{{- define "prometheus-pushgateway.ingress.apiVersion" -}} +{{- if semverCompare ">=1.19-0" .Capabilities.KubeVersion.GitVersion }} +{{- print "networking.k8s.io/v1" }} +{{- else if semverCompare ">=1.14-0" .Capabilities.KubeVersion.GitVersion }} +{{- print "networking.k8s.io/v1beta1" }} +{{- else }} +{{- print "extensions/v1beta1" }} +{{- end }} +{{- end }} + +{{/* +Define webConfiguration +*/}} +{{- define "prometheus-pushgateway.webConfiguration" -}} +basic_auth_users: +{{- range $k, $v := .Values.webConfiguration.basicAuthUsers }} + {{ $k }}: {{ htpasswd "" $v | trimPrefix ":"}} +{{- end }} +{{- end }} + +{{/* +Define Authorization +*/}} +{{- define "prometheus-pushgateway.Authorization" -}} +{{- $users := keys .Values.webConfiguration.basicAuthUsers }} +{{- $user := first $users }} +{{- $password := index .Values.webConfiguration.basicAuthUsers $user }} +{{- $user }}:{{ $password }} +{{- end }} + +{{/* +Returns pod spec +*/}} +{{- define "prometheus-pushgateway.podSpec" -}} +serviceAccountName: {{ include "prometheus-pushgateway.serviceAccountName" . }} +automountServiceAccountToken: {{ .Values.automountServiceAccountToken }} +{{- with .Values.priorityClassName }} +priorityClassName: {{ . | quote }} +{{- end }} +{{- with .Values.hostAliases }} +hostAliases: +{{- toYaml . | nindent 2 }} +{{- end }} +{{- with .Values.imagePullSecrets }} +imagePullSecrets: + {{- toYaml . | nindent 2 }} +{{- end }} +{{- with .Values.extraInitContainers }} +initContainers: + {{- toYaml . | nindent 2 }} +{{- end }} +containers: + {{- with .Values.extraContainers }} + {{- toYaml . | nindent 2 }} + {{- end }} + - name: pushgateway + image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}" + imagePullPolicy: {{ .Values.image.pullPolicy }} + {{- with .Values.extraVars }} + env: + {{- toYaml . | nindent 6 }} + {{- end }} + {{- if or .Values.extraArgs .Values.webConfiguration }} + args: + {{- with .Values.extraArgs }} + {{- toYaml . | nindent 6 }} + {{- end }} + {{- if .Values.webConfiguration }} + - --web.config.file=/etc/config/web-config.yaml + {{- end }} + {{- end }} + ports: + - name: metrics + containerPort: 9091 + protocol: TCP + {{- if .Values.liveness.enabled }} + {{- $livenessCommon := omit .Values.liveness.probe "httpGet" }} + livenessProbe: + {{- with .Values.liveness.probe }} + httpGet: + path: {{ .httpGet.path }} + port: {{ .httpGet.port }} + {{- if or .httpGet.httpHeaders $.Values.webConfiguration.basicAuthUsers }} + httpHeaders: + {{- if $.Values.webConfiguration.basicAuthUsers }} + - name: Authorization + value: Basic {{ include "prometheus-pushgateway.Authorization" $ | b64enc }} + {{- end }} + {{- with .httpGet.httpHeaders }} + {{- toYaml . | nindent 10 }} + {{- end }} + {{- end }} + {{- toYaml $livenessCommon | nindent 6 }} + {{- end }} + {{- end }} + {{- if .Values.readiness.enabled }} + {{- $readinessCommon := omit .Values.readiness.probe "httpGet" }} + readinessProbe: + {{- with .Values.readiness.probe }} + httpGet: + path: {{ .httpGet.path }} + port: {{ .httpGet.port }} + {{- if or .httpGet.httpHeaders $.Values.webConfiguration.basicAuthUsers }} + httpHeaders: + {{- if $.Values.webConfiguration.basicAuthUsers }} + - name: Authorization + value: Basic {{ include "prometheus-pushgateway.Authorization" $ | b64enc }} + {{- end }} + {{- with .httpGet.httpHeaders }} + {{- toYaml . | nindent 10 }} + {{- end }} + {{- end }} + {{- toYaml $readinessCommon | nindent 6 }} + {{- end }} + {{- end }} + {{- with .Values.resources }} + resources: + {{- toYaml . | nindent 6 }} + {{- end }} + {{- with .Values.containerSecurityContext }} + securityContext: + {{- toYaml . | nindent 6 }} + {{- end }} + {{- with .Values.lifecycle }} + lifecycle: {{ toYaml . | nindent 6 }} + {{- end }} + volumeMounts: + - name: storage-volume + mountPath: "{{ .Values.persistentVolume.mountPath }}" + subPath: "{{ .Values.persistentVolume.subPath }}" + {{- if .Values.webConfiguration }} + - name: web-config + mountPath: "/etc/config" + {{- end }} + {{- with .Values.extraVolumeMounts }} + {{- toYaml . | nindent 6 }} + {{- end }} +{{- with .Values.nodeSelector }} +nodeSelector: + {{- toYaml . | nindent 2 }} +{{- end }} +{{- with .Values.tolerations }} +tolerations: + {{- toYaml . | nindent 2 }} +{{- end }} +{{- if or .Values.podAntiAffinity .Values.affinity }} +affinity: +{{- end }} + {{- with .Values.affinity }} + {{- toYaml . | nindent 2 }} + {{- end }} + {{- if eq .Values.podAntiAffinity "hard" }} + podAntiAffinity: + requiredDuringSchedulingIgnoredDuringExecution: + - topologyKey: {{ .Values.podAntiAffinityTopologyKey }} + labelSelector: + matchExpressions: + - {key: app.kubernetes.io/name, operator: In, values: [{{ include "prometheus-pushgateway.name" . }}]} + {{- else if eq .Values.podAntiAffinity "soft" }} + podAntiAffinity: + preferredDuringSchedulingIgnoredDuringExecution: + - weight: 100 + podAffinityTerm: + topologyKey: {{ .Values.podAntiAffinityTopologyKey }} + labelSelector: + matchExpressions: + - {key: app.kubernetes.io/name, operator: In, values: [{{ include "prometheus-pushgateway.name" . }}]} + {{- end }} +{{- with .Values.topologySpreadConstraints }} +topologySpreadConstraints: + {{- toYaml . | nindent 2 }} +{{- end }} +{{- with .Values.securityContext }} +securityContext: + {{- toYaml . | nindent 2 }} +{{- end }} +volumes: + {{- $storageVolumeAsPVCTemplate := and .Values.runAsStatefulSet .Values.persistentVolume.enabled -}} + {{- if not $storageVolumeAsPVCTemplate }} + - name: storage-volume + {{- if .Values.persistentVolume.enabled }} + persistentVolumeClaim: + claimName: {{ if .Values.persistentVolume.existingClaim }}{{ .Values.persistentVolume.existingClaim }}{{- else }}{{ include "prometheus-pushgateway.fullname" . }}{{- end }} + {{- else }} + emptyDir: {} + {{- end }} + {{- if .Values.webConfiguration }} + - name: web-config + secret: + secretName: {{ include "prometheus-pushgateway.fullname" . }} + {{- end }} + {{- end }} + {{- if .Values.extraVolumes }} + {{- toYaml .Values.extraVolumes | nindent 2 }} + {{- else if $storageVolumeAsPVCTemplate }} + {{- if .Values.webConfiguration }} + - name: web-config + secret: + secretName: {{ include "prometheus-pushgateway.fullname" . }} + {{- else }} + [] + {{- end }} + {{- end }} +{{- end }} diff --git a/charts/kasten/k10/7.5.401/charts/prometheus/charts/prometheus-pushgateway/templates/deployment.yaml b/charts/kasten/k10/7.5.401/charts/prometheus/charts/prometheus-pushgateway/templates/deployment.yaml new file mode 100644 index 0000000000..5fab383f28 --- /dev/null +++ b/charts/kasten/k10/7.5.401/charts/prometheus/charts/prometheus-pushgateway/templates/deployment.yaml @@ -0,0 +1,31 @@ +{{- if not .Values.runAsStatefulSet }} +apiVersion: apps/v1 +kind: Deployment +metadata: + labels: + {{- include "prometheus-pushgateway.defaultLabels" . | nindent 4 }} + name: {{ include "prometheus-pushgateway.fullname" . }} + namespace: {{ template "prometheus-pushgateway.namespace" . }} +spec: + replicas: {{ .Values.replicaCount }} + {{- with .Values.strategy }} + strategy: + {{- toYaml . | nindent 4 }} + {{- end }} + selector: + matchLabels: + {{- include "prometheus-pushgateway.selectorLabels" . | nindent 6 }} + template: + metadata: + {{- with .Values.podAnnotations }} + annotations: + {{- toYaml . | nindent 8 }} + {{- end }} + labels: + {{- include "prometheus-pushgateway.defaultLabels" . | nindent 8 }} + {{- if ( .Values.global | default dict ).azMarketPlace }} + azure-extensions-usage-release-identifier: {{.Release.Name}} + {{- end }} + spec: + {{- include "prometheus-pushgateway.podSpec" . | nindent 6 }} +{{- end }} diff --git a/charts/kasten/k10/7.5.401/charts/prometheus/charts/prometheus-pushgateway/templates/extra-manifests.yaml b/charts/kasten/k10/7.5.401/charts/prometheus/charts/prometheus-pushgateway/templates/extra-manifests.yaml new file mode 100644 index 0000000000..bafee95185 --- /dev/null +++ b/charts/kasten/k10/7.5.401/charts/prometheus/charts/prometheus-pushgateway/templates/extra-manifests.yaml @@ -0,0 +1,8 @@ +{{- range .Values.extraManifests }} +--- + {{- if typeIs "string" . }} + {{- tpl . $ }} + {{- else }} + {{- tpl (. | toYaml | nindent 0) $ }} + {{- end }} +{{- end }} diff --git a/charts/kasten/k10/7.5.401/charts/prometheus/charts/prometheus-pushgateway/templates/ingress.yaml b/charts/kasten/k10/7.5.401/charts/prometheus/charts/prometheus-pushgateway/templates/ingress.yaml new file mode 100644 index 0000000000..237ac4a121 --- /dev/null +++ b/charts/kasten/k10/7.5.401/charts/prometheus/charts/prometheus-pushgateway/templates/ingress.yaml @@ -0,0 +1,50 @@ +{{- if .Values.ingress.enabled }} +{{- $serviceName := include "prometheus-pushgateway.fullname" . }} +{{- $servicePort := .Values.service.port }} +{{- $ingressPath := .Values.ingress.path }} +{{- $ingressClassName := .Values.ingress.className }} +{{- $ingressPathType := .Values.ingress.pathType }} +{{- $extraPaths := .Values.ingress.extraPaths }} +apiVersion: {{ include "prometheus-pushgateway.ingress.apiVersion" . }} +kind: Ingress +metadata: + {{- with .Values.ingress.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} + labels: + {{- include "prometheus-pushgateway.defaultLabels" . | nindent 4 }} + name: {{ include "prometheus-pushgateway.fullname" . }} + namespace: {{ template "prometheus-pushgateway.namespace" . }} +spec: + {{- if semverCompare ">=1.19-0" .Capabilities.KubeVersion.GitVersion }} + ingressClassName: {{ $ingressClassName }} + {{- end }} + rules: + {{- range $host := .Values.ingress.hosts }} + - host: {{ $host }} + http: + paths: + {{- with $extraPaths }} + {{- toYaml . | nindent 10 }} + {{- end }} + - path: {{ $ingressPath }} + {{- if semverCompare ">=1.19-0" $.Capabilities.KubeVersion.GitVersion }} + pathType: {{ $ingressPathType }} + {{- end }} + backend: + {{- if semverCompare ">=1.19-0" $.Capabilities.KubeVersion.GitVersion }} + service: + name: {{ $serviceName }} + port: + number: {{ $servicePort }} + {{- else }} + serviceName: {{ $serviceName }} + servicePort: {{ $servicePort }} + {{- end }} + {{- end -}} + {{- with .Values.ingress.tls }} + tls: + {{- toYaml . | nindent 4 }} + {{- end }} +{{- end }} diff --git a/charts/kasten/k10/7.5.401/charts/prometheus/charts/prometheus-pushgateway/templates/networkpolicy.yaml b/charts/kasten/k10/7.5.401/charts/prometheus/charts/prometheus-pushgateway/templates/networkpolicy.yaml new file mode 100644 index 0000000000..d3b8019e3f --- /dev/null +++ b/charts/kasten/k10/7.5.401/charts/prometheus/charts/prometheus-pushgateway/templates/networkpolicy.yaml @@ -0,0 +1,26 @@ +{{- if .Values.networkPolicy }} +apiVersion: {{ include "prometheus-pushgateway.networkPolicy.apiVersion" . }} +kind: NetworkPolicy +metadata: + labels: + {{- include "prometheus-pushgateway.defaultLabels" . | nindent 4 }} + {{- if .Values.networkPolicy.customSelectors }} + name: ingress-allow-customselector-{{ template "prometheus-pushgateway.name" . }} + {{- else if .Values.networkPolicy.allowAll }} + name: ingress-allow-all-{{ template "prometheus-pushgateway.name" . }} + {{- else -}} + {{- fail "One of `allowAll` or `customSelectors` must be specified." }} + {{- end }} + namespace: {{ template "prometheus-pushgateway.namespace" . }} +spec: + podSelector: + matchLabels: + {{- include "prometheus-pushgateway.selectorLabels" . | nindent 6 }} + ingress: + - ports: + - port: {{ .Values.service.targetPort }} + {{- with .Values.networkPolicy.customSelectors }} + from: + {{- toYaml . | nindent 8 }} + {{- end }} +{{- end }} diff --git a/charts/kasten/k10/7.5.401/charts/prometheus/charts/prometheus-pushgateway/templates/pdb.yaml b/charts/kasten/k10/7.5.401/charts/prometheus/charts/prometheus-pushgateway/templates/pdb.yaml new file mode 100644 index 0000000000..6051133c68 --- /dev/null +++ b/charts/kasten/k10/7.5.401/charts/prometheus/charts/prometheus-pushgateway/templates/pdb.yaml @@ -0,0 +1,14 @@ +{{- if .Values.podDisruptionBudget }} +apiVersion: {{ include "prometheus-pushgateway.pdb.apiVersion" . }} +kind: PodDisruptionBudget +metadata: + labels: + {{- include "prometheus-pushgateway.defaultLabels" . | nindent 4 }} + name: {{ include "prometheus-pushgateway.fullname" . }} + namespace: {{ template "prometheus-pushgateway.namespace" . }} +spec: + selector: + matchLabels: + {{- include "prometheus-pushgateway.selectorLabels" . | nindent 6 }} + {{- toYaml .Values.podDisruptionBudget | nindent 2 }} +{{- end }} diff --git a/charts/kasten/k10/7.5.401/charts/prometheus/charts/prometheus-pushgateway/templates/pushgateway-pvc.yaml b/charts/kasten/k10/7.5.401/charts/prometheus/charts/prometheus-pushgateway/templates/pushgateway-pvc.yaml new file mode 100644 index 0000000000..d2a85f4242 --- /dev/null +++ b/charts/kasten/k10/7.5.401/charts/prometheus/charts/prometheus-pushgateway/templates/pushgateway-pvc.yaml @@ -0,0 +1,29 @@ +{{- if and (not .Values.runAsStatefulSet) .Values.persistentVolume.enabled (not .Values.persistentVolume.existingClaim) }} +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + {{- with .Values.persistentVolume.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} + labels: + {{- include "prometheus-pushgateway.defaultLabels" . | nindent 4 }} + {{- with .Values.persistentVolumeLabels }} + {{- toYaml . | nindent 4 }} + {{- end }} + name: {{ include "prometheus-pushgateway.fullname" . }} + namespace: {{ template "prometheus-pushgateway.namespace" . }} +spec: + accessModes: + {{- toYaml .Values.persistentVolume.accessModes | nindent 4 }} + {{- if .Values.persistentVolume.storageClass }} + {{- if (eq "-" .Values.persistentVolume.storageClass) }} + storageClassName: "" + {{- else }} + storageClassName: "{{ .Values.persistentVolume.storageClass }}" + {{- end }} + {{- end }} + resources: + requests: + storage: "{{ .Values.persistentVolume.size }}" +{{- end }} diff --git a/charts/kasten/k10/7.5.401/charts/prometheus/charts/prometheus-pushgateway/templates/secret.yaml b/charts/kasten/k10/7.5.401/charts/prometheus/charts/prometheus-pushgateway/templates/secret.yaml new file mode 100644 index 0000000000..a8142d1389 --- /dev/null +++ b/charts/kasten/k10/7.5.401/charts/prometheus/charts/prometheus-pushgateway/templates/secret.yaml @@ -0,0 +1,10 @@ +{{- if .Values.webConfiguration }} +apiVersion: v1 +kind: Secret +metadata: + name: {{ include "prometheus-pushgateway.fullname" . }} + labels: + {{- include "prometheus-pushgateway.defaultLabels" . | nindent 4 }} +data: + web-config.yaml: {{ include "prometheus-pushgateway.webConfiguration" . | b64enc}} +{{- end }} diff --git a/charts/kasten/k10/7.5.401/charts/prometheus/charts/prometheus-pushgateway/templates/service.yaml b/charts/kasten/k10/7.5.401/charts/prometheus/charts/prometheus-pushgateway/templates/service.yaml new file mode 100644 index 0000000000..15029f7e30 --- /dev/null +++ b/charts/kasten/k10/7.5.401/charts/prometheus/charts/prometheus-pushgateway/templates/service.yaml @@ -0,0 +1,45 @@ +{{- $stsNoHeadlessSvcTypes := list "LoadBalancer" "NodePort" -}} +apiVersion: v1 +kind: Service +metadata: + {{- with .Values.serviceAnnotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} + labels: + {{- include "prometheus-pushgateway.defaultLabels" . | nindent 4 }} + {{- with .Values.serviceLabels }} + {{- toYaml . | nindent 4 }} + {{- end }} + name: {{ include "prometheus-pushgateway.fullname" . }} + namespace: {{ template "prometheus-pushgateway.namespace" . }} +spec: + {{- if .Values.service.clusterIP }} + clusterIP: {{ .Values.service.clusterIP }} + {{ else if and .Values.runAsStatefulSet (not (has .Values.service.type $stsNoHeadlessSvcTypes)) }} + clusterIP: None # Headless service + {{- end }} + {{- if .Values.service.ipDualStack.enabled }} + ipFamilies: {{ toYaml .Values.service.ipDualStack.ipFamilies | nindent 4 }} + ipFamilyPolicy: {{ .Values.service.ipDualStack.ipFamilyPolicy }} + {{- end }} + type: {{ .Values.service.type }} + {{- with .Values.service.loadBalancerIP }} + loadBalancerIP: {{ . }} + {{- end }} + {{- if .Values.service.loadBalancerSourceRanges }} + loadBalancerSourceRanges: + {{- range $cidr := .Values.service.loadBalancerSourceRanges }} + - {{ $cidr }} + {{- end }} + {{- end }} + ports: + - port: {{ .Values.service.port }} + targetPort: {{ .Values.service.targetPort }} + {{- if and (eq .Values.service.type "NodePort") .Values.service.nodePort }} + nodePort: {{ .Values.service.nodePort }} + {{- end }} + protocol: TCP + name: {{ .Values.service.portName }} + selector: + {{- include "prometheus-pushgateway.selectorLabels" . | nindent 4 }} diff --git a/charts/kasten/k10/7.5.401/charts/prometheus/charts/prometheus-pushgateway/templates/serviceaccount.yaml b/charts/kasten/k10/7.5.401/charts/prometheus/charts/prometheus-pushgateway/templates/serviceaccount.yaml new file mode 100644 index 0000000000..88f1470480 --- /dev/null +++ b/charts/kasten/k10/7.5.401/charts/prometheus/charts/prometheus-pushgateway/templates/serviceaccount.yaml @@ -0,0 +1,17 @@ +{{- if .Values.serviceAccount.create }} +apiVersion: v1 +kind: ServiceAccount +metadata: + {{- with .Values.serviceAccount.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} + labels: + {{- include "prometheus-pushgateway.defaultLabels" . | nindent 4 }} + {{- with .Values.serviceAccountLabels }} + {{- toYaml . | nindent 4 }} + {{- end }} + name: {{ include "prometheus-pushgateway.serviceAccountName" . }} + namespace: {{ template "prometheus-pushgateway.namespace" . }} +automountServiceAccountToken: {{ .Values.automountServiceAccountToken }} +{{- end }} diff --git a/charts/kasten/k10/7.5.401/charts/prometheus/charts/prometheus-pushgateway/templates/servicemonitor.yaml b/charts/kasten/k10/7.5.401/charts/prometheus/charts/prometheus-pushgateway/templates/servicemonitor.yaml new file mode 100644 index 0000000000..ae173199b3 --- /dev/null +++ b/charts/kasten/k10/7.5.401/charts/prometheus/charts/prometheus-pushgateway/templates/servicemonitor.yaml @@ -0,0 +1,51 @@ +{{- if .Values.serviceMonitor.enabled }} +apiVersion: monitoring.coreos.com/v1 +kind: ServiceMonitor +metadata: + labels: + {{- include "prometheus-pushgateway.defaultLabels" . | nindent 4 }} + {{- if .Values.serviceMonitor.additionalLabels }} + {{- toYaml .Values.serviceMonitor.additionalLabels | nindent 4 }} + {{- end }} + name: {{ include "prometheus-pushgateway.fullname" . }} + {{- if .Values.serviceMonitor.namespace }} + namespace: {{ .Values.serviceMonitor.namespace }} + {{- else }} + namespace: {{ template "prometheus-pushgateway.namespace" . }} + {{- end }} +spec: + endpoints: + - port: {{ .Values.service.portName }} + {{- with .Values.serviceMonitor.interval }} + interval: {{ . }} + {{- end }} + {{- with .Values.serviceMonitor.scheme }} + scheme: {{ . }} + {{- end }} + {{- with .Values.serviceMonitor.bearerTokenFile }} + bearerTokenFile: {{ . }} + {{- end }} + {{- with .Values.serviceMonitor.tlsConfig }} + tlsConfig: + {{- toYaml .| nindent 6 }} + {{- end }} + {{- with .Values.serviceMonitor.scrapeTimeout }} + scrapeTimeout: {{ . }} + {{- end }} + path: {{ .Values.serviceMonitor.telemetryPath }} + honorLabels: {{ .Values.serviceMonitor.honorLabels }} + {{- with .Values.serviceMonitor.metricRelabelings }} + metricRelabelings: + {{- tpl (toYaml . | nindent 6) $ }} + {{- end }} + {{- with .Values.serviceMonitor.relabelings }} + relabelings: + {{- toYaml . | nindent 6 }} + {{- end }} + namespaceSelector: + matchNames: + - {{ template "prometheus-pushgateway.namespace" . }} + selector: + matchLabels: + {{- include "prometheus-pushgateway.selectorLabels" . | nindent 6 }} +{{- end -}} diff --git a/charts/kasten/k10/7.5.401/charts/prometheus/charts/prometheus-pushgateway/templates/statefulset.yaml b/charts/kasten/k10/7.5.401/charts/prometheus/charts/prometheus-pushgateway/templates/statefulset.yaml new file mode 100644 index 0000000000..8d486a306f --- /dev/null +++ b/charts/kasten/k10/7.5.401/charts/prometheus/charts/prometheus-pushgateway/templates/statefulset.yaml @@ -0,0 +1,49 @@ +{{- if .Values.runAsStatefulSet }} +apiVersion: apps/v1 +kind: StatefulSet +metadata: + labels: + {{- include "prometheus-pushgateway.defaultLabels" . | nindent 4 }} + name: {{ include "prometheus-pushgateway.fullname" . }} + namespace: {{ template "prometheus-pushgateway.namespace" . }} +spec: + replicas: {{ .Values.replicaCount }} + serviceName: {{ include "prometheus-pushgateway.fullname" . }} + selector: + matchLabels: + {{- include "prometheus-pushgateway.selectorLabels" . | nindent 6 }} + template: + metadata: + {{- with .Values.podAnnotations }} + annotations: + {{- toYaml . | nindent 8 }} + {{- end }} + labels: + {{- include "prometheus-pushgateway.defaultLabels" . | nindent 8 }} + spec: + {{- include "prometheus-pushgateway.podSpec" . | nindent 6 }} + {{- if .Values.persistentVolume.enabled }} + volumeClaimTemplates: + - metadata: + {{- with .Values.persistentVolume.annotations }} + annotations: + {{- toYaml . | nindent 10 }} + {{- end }} + labels: + {{- include "prometheus-pushgateway.defaultLabels" . | nindent 10 }} + name: storage-volume + spec: + accessModes: + {{ toYaml .Values.persistentVolume.accessModes }} + {{- if .Values.persistentVolume.storageClass }} + {{- if (eq "-" .Values.persistentVolume.storageClass) }} + storageClassName: "" + {{- else }} + storageClassName: "{{ .Values.persistentVolume.storageClass }}" + {{- end }} + {{- end }} + resources: + requests: + storage: "{{ .Values.persistentVolume.size }}" + {{- end }} +{{- end }} diff --git a/charts/kasten/k10/7.5.401/charts/prometheus/charts/prometheus-pushgateway/values.yaml b/charts/kasten/k10/7.5.401/charts/prometheus/charts/prometheus-pushgateway/values.yaml new file mode 100644 index 0000000000..4bc5e14628 --- /dev/null +++ b/charts/kasten/k10/7.5.401/charts/prometheus/charts/prometheus-pushgateway/values.yaml @@ -0,0 +1,377 @@ +# Default values for prometheus-pushgateway. +# This is a YAML-formatted file. +# Declare variables to be passed into your templates. + +# Provide a name in place of prometheus-pushgateway for `app:` labels +nameOverride: "" + +# Provide a name to substitute for the full names of resources +fullnameOverride: "" + +# Provide a namespace to substitude for the namespace on resources +namespaceOverride: "" + +image: + repository: quay.io/prometheus/pushgateway + # if not set appVersion field from Chart.yaml is used + tag: "" + pullPolicy: IfNotPresent + +# Optional pod imagePullSecrets +imagePullSecrets: [] + +service: + type: ClusterIP + port: 9091 + targetPort: 9091 + # nodePort: 32100 + portName: http + + # Optional - Can be used for headless if value is "None" + clusterIP: "" + + ipDualStack: + enabled: false + ipFamilies: ["IPv6", "IPv4"] + ipFamilyPolicy: "PreferDualStack" + + loadBalancerIP: "" + loadBalancerSourceRanges: [] + +# Whether to automatically mount a service account token into the pod +automountServiceAccountToken: true + +# Optional pod annotations +podAnnotations: {} + +# Optional pod labels +podLabels: {} + +# Optional service annotations +serviceAnnotations: {} + +# Optional service labels +serviceLabels: {} + +# Optional serviceAccount labels +serviceAccountLabels: {} + +# Optional persistentVolume labels +persistentVolumeLabels: {} + +# Optional additional environment variables +extraVars: [] + +## Additional pushgateway container arguments +## +## example: +## extraArgs: +## - --persistence.file=/data/pushgateway.data +## - --persistence.interval=5m +extraArgs: [] + +## Additional InitContainers to initialize the pod +## +extraInitContainers: [] + +# Optional additional containers (sidecar) +extraContainers: [] + # - name: oAuth2-proxy + # args: + # - -https-address=:9092 + # - -upstream=http://localhost:9091 + # - -skip-auth-regex=^/metrics + # - -openshift-delegate-urls={"/":{"group":"monitoring.coreos.com","resource":"prometheuses","verb":"get"}} + # image: openshift/oauth-proxy:v1.1.0 + # ports: + # - containerPort: 9092 + # name: proxy + # resources: + # limits: + # memory: 16Mi + # requests: + # memory: 4Mi + # cpu: 20m + # volumeMounts: + # - mountPath: /etc/prometheus/secrets/pushgateway-tls + # name: secret-pushgateway-tls + +resources: {} + # We usually recommend not to specify default resources and to leave this as a conscious + # choice for the user. This also increases chances charts run on environments with little + # resources, such as Minikube. If you do want to specify resources, uncomment the following + # lines, adjust them as necessary, and remove the curly braces after 'resources:'. + # limits: + # cpu: 200m + # memory: 50Mi + # requests: + # cpu: 100m + # memory: 30Mi + +# -- Sets web configuration +# To enable basic authentication, provide basicAuthUsers as a map +webConfiguration: {} + # basicAuthUsers: + # username: password + +liveness: + enabled: true + probe: + httpGet: + path: /-/healthy + port: 9091 + initialDelaySeconds: 10 + timeoutSeconds: 10 + +readiness: + enabled: true + probe: + httpGet: + path: /-/ready + port: 9091 + initialDelaySeconds: 10 + timeoutSeconds: 10 + +serviceAccount: + # Specifies whether a ServiceAccount should be created + create: true + # The name of the ServiceAccount to use. + # If not set and create is true, a name is generated using the fullname template + name: + +## Configure ingress resource that allow you to access the +## pushgateway installation. Set up the URL +## ref: http://kubernetes.io/docs/user-guide/ingress/ +## +ingress: + ## Enable Ingress. + ## + enabled: false + # AWS ALB requires path of /* + className: "" + path: / + pathType: ImplementationSpecific + + ## Extra paths to prepend to every host configuration. This is useful when working with annotation based services. + extraPaths: [] + # - path: /* + # backend: + # serviceName: ssl-redirect + # servicePort: use-annotation + + ## Annotations. + ## + # annotations: + # kubernetes.io/ingress.class: nginx + # kubernetes.io/tls-acme: 'true' + + ## Hostnames. + ## Must be provided if Ingress is enabled. + ## + # hosts: + # - pushgateway.domain.com + + ## TLS configuration. + ## Secrets must be manually created in the namespace. + ## + # tls: + # - secretName: pushgateway-tls + # hosts: + # - pushgateway.domain.com + +tolerations: [] + # - effect: NoSchedule + # operator: Exists + +## Node labels for pushgateway pod assignment +## Ref: https://kubernetes.io/docs/user-guide/node-selection/ +## +nodeSelector: {} + +replicaCount: 1 + +hostAliases: [] + # - ip: "127.0.0.1" + # hostnames: + # - "foo.local" + # - "bar.local" + # - ip: "10.1.2.3" + # hostnames: + # - "foo.remote" + # - "bar.remote" + +## When running more than one replica alongside with persistence, different volumes are needed +## per replica, since sharing a `persistence.file` across replicas does not keep metrics synced. +## For this purpose, you can enable the `runAsStatefulSet` to deploy the pushgateway as a +## StatefulSet instead of as a Deployment. +runAsStatefulSet: false + +## Security context to be added to push-gateway pods +## +securityContext: + fsGroup: 65534 + runAsUser: 65534 + runAsNonRoot: true + +## Security context to be added to push-gateway containers +## Having a separate variable as securityContext differs for pods and containers. +containerSecurityContext: {} +# allowPrivilegeEscalation: false +# readOnlyRootFilesystem: true +# runAsUser: 65534 +# runAsNonRoot: true + +## Affinity for pod assignment +## Ref: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#affinity-and-anti-affinity +affinity: {} + +## Pod anti-affinity can prevent the scheduler from placing pushgateway replicas on the same node. +## The value "soft" means that the scheduler should *prefer* to not schedule two replica pods onto the same node but no guarantee is provided. +## The value "hard" means that the scheduler is *required* to not schedule two replica pods onto the same node. +## The default value "" will disable pod anti-affinity so that no anti-affinity rules will be configured (unless set in `affinity`). +## +podAntiAffinity: "" + +## If anti-affinity is enabled sets the topologyKey to use for anti-affinity. +## This can be changed to, for example, failure-domain.beta.kubernetes.io/zone +## +podAntiAffinityTopologyKey: kubernetes.io/hostname + +## Topology spread constraints for pods +## Ref: https://kubernetes.io/docs/concepts/workloads/pods/pod-topology-spread-constraints/ +topologySpreadConstraints: [] + +# Enable this if you're using https://github.com/coreos/prometheus-operator +serviceMonitor: + enabled: false + namespace: monitoring + + # telemetryPath: HTTP resource path from which to fetch metrics. + # Telemetry path, default /metrics, has to be prefixed accordingly if pushgateway sets a route prefix at start-up. + # + telemetryPath: "/metrics" + + # Fallback to the prometheus default unless specified + # interval: 10s + + ## scheme: HTTP scheme to use for scraping. Can be used with `tlsConfig` for example if using istio mTLS. + # scheme: "" + + ## tlsConfig: TLS configuration to use when scraping the endpoint. For example if using istio mTLS. + ## Of type: https://github.com/coreos/prometheus-operator/blob/master/Documentation/api.md#tlsconfig + # tlsConfig: {} + + # bearerTokenFile: + # Fallback to the prometheus default unless specified + # scrapeTimeout: 30s + + ## Used to pass Labels that are used by the Prometheus installed in your cluster to select Service Monitors to work with + ## ref: https://github.com/coreos/prometheus-operator/blob/master/Documentation/api.md#prometheusspec + additionalLabels: {} + + # Retain the job and instance labels of the metrics pushed to the Pushgateway + # [Scraping Pushgateway](https://github.com/prometheus/pushgateway#configure-the-pushgateway-as-a-target-to-scrape) + honorLabels: true + + ## Metric relabel configs to apply to samples before ingestion. + ## [Metric Relabeling](https://prometheus.io/docs/prometheus/latest/configuration/configuration/#metric_relabel_configs) + metricRelabelings: [] + # - action: keep + # regex: 'kube_(daemonset|deployment|pod|namespace|node|statefulset).+' + # sourceLabels: [__name__] + + ## Relabel configs to apply to samples before ingestion. + ## [Relabeling](https://prometheus.io/docs/prometheus/latest/configuration/configuration/#relabel_config) + relabelings: [] + # - sourceLabels: [__meta_kubernetes_pod_node_name] + # separator: ; + # regex: ^(.*)$ + # targetLabel: nodename + # replacement: $1 + # action: replace + +# The values to set in the PodDisruptionBudget spec (minAvailable/maxUnavailable) +# If not set then a PodDisruptionBudget will not be created +podDisruptionBudget: {} + +priorityClassName: + +# Deployment Strategy type +strategy: + type: Recreate + +persistentVolume: + ## If true, pushgateway will create/use a Persistent Volume Claim + ## If false, use emptyDir + ## + enabled: false + + ## pushgateway data Persistent Volume access modes + ## Must match those of existing PV or dynamic provisioner + ## Ref: http://kubernetes.io/docs/user-guide/persistent-volumes/ + ## + accessModes: + - ReadWriteOnce + + ## pushgateway data Persistent Volume Claim annotations + ## + annotations: {} + + ## pushgateway data Persistent Volume existing claim name + ## Requires pushgateway.persistentVolume.enabled: true + ## If defined, PVC must be created manually before volume will be bound + existingClaim: "" + + ## pushgateway data Persistent Volume mount root path + ## + mountPath: /data + + ## pushgateway data Persistent Volume size + ## + size: 2Gi + + ## pushgateway data Persistent Volume Storage Class + ## If defined, storageClassName: + ## If set to "-", storageClassName: "", which disables dynamic provisioning + ## If undefined (the default) or set to null, no storageClassName spec is + ## set, choosing the default provisioner. (gp2 on AWS, standard on + ## GKE, AWS & OpenStack) + ## + # storageClass: "-" + + ## Subdirectory of pushgateway data Persistent Volume to mount + ## Useful if the volume's root directory is not empty + ## + subPath: "" + +extraVolumes: [] + # - name: extra + # emptyDir: {} +extraVolumeMounts: [] + # - name: extra + # mountPath: /usr/share/extras + # readOnly: true + +# Configuration for clusters with restrictive network policies in place: +# - allowAll allows access to the PushGateway from any namespace +# - customSelector is a list of pod/namespaceSelectors to allow access from +# These options are mutually exclusive and the latter will take precedence. +networkPolicy: {} + # allowAll: true + # customSelectors: + # - namespaceSelector: + # matchLabels: + # type: admin + # - podSelector: + # matchLabels: + # app: myapp + +# Array of extra K8s objects to deploy (evaluated as a template) +# The value can hold an array of strings as well as objects +extraManifests: [] + +# Lifecycle hooks configuration +lifecycle: {} +# preStop: +# exec: +# command: ["/bin/sh", "-c", "sleep 30"] diff --git a/charts/kasten/k10/7.5.401/charts/prometheus/templates/NOTES.txt b/charts/kasten/k10/7.5.401/charts/prometheus/templates/NOTES.txt new file mode 100644 index 0000000000..12e4acc5bf --- /dev/null +++ b/charts/kasten/k10/7.5.401/charts/prometheus/templates/NOTES.txt @@ -0,0 +1,113 @@ +The Prometheus server can be accessed via port {{ .Values.server.service.servicePort }} on the following DNS name from within your cluster: +{{ template "prometheus.server.fullname" . }}.{{ .Release.Namespace }}.svc.cluster.local + +{{ if .Values.server.ingress.enabled -}} +From outside the cluster, the server URL(s) are: +{{- range .Values.server.ingress.hosts }} +http://{{ tpl . $ }} +{{- end }} +{{- else }} +Get the Prometheus server URL by running these commands in the same shell: +{{- if contains "NodePort" .Values.server.service.type }} + export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ template "prometheus.server.fullname" . }}) + export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}") + echo http://$NODE_IP:$NODE_PORT +{{- else if contains "LoadBalancer" .Values.server.service.type }} + NOTE: It may take a few minutes for the LoadBalancer IP to be available. + You can watch the status of by running 'kubectl get svc --namespace {{ .Release.Namespace }} -w {{ template "prometheus.server.fullname" . }}' + + export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ template "prometheus.server.fullname" . }} -o jsonpath='{.status.loadBalancer.ingress[0].ip}') + echo http://$SERVICE_IP:{{ .Values.server.service.servicePort }} +{{- else if contains "ClusterIP" .Values.server.service.type }} + export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app.kubernetes.io/name={{ include "prometheus.name" . }},app.kubernetes.io/instance={{ .Release.Name }}" -o jsonpath="{.items[0].metadata.name}") + kubectl --namespace {{ .Release.Namespace }} port-forward $POD_NAME 9090 +{{- end }} + + +{{- if .Values.server.persistentVolume.enabled }} +{{- else }} +################################################################################# +###### WARNING: Persistence is disabled!!! You will lose your data when ##### +###### the Server pod is terminated. ##### +################################################################################# +{{- end }} +{{- end }} + +{{ if .Values.alertmanager.enabled }} +The Prometheus alertmanager can be accessed via port {{ .Values.alertmanager.service.port }} on the following DNS name from within your cluster: +{{ template "prometheus.alertmanager.fullname" . }}.{{ .Release.Namespace }}.svc.cluster.local + +{{ if .Values.alertmanager.ingress.enabled -}} +From outside the cluster, the alertmanager URL(s) are: +{{- range .Values.alertmanager.ingress.hosts }} +http://{{ . }} +{{- end }} +{{- else }} +Get the Alertmanager URL by running these commands in the same shell: +{{- if contains "NodePort" .Values.alertmanager.service.type }} + export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ template "prometheus.alertmanager.fullname" . }}) + export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}") + echo http://$NODE_IP:$NODE_PORT +{{- else if contains "LoadBalancer" .Values.alertmanager.service.type }} + NOTE: It may take a few minutes for the LoadBalancer IP to be available. + You can watch the status of by running 'kubectl get svc --namespace {{ .Release.Namespace }} -w {{ template "prometheus.alertmanager.fullname" . }}' + + export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ template "prometheus.alertmanager.fullname" . }} -o jsonpath='{.status.loadBalancer.ingress[0].ip}') + echo http://$SERVICE_IP:{{ .Values.alertmanager.service.servicePort }} +{{- else if contains "ClusterIP" .Values.alertmanager.service.type }} + export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app.kubernetes.io/name={{ include "alertmanager.name" .Subcharts.alertmanager }},app.kubernetes.io/instance={{ .Release.Name }}" -o jsonpath="{.items[0].metadata.name}") + kubectl --namespace {{ .Release.Namespace }} port-forward $POD_NAME 9093 +{{- end }} +{{- end }} + +{{- if .Values.alertmanager.persistence.enabled }} +{{- else }} +################################################################################# +###### WARNING: Persistence is disabled!!! You will lose your data when ##### +###### the AlertManager pod is terminated. ##### +################################################################################# +{{- end }} +{{- end }} + +{{- if (index .Values "prometheus-node-exporter" "enabled") }} +################################################################################# +###### WARNING: Pod Security Policy has been disabled by default since ##### +###### it deprecated after k8s 1.25+. use ##### +###### (index .Values "prometheus-node-exporter" "rbac" ##### +###### . "pspEnabled") with (index .Values ##### +###### "prometheus-node-exporter" "rbac" "pspAnnotations") ##### +###### in case you still need it. ##### +################################################################################# +{{- end }} + +{{ if (index .Values "prometheus-pushgateway" "enabled") }} +The Prometheus PushGateway can be accessed via port {{ index .Values "prometheus-pushgateway" "service" "port" }} on the following DNS name from within your cluster: +{{ include "prometheus-pushgateway.fullname" (index .Subcharts "prometheus-pushgateway") }}.{{ .Release.Namespace }}.svc.cluster.local + +{{ if (index .Values "prometheus-pushgateway" "ingress" "enabled") -}} +From outside the cluster, the pushgateway URL(s) are: +{{- range (index .Values "prometheus-pushgateway" "ingress" "hosts") }} +http://{{ . }} +{{- end }} +{{- else }} +Get the PushGateway URL by running these commands in the same shell: +{{- $pushgateway_svc_type := index .Values "prometheus-pushgateway" "service" "type" -}} +{{- if contains "NodePort" $pushgateway_svc_type }} + export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ include "prometheus-pushgateway.fullname" (index .Subcharts "prometheus-pushgateway") }}) + export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}") + echo http://$NODE_IP:$NODE_PORT +{{- else if contains "LoadBalancer" $pushgateway_svc_type }} + NOTE: It may take a few minutes for the LoadBalancer IP to be available. + You can watch the status of by running 'kubectl get svc --namespace {{ .Release.Namespace }} -w {{ include "prometheus-pushgateway.fullname" (index .Subcharts "prometheus-pushgateway") }}' + + export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ include "prometheus-pushgateway.fullname" (index .Subcharts "prometheus-pushgateway") }} -o jsonpath='{.status.loadBalancer.ingress[0].ip}') + echo http://$SERVICE_IP:{{ index .Values "prometheus-pushgateway" "service" "port" }} +{{- else if contains "ClusterIP" $pushgateway_svc_type }} + export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app={{ include "prometheus.name" (index .Subcharts "prometheus-pushgateway") }},component=pushgateway" -o jsonpath="{.items[0].metadata.name}") + kubectl --namespace {{ .Release.Namespace }} port-forward $POD_NAME 9091 +{{- end }} +{{- end }} +{{- end }} + +For more information on running Prometheus, visit: +https://prometheus.io/ diff --git a/charts/kasten/k10/7.5.401/charts/prometheus/templates/_helpers.tpl b/charts/kasten/k10/7.5.401/charts/prometheus/templates/_helpers.tpl new file mode 100644 index 0000000000..6704be2751 --- /dev/null +++ b/charts/kasten/k10/7.5.401/charts/prometheus/templates/_helpers.tpl @@ -0,0 +1,237 @@ +{{/* vim: set filetype=mustache: */}} +{{/* +Expand the name of the chart. +*/}} +{{- define "prometheus.name" -}} +{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}} +{{- end -}} + +{{/* +Create chart name and version as used by the chart label. +*/}} +{{- define "prometheus.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}} +{{- end -}} + +{{/* +Create labels for prometheus +*/}} +{{- define "prometheus.common.matchLabels" -}} +app.kubernetes.io/name: {{ include "prometheus.name" . }} +app.kubernetes.io/instance: {{ .Release.Name }} +{{- end -}} + +{{/* +Create unified labels for prometheus components +*/}} +{{- define "prometheus.common.metaLabels" -}} +app.kubernetes.io/version: {{ .Chart.AppVersion }} +helm.sh/chart: {{ include "prometheus.chart" . }} +app.kubernetes.io/part-of: {{ include "prometheus.name" . }} +{{- with .Values.commonMetaLabels}} +{{ toYaml . }} +{{- end }} +{{- end -}} + +{{- define "prometheus.server.labels" -}} +{{ include "prometheus.server.matchLabels" . }} +{{ include "prometheus.common.metaLabels" . }} +{{- end -}} + +{{- define "prometheus.server.matchLabels" -}} +app.kubernetes.io/component: {{ .Values.server.name }} +{{ include "prometheus.common.matchLabels" . }} +{{- end -}} + +{{/* +Create a default fully qualified app name. +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +*/}} +{{- define "prometheus.fullname" -}} +{{- if .Values.fullnameOverride -}} +{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" -}} +{{- else -}} +{{- $name := default .Chart.Name .Values.nameOverride -}} +{{- if contains $name .Release.Name -}} +{{- .Release.Name | trunc 63 | trimSuffix "-" -}} +{{- else -}} +{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}} +{{- end -}} +{{- end -}} +{{- end -}} + +{{/* +Create a fully qualified ClusterRole name. +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +*/}} +{{- define "prometheus.clusterRoleName" -}} +{{- if .Values.server.clusterRoleNameOverride -}} +{{ .Values.server.clusterRoleNameOverride | trunc 63 | trimSuffix "-" }} +{{- else -}} +{{ include "prometheus.server.fullname" . }} +{{- end -}} +{{- end -}} + +{{/* +Create a fully qualified alertmanager name for communicating and check to ensure that `alertmanager` exists before trying to use it with the user via NOTES.txt +*/}} +{{- define "prometheus.alertmanager.fullname" -}} +{{- if .Subcharts.alertmanager -}} +{{- template "alertmanager.fullname" .Subcharts.alertmanager -}} +{{- else -}} +{{- "alertmanager not found" -}} +{{- end -}} +{{- end -}} + +{{/* +Create a fully qualified Prometheus server name. +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +*/}} +{{- define "prometheus.server.fullname" -}} +{{- if .Values.server.fullnameOverride -}} +{{- .Values.server.fullnameOverride | trunc 63 | trimSuffix "-" -}} +{{- else -}} +{{- $name := default .Chart.Name .Values.nameOverride -}} +{{- if contains $name .Release.Name -}} +{{- printf "%s-%s" .Release.Name .Values.server.name | trunc 63 | trimSuffix "-" -}} +{{- else -}} +{{- printf "%s-%s-%s" .Release.Name $name .Values.server.name | trunc 63 | trimSuffix "-" -}} +{{- end -}} +{{- end -}} +{{- end -}} + +{{/* +Get KubeVersion removing pre-release information. +*/}} +{{- define "prometheus.kubeVersion" -}} + {{- default .Capabilities.KubeVersion.Version (regexFind "v[0-9]+\\.[0-9]+\\.[0-9]+" .Capabilities.KubeVersion.Version) -}} +{{- end -}} + +{{/* +Return the appropriate apiVersion for deployment. +*/}} +{{- define "prometheus.deployment.apiVersion" -}} +{{- print "apps/v1" -}} +{{- end -}} + +{{/* +Return the appropriate apiVersion for networkpolicy. +*/}} +{{- define "prometheus.networkPolicy.apiVersion" -}} +{{- print "networking.k8s.io/v1" -}} +{{- end -}} + +{{/* +Return the appropriate apiVersion for poddisruptionbudget. +*/}} +{{- define "prometheus.podDisruptionBudget.apiVersion" -}} +{{- if .Capabilities.APIVersions.Has "policy/v1" }} +{{- print "policy/v1" -}} +{{- else -}} +{{- print "policy/v1beta1" -}} +{{- end -}} +{{- end -}} + +{{/* +Return the appropriate apiVersion for rbac. +*/}} +{{- define "rbac.apiVersion" -}} +{{- if .Capabilities.APIVersions.Has "rbac.authorization.k8s.io/v1" }} +{{- print "rbac.authorization.k8s.io/v1" -}} +{{- else -}} +{{- print "rbac.authorization.k8s.io/v1beta1" -}} +{{- end -}} +{{- end -}} + +{{/* +Return the appropriate apiVersion for ingress. +*/}} +{{- define "ingress.apiVersion" -}} + {{- if and (.Capabilities.APIVersions.Has "networking.k8s.io/v1") (semverCompare ">= 1.19.x" (include "prometheus.kubeVersion" .)) -}} + {{- print "networking.k8s.io/v1" -}} + {{- else if .Capabilities.APIVersions.Has "networking.k8s.io/v1beta1" -}} + {{- print "networking.k8s.io/v1beta1" -}} + {{- else -}} + {{- print "extensions/v1beta1" -}} + {{- end -}} +{{- end -}} + +{{/* +Return if ingress is stable. +*/}} +{{- define "ingress.isStable" -}} + {{- eq (include "ingress.apiVersion" .) "networking.k8s.io/v1" -}} +{{- end -}} + +{{/* +Return if ingress supports ingressClassName. +*/}} +{{- define "ingress.supportsIngressClassName" -}} + {{- or (eq (include "ingress.isStable" .) "true") (and (eq (include "ingress.apiVersion" .) "networking.k8s.io/v1beta1") (semverCompare ">= 1.18.x" (include "prometheus.kubeVersion" .))) -}} +{{- end -}} + +{{/* +Return if ingress supports pathType. +*/}} +{{- define "ingress.supportsPathType" -}} + {{- or (eq (include "ingress.isStable" .) "true") (and (eq (include "ingress.apiVersion" .) "networking.k8s.io/v1beta1") (semverCompare ">= 1.18.x" (include "prometheus.kubeVersion" .))) -}} +{{- end -}} + +{{/* +Create the name of the service account to use for the server component +*/}} +{{- define "prometheus.serviceAccountName.server" -}} +{{- if .Values.serviceAccounts.server.create -}} + {{ default (include "prometheus.server.fullname" .) .Values.serviceAccounts.server.name }} +{{- else -}} + {{ default "default" .Values.serviceAccounts.server.name }} +{{- end -}} +{{- end -}} + +{{/* +Define the prometheus.namespace template if set with forceNamespace or .Release.Namespace is set +*/}} +{{- define "prometheus.namespace" -}} + {{- default .Release.Namespace .Values.forceNamespace -}} +{{- end }} + +{{/* +Define template prometheus.namespaces producing a list of namespaces to monitor +*/}} +{{- define "prometheus.namespaces" -}} +{{- $namespaces := list }} +{{- if and .Values.rbac.create .Values.server.useExistingClusterRoleName }} + {{- if .Values.server.namespaces -}} + {{- range $ns := join "," .Values.server.namespaces | split "," }} + {{- $namespaces = append $namespaces (tpl $ns $) }} + {{- end -}} + {{- end -}} + {{- if .Values.server.releaseNamespace -}} + {{- $namespaces = append $namespaces (include "prometheus.namespace" .) }} + {{- end -}} +{{- end -}} +{{ mustToJson $namespaces }} +{{- end -}} + +{{/* +Define prometheus.server.remoteWrite producing a list of remoteWrite configurations with URL templating +*/}} +{{- define "prometheus.server.remoteWrite" -}} +{{- $remoteWrites := list }} +{{- range $remoteWrite := .Values.server.remoteWrite }} + {{- $remoteWrites = tpl $remoteWrite.url $ | set $remoteWrite "url" | append $remoteWrites }} +{{- end -}} +{{ toYaml $remoteWrites }} +{{- end -}} + +{{/* +Define prometheus.server.remoteRead producing a list of remoteRead configurations with URL templating +*/}} +{{- define "prometheus.server.remoteRead" -}} +{{- $remoteReads := list }} +{{- range $remoteRead := .Values.server.remoteRead }} + {{- $remoteReads = tpl $remoteRead.url $ | set $remoteRead "url" | append $remoteReads }} +{{- end -}} +{{ toYaml $remoteReads }} +{{- end -}} + diff --git a/charts/kasten/k10/7.5.401/charts/prometheus/templates/clusterrole.yaml b/charts/kasten/k10/7.5.401/charts/prometheus/templates/clusterrole.yaml new file mode 100644 index 0000000000..25e3cec45d --- /dev/null +++ b/charts/kasten/k10/7.5.401/charts/prometheus/templates/clusterrole.yaml @@ -0,0 +1,56 @@ +{{- if and .Values.rbac.create (empty .Values.server.useExistingClusterRoleName) -}} +apiVersion: {{ template "rbac.apiVersion" . }} +kind: ClusterRole +metadata: + labels: + {{- include "prometheus.server.labels" . | nindent 4 }} + name: {{ include "prometheus.clusterRoleName" . }} +rules: +{{- if and .Values.podSecurityPolicy.enabled (.Capabilities.APIVersions.Has "policy/v1beta1/PodSecurityPolicy") }} + - apiGroups: + - extensions + resources: + - podsecuritypolicies + verbs: + - use + resourceNames: + - {{ template "prometheus.server.fullname" . }} +{{- end }} + - apiGroups: + - "" + resources: + - nodes + - nodes/proxy + - nodes/metrics + - services + - endpoints + - pods + - ingresses + - configmaps + verbs: + - get + - list + - watch + - apiGroups: + - "extensions" + - "networking.k8s.io" + resources: + - ingresses/status + - ingresses + verbs: + - get + - list + - watch + - apiGroups: + - "discovery.k8s.io" + resources: + - endpointslices + verbs: + - get + - list + - watch + - nonResourceURLs: + - "/metrics" + verbs: + - get +{{- end }} diff --git a/charts/kasten/k10/7.5.401/charts/prometheus/templates/clusterrolebinding.yaml b/charts/kasten/k10/7.5.401/charts/prometheus/templates/clusterrolebinding.yaml new file mode 100644 index 0000000000..28f4bda776 --- /dev/null +++ b/charts/kasten/k10/7.5.401/charts/prometheus/templates/clusterrolebinding.yaml @@ -0,0 +1,16 @@ +{{- if and .Values.rbac.create (empty .Values.server.namespaces) (empty .Values.server.useExistingClusterRoleName) -}} +apiVersion: {{ template "rbac.apiVersion" . }} +kind: ClusterRoleBinding +metadata: + labels: + {{- include "prometheus.server.labels" . | nindent 4 }} + name: {{ include "prometheus.clusterRoleName" . }} +subjects: + - kind: ServiceAccount + name: {{ template "prometheus.serviceAccountName.server" . }} + namespace: {{ include "prometheus.namespace" . }} +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: {{ include "prometheus.clusterRoleName" . }} +{{- end }} diff --git a/charts/kasten/k10/7.5.401/charts/prometheus/templates/cm.yaml b/charts/kasten/k10/7.5.401/charts/prometheus/templates/cm.yaml new file mode 100644 index 0000000000..8713bd1ea2 --- /dev/null +++ b/charts/kasten/k10/7.5.401/charts/prometheus/templates/cm.yaml @@ -0,0 +1,103 @@ +{{- if (empty .Values.server.configMapOverrideName) -}} +apiVersion: v1 +kind: ConfigMap +metadata: +{{- with .Values.server.configMapAnnotations }} + annotations: + {{- toYaml . | nindent 4 }} +{{- end }} + labels: + {{- include "prometheus.server.labels" . | nindent 4 }} + {{- with .Values.server.extraConfigmapLabels }} + {{- toYaml . | nindent 4 }} + {{- end }} + name: {{ template "prometheus.server.fullname" . }} + namespace: {{ include "prometheus.namespace" . }} +data: + allow-snippet-annotations: "false" +{{- $root := . -}} +{{- range $key, $value := .Values.ruleFiles }} + {{ $key }}: {{- toYaml $value | indent 2 }} +{{- end }} +{{- range $key, $value := .Values.serverFiles }} + {{ $key }}: | +{{- if eq $key "prometheus.yml" }} + global: +{{ $root.Values.server.global | toYaml | trimSuffix "\n" | indent 6 }} +{{- if $root.Values.server.remoteWrite }} + remote_write: +{{- include "prometheus.server.remoteWrite" $root | nindent 4 }} +{{- end }} +{{- if $root.Values.server.remoteRead }} + remote_read: +{{- include "prometheus.server.remoteRead" $root | nindent 4 }} +{{- end }} +{{- if or $root.Values.server.tsdb $root.Values.server.exemplars }} + storage: +{{- if $root.Values.server.tsdb }} + tsdb: +{{ $root.Values.server.tsdb | toYaml | indent 8 }} +{{- end }} +{{- if $root.Values.server.exemplars }} + exemplars: +{{ $root.Values.server.exemplars | toYaml | indent 8 }} +{{- end }} +{{- end }} +{{- if $root.Values.scrapeConfigFiles }} + scrape_config_files: +{{ toYaml $root.Values.scrapeConfigFiles | indent 4 }} +{{- end }} +{{- end }} +{{- if eq $key "alerts" }} +{{- if and (not (empty $value)) (empty $value.groups) }} + groups: +{{- range $ruleKey, $ruleValue := $value }} + - name: {{ $ruleKey -}}.rules + rules: +{{ $ruleValue | toYaml | trimSuffix "\n" | indent 6 }} +{{- end }} +{{- else }} +{{ toYaml $value | indent 4 }} +{{- end }} +{{- else }} +{{ toYaml $value | default "{}" | indent 4 }} +{{- end }} +{{- if eq $key "prometheus.yml" -}} +{{- if $root.Values.extraScrapeConfigs }} +{{ tpl $root.Values.extraScrapeConfigs $root | indent 4 }} +{{- end -}} +{{- if or ($root.Values.alertmanager.enabled) ($root.Values.server.alertmanagers) }} + alerting: +{{- if $root.Values.alertRelabelConfigs }} +{{ $root.Values.alertRelabelConfigs | toYaml | trimSuffix "\n" | indent 6 }} +{{- end }} + alertmanagers: +{{- if $root.Values.server.alertmanagers }} +{{ toYaml $root.Values.server.alertmanagers | indent 8 }} +{{- else }} + - kubernetes_sd_configs: + - role: pod + tls_config: + ca_file: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt + bearer_token_file: /var/run/secrets/kubernetes.io/serviceaccount/token + {{- if $root.Values.alertmanager.prefixURL }} + path_prefix: {{ $root.Values.alertmanager.prefixURL }} + {{- end }} + relabel_configs: + - source_labels: [__meta_kubernetes_namespace] + regex: {{ $root.Release.Namespace }} + action: keep + - source_labels: [__meta_kubernetes_pod_label_app_kubernetes_io_instance] + regex: {{ $root.Release.Name }} + action: keep + - source_labels: [__meta_kubernetes_pod_label_app_kubernetes_io_name] + regex: {{ default "alertmanager" $root.Values.alertmanager.nameOverride | trunc 63 | trimSuffix "-" }} + action: keep + - source_labels: [__meta_kubernetes_pod_container_port_number] + regex: "9093" + action: keep +{{- end -}} +{{- end -}} +{{- end -}} +{{- end -}} +{{- end -}} diff --git a/charts/kasten/k10/7.5.401/charts/prometheus/templates/deploy.yaml b/charts/kasten/k10/7.5.401/charts/prometheus/templates/deploy.yaml new file mode 100644 index 0000000000..f0ccb1492b --- /dev/null +++ b/charts/kasten/k10/7.5.401/charts/prometheus/templates/deploy.yaml @@ -0,0 +1,412 @@ +{{- if not .Values.server.statefulSet.enabled -}} +apiVersion: {{ template "prometheus.deployment.apiVersion" . }} +kind: Deployment +metadata: +{{- if .Values.server.deploymentAnnotations }} + annotations: + {{ toYaml .Values.server.deploymentAnnotations | nindent 4 }} +{{- end }} + labels: + {{- include "prometheus.server.labels" . | nindent 4 }} + name: {{ template "prometheus.server.fullname" . }} + namespace: {{ include "prometheus.namespace" . }} +spec: + selector: + matchLabels: + {{- include "prometheus.server.matchLabels" . | nindent 6 }} + replicas: {{ .Values.server.replicaCount }} + revisionHistoryLimit: {{ .Values.server.revisionHistoryLimit }} + {{- if .Values.server.strategy }} + strategy: +{{ toYaml .Values.server.strategy | trim | indent 4 }} + {{ if eq .Values.server.strategy.type "Recreate" }}rollingUpdate: null{{ end }} +{{- end }} + template: + metadata: + {{- if .Values.server.podAnnotations }} + annotations: + {{ toYaml .Values.server.podAnnotations | nindent 8 }} + {{- end }} + labels: + {{- include "prometheus.server.labels" . | nindent 8 }} + {{- if .Values.server.podLabels}} + {{ toYaml .Values.server.podLabels | nindent 8 }} + {{- end}} + {{- if ( .Values.global | default dict ).azMarketPlace }} + azure-extensions-usage-release-identifier: {{.Release.Name}} + {{- end }} + spec: +{{- if .Values.server.priorityClassName }} + priorityClassName: "{{ .Values.server.priorityClassName }}" +{{- end }} +{{- if .Values.server.schedulerName }} + schedulerName: "{{ .Values.server.schedulerName }}" +{{- end }} +{{- if semverCompare ">=1.13-0" .Capabilities.KubeVersion.GitVersion }} + {{- if or (.Values.server.enableServiceLinks) (eq (.Values.server.enableServiceLinks | toString) "") }} + enableServiceLinks: true + {{- else }} + enableServiceLinks: false + {{- end }} +{{- end }} + serviceAccountName: {{ template "prometheus.serviceAccountName.server" . }} +{{- if kindIs "bool" .Values.server.automountServiceAccountToken }} + automountServiceAccountToken: {{ .Values.server.automountServiceAccountToken }} +{{- end }} + {{- if .Values.server.extraInitContainers }} + initContainers: +{{ toYaml .Values.server.extraInitContainers | indent 8 }} + {{- end }} + containers: + {{- if .Values.configmapReload.prometheus.enabled }} + - name: {{ template "prometheus.name" . }}-{{ .Values.server.name }}-{{ .Values.configmapReload.prometheus.name }} + {{- if .Values.configmapReload.prometheus.image.digest }} + image: "{{ .Values.configmapReload.prometheus.image.repository }}@{{ .Values.configmapReload.prometheus.image.digest }}" + {{- else }} + image: "{{ .Values.configmapReload.prometheus.image.repository }}:{{ .Values.configmapReload.prometheus.image.tag }}" + {{- end }} + imagePullPolicy: "{{ .Values.configmapReload.prometheus.image.pullPolicy }}" + {{- with .Values.configmapReload.prometheus.containerSecurityContext }} + securityContext: + {{- toYaml . | nindent 12 }} + {{- end }} + args: + - --watched-dir=/etc/config + {{- $default_url := "http://127.0.0.1:9090/-/reload" }} + {{- with .Values.server.prefixURL }} + {{- $default_url = printf "http://127.0.0.1:9090%s/-/reload" . }} + {{- end }} + {{- if .Values.configmapReload.prometheus.containerPort }} + - --listen-address=0.0.0.0:{{ .Values.configmapReload.prometheus.containerPort }} + {{- end }} + - --reload-url={{ default $default_url .Values.configmapReload.reloadUrl }} + {{- range $key, $value := .Values.configmapReload.prometheus.extraArgs }} + {{- if $value }} + - --{{ $key }}={{ $value }} + {{- else }} + - --{{ $key }} + {{- end }} + {{- end }} + {{- range .Values.configmapReload.prometheus.extraVolumeDirs }} + - --watched-dir={{ . }} + {{- end }} + {{- with .Values.configmapReload.env }} + env: + {{- toYaml . | nindent 12 }} + {{- end }} + {{- if .Values.configmapReload.prometheus.containerPort }} + ports: + - containerPort: {{ .Values.configmapReload.prometheus.containerPort }} + {{- if .Values.configmapReload.prometheus.containerPortName }} + name: {{ .Values.configmapReload.prometheus.containerPortName }} + {{- end }} + {{- end }} + {{- with .Values.configmapReload.prometheus.livenessProbe }} + livenessProbe: + {{- toYaml . | nindent 12 }} + {{- end }} + {{- with .Values.configmapReload.prometheus.readinessProbe }} + readinessProbe: + {{- toYaml . | nindent 12 }} + {{- end }} + {{- if .Values.configmapReload.prometheus.startupProbe.enabled }} + {{- $startupProbe := omit .Values.configmapReload.prometheus.startupProbe "enabled" }} + startupProbe: + {{- toYaml $startupProbe | nindent 12 }} + {{- end }} + {{- with .Values.configmapReload.prometheus.resources }} + resources: + {{- toYaml . | nindent 12 }} + {{- end }} + volumeMounts: + - name: config-volume + mountPath: /etc/config + readOnly: true + {{- range .Values.configmapReload.prometheus.extraConfigmapMounts }} + - name: {{ $.Values.configmapReload.prometheus.name }}-{{ .name }} + mountPath: {{ .mountPath }} + subPath: {{ .subPath }} + readOnly: {{ .readOnly }} + {{- end }} + {{- with .Values.configmapReload.prometheus.extraVolumeMounts }} + {{ toYaml . | nindent 12 }} + {{- end }} + {{- end }} + + - name: {{ template "prometheus.name" . }}-{{ .Values.server.name }} + {{- if .Values.server.image.digest }} + image: "{{ .Values.server.image.repository }}@{{ .Values.server.image.digest }}" + {{- else }} + image: "{{ .Values.server.image.repository }}:{{ .Values.server.image.tag | default .Chart.AppVersion}}" + {{- end }} + imagePullPolicy: "{{ .Values.server.image.pullPolicy }}" + {{- with .Values.server.command }} + command: + {{- toYaml . | nindent 12 }} + {{- end }} + {{- if .Values.server.env }} + env: +{{ toYaml .Values.server.env | indent 12}} + {{- end }} + args: + {{- if .Values.server.defaultFlagsOverride }} + {{ toYaml .Values.server.defaultFlagsOverride | nindent 12}} + {{- else }} + {{- if .Values.server.retention }} + - --storage.tsdb.retention.time={{ .Values.server.retention }} + {{- end }} + {{- if .Values.server.retentionSize }} + - --storage.tsdb.retention.size={{ .Values.server.retentionSize }} + {{- end }} + - --config.file={{ .Values.server.configPath }} + {{- if .Values.server.storagePath }} + - --storage.tsdb.path={{ .Values.server.storagePath }} + {{- else }} + - --storage.tsdb.path={{ .Values.server.persistentVolume.mountPath }} + {{- end }} + - --web.console.libraries=/etc/prometheus/console_libraries + - --web.console.templates=/etc/prometheus/consoles + {{- range .Values.server.extraFlags }} + - --{{ . }} + {{- end }} + {{- range $key, $value := .Values.server.extraArgs }} + {{- if $value }} + - --{{ $key }}={{ $value }} + {{- else }} + - --{{ $key }} + {{- end }} + {{- end }} + {{- if .Values.server.prefixURL }} + - --web.route-prefix={{ .Values.server.prefixURL }} + {{- end }} + {{- if .Values.server.baseURL }} + - --web.external-url={{ .Values.server.baseURL }} + {{- end }} + {{- end }} + ports: + - containerPort: 9090 + {{- if .Values.server.portName }} + name: {{ .Values.server.portName }} + {{- end }} + {{- if .Values.server.hostPort }} + hostPort: {{ .Values.server.hostPort }} + {{- end }} + readinessProbe: + {{- if not .Values.server.tcpSocketProbeEnabled }} + httpGet: + path: {{ .Values.server.prefixURL }}/-/ready + port: 9090 + scheme: {{ .Values.server.probeScheme }} + {{- with .Values.server.probeHeaders }} + httpHeaders: +{{- toYaml . | nindent 14 }} + {{- end }} + {{- else }} + tcpSocket: + port: 9090 + {{- end }} + initialDelaySeconds: {{ .Values.server.readinessProbeInitialDelay }} + periodSeconds: {{ .Values.server.readinessProbePeriodSeconds }} + timeoutSeconds: {{ .Values.server.readinessProbeTimeout }} + failureThreshold: {{ .Values.server.readinessProbeFailureThreshold }} + successThreshold: {{ .Values.server.readinessProbeSuccessThreshold }} + livenessProbe: + {{- if not .Values.server.tcpSocketProbeEnabled }} + httpGet: + path: {{ .Values.server.prefixURL }}/-/healthy + port: 9090 + scheme: {{ .Values.server.probeScheme }} + {{- with .Values.server.probeHeaders }} + httpHeaders: +{{- toYaml . | nindent 14 }} + {{- end }} + {{- else }} + tcpSocket: + port: 9090 + {{- end }} + initialDelaySeconds: {{ .Values.server.livenessProbeInitialDelay }} + periodSeconds: {{ .Values.server.livenessProbePeriodSeconds }} + timeoutSeconds: {{ .Values.server.livenessProbeTimeout }} + failureThreshold: {{ .Values.server.livenessProbeFailureThreshold }} + successThreshold: {{ .Values.server.livenessProbeSuccessThreshold }} + {{- if .Values.server.startupProbe.enabled }} + startupProbe: + {{- if not .Values.server.tcpSocketProbeEnabled }} + httpGet: + path: {{ .Values.server.prefixURL }}/-/healthy + port: 9090 + scheme: {{ .Values.server.probeScheme }} + {{- if .Values.server.probeHeaders }} + httpHeaders: + {{- range .Values.server.probeHeaders}} + - name: {{ .name }} + value: {{ .value }} + {{- end }} + {{- end }} + {{- else }} + tcpSocket: + port: 9090 + {{- end }} + failureThreshold: {{ .Values.server.startupProbe.failureThreshold }} + periodSeconds: {{ .Values.server.startupProbe.periodSeconds }} + timeoutSeconds: {{ .Values.server.startupProbe.timeoutSeconds }} + {{- end }} + {{- with .Values.server.resources }} + resources: + {{- toYaml . | nindent 12 }} + {{- end }} + volumeMounts: + - name: config-volume + mountPath: /etc/config + - name: storage-volume + mountPath: {{ .Values.server.persistentVolume.mountPath }} + subPath: "{{ .Values.server.persistentVolume.subPath }}" + {{- range .Values.server.extraHostPathMounts }} + - name: {{ .name }} + mountPath: {{ .mountPath }} + subPath: {{ .subPath }} + readOnly: {{ .readOnly }} + {{- end }} + {{- range .Values.server.extraConfigmapMounts }} + - name: {{ $.Values.server.name }}-{{ .name }} + mountPath: {{ .mountPath }} + subPath: {{ .subPath }} + readOnly: {{ .readOnly }} + {{- end }} + {{- range .Values.server.extraSecretMounts }} + - name: {{ .name }} + mountPath: {{ .mountPath }} + subPath: {{ .subPath }} + readOnly: {{ .readOnly }} + {{- end }} + {{- if .Values.server.extraVolumeMounts }} + {{ toYaml .Values.server.extraVolumeMounts | nindent 12 }} + {{- end }} + {{- with .Values.server.containerSecurityContext }} + securityContext: + {{- toYaml . | nindent 12 }} + {{- end }} + {{- if .Values.server.sidecarContainers }} + {{- range $name, $spec := .Values.server.sidecarContainers }} + - name: {{ $name }} + {{- if kindIs "string" $spec }} + {{- tpl $spec $ | nindent 10 }} + {{- else }} + {{- toYaml $spec | nindent 10 }} + {{- end }} + {{- end }} + {{- end }} + {{- if .Values.server.hostNetwork }} + hostNetwork: true + dnsPolicy: ClusterFirstWithHostNet + {{- else }} + dnsPolicy: {{ .Values.server.dnsPolicy }} + {{- end }} + {{- if .Values.imagePullSecrets }} + imagePullSecrets: +{{ toYaml .Values.imagePullSecrets | indent 8 }} + {{- end }} + {{- if .Values.server.nodeSelector }} + nodeSelector: +{{ toYaml .Values.server.nodeSelector | indent 8 }} + {{- end }} + {{- if .Values.server.hostAliases }} + hostAliases: +{{ toYaml .Values.server.hostAliases | indent 8 }} + {{- end }} + {{- if .Values.server.dnsConfig }} + dnsConfig: +{{ toYaml .Values.server.dnsConfig | indent 8 }} + {{- end }} + {{- with .Values.server.securityContext }} + securityContext: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- if .Values.server.tolerations }} + tolerations: +{{ toYaml .Values.server.tolerations | indent 8 }} + {{- end }} + {{- if or .Values.server.affinity .Values.server.podAntiAffinity }} + affinity: + {{- end }} + {{- with .Values.server.affinity }} + {{- toYaml . | nindent 8 }} + {{- end }} + {{- if eq .Values.server.podAntiAffinity "hard" }} + podAntiAffinity: + requiredDuringSchedulingIgnoredDuringExecution: + - topologyKey: {{ .Values.server.podAntiAffinityTopologyKey }} + labelSelector: + matchExpressions: + - {key: app.kubernetes.io/name, operator: In, values: [{{ template "prometheus.name" . }}]} + {{- else if eq .Values.server.podAntiAffinity "soft" }} + podAntiAffinity: + preferredDuringSchedulingIgnoredDuringExecution: + - weight: 100 + podAffinityTerm: + topologyKey: {{ .Values.server.podAntiAffinityTopologyKey }} + labelSelector: + matchExpressions: + - {key: app.kubernetes.io/name, operator: In, values: [{{ template "prometheus.name" . }}]} + {{- end }} + {{- with .Values.server.topologySpreadConstraints }} + topologySpreadConstraints: + {{- toYaml . | nindent 8 }} + {{- end }} + terminationGracePeriodSeconds: {{ .Values.server.terminationGracePeriodSeconds }} + volumes: + - name: config-volume + {{- if empty .Values.server.configFromSecret }} + configMap: + name: {{ if .Values.server.configMapOverrideName }}{{ .Release.Name }}-{{ .Values.server.configMapOverrideName }}{{- else }}{{ template "prometheus.server.fullname" . }}{{- end }} + {{- else }} + secret: + secretName: {{ .Values.server.configFromSecret }} + {{- end }} + {{- range .Values.server.extraHostPathMounts }} + - name: {{ .name }} + hostPath: + path: {{ .hostPath }} + {{- end }} + {{- range .Values.configmapReload.prometheus.extraConfigmapMounts }} + - name: {{ $.Values.configmapReload.prometheus.name }}-{{ .name }} + configMap: + name: {{ .configMap }} + {{- end }} + {{- range .Values.server.extraConfigmapMounts }} + - name: {{ $.Values.server.name }}-{{ .name }} + configMap: + name: {{ .configMap }} + {{- end }} + {{- range .Values.server.extraSecretMounts }} + - name: {{ .name }} + secret: + secretName: {{ .secretName }} + {{- with .optional }} + optional: {{ . }} + {{- end }} + {{- end }} + {{- range .Values.configmapReload.prometheus.extraConfigmapMounts }} + - name: {{ .name }} + configMap: + name: {{ .configMap }} + {{- with .optional }} + optional: {{ . }} + {{- end }} + {{- end }} +{{- if .Values.server.extraVolumes }} +{{ toYaml .Values.server.extraVolumes | indent 8}} +{{- end }} + - name: storage-volume + {{- if .Values.server.persistentVolume.enabled }} + persistentVolumeClaim: + claimName: {{ if .Values.server.persistentVolume.existingClaim }}{{ .Values.server.persistentVolume.existingClaim }}{{- else }}{{ template "prometheus.server.fullname" . }}{{- end }} + {{- else }} + emptyDir: + {{- if .Values.server.emptyDir.sizeLimit }} + sizeLimit: {{ .Values.server.emptyDir.sizeLimit }} + {{- else }} + {} + {{- end -}} + {{- end -}} +{{- end -}} diff --git a/charts/kasten/k10/7.5.401/charts/prometheus/templates/extra-manifests.yaml b/charts/kasten/k10/7.5.401/charts/prometheus/templates/extra-manifests.yaml new file mode 100644 index 0000000000..2b21b71062 --- /dev/null +++ b/charts/kasten/k10/7.5.401/charts/prometheus/templates/extra-manifests.yaml @@ -0,0 +1,4 @@ +{{ range .Values.extraManifests }} +--- +{{ tpl . $ }} +{{ end }} diff --git a/charts/kasten/k10/7.5.401/charts/prometheus/templates/headless-svc.yaml b/charts/kasten/k10/7.5.401/charts/prometheus/templates/headless-svc.yaml new file mode 100644 index 0000000000..df9db99147 --- /dev/null +++ b/charts/kasten/k10/7.5.401/charts/prometheus/templates/headless-svc.yaml @@ -0,0 +1,35 @@ +{{- if .Values.server.statefulSet.enabled -}} +apiVersion: v1 +kind: Service +metadata: +{{- if .Values.server.statefulSet.headless.annotations }} + annotations: +{{ toYaml .Values.server.statefulSet.headless.annotations | indent 4 }} +{{- end }} + labels: + {{- include "prometheus.server.labels" . | nindent 4 }} +{{- if .Values.server.statefulSet.headless.labels }} +{{ toYaml .Values.server.statefulSet.headless.labels | indent 4 }} +{{- end }} + name: {{ template "prometheus.server.fullname" . }}-headless + namespace: {{ include "prometheus.namespace" . }} +spec: + clusterIP: None + ports: + - name: http + port: {{ .Values.server.statefulSet.headless.servicePort }} + protocol: TCP + targetPort: 9090 + {{- if .Values.server.statefulSet.headless.gRPC.enabled }} + - name: grpc + port: {{ .Values.server.statefulSet.headless.gRPC.servicePort }} + protocol: TCP + targetPort: 10901 + {{- if .Values.server.statefulSet.headless.gRPC.nodePort }} + nodePort: {{ .Values.server.statefulSet.headless.gRPC.nodePort }} + {{- end }} + {{- end }} + + selector: + {{- include "prometheus.server.matchLabels" . | nindent 4 }} +{{- end -}} diff --git a/charts/kasten/k10/7.5.401/charts/prometheus/templates/ingress.yaml b/charts/kasten/k10/7.5.401/charts/prometheus/templates/ingress.yaml new file mode 100644 index 0000000000..21675846e3 --- /dev/null +++ b/charts/kasten/k10/7.5.401/charts/prometheus/templates/ingress.yaml @@ -0,0 +1,57 @@ +{{- if .Values.server.ingress.enabled -}} +{{- $ingressApiIsStable := eq (include "ingress.isStable" .) "true" -}} +{{- $ingressSupportsIngressClassName := eq (include "ingress.supportsIngressClassName" .) "true" -}} +{{- $ingressSupportsPathType := eq (include "ingress.supportsPathType" .) "true" -}} +{{- $releaseName := .Release.Name -}} +{{- $serviceName := include "prometheus.server.fullname" . }} +{{- $servicePort := .Values.server.ingress.servicePort | default .Values.server.service.servicePort -}} +{{- $ingressPath := .Values.server.ingress.path -}} +{{- $ingressPathType := .Values.server.ingress.pathType -}} +{{- $extraPaths := .Values.server.ingress.extraPaths -}} +apiVersion: {{ template "ingress.apiVersion" . }} +kind: Ingress +metadata: +{{- if .Values.server.ingress.annotations }} + annotations: +{{ toYaml .Values.server.ingress.annotations | indent 4 }} +{{- end }} + labels: + {{- include "prometheus.server.labels" . | nindent 4 }} +{{- range $key, $value := .Values.server.ingress.extraLabels }} + {{ $key }}: {{ $value }} +{{- end }} + name: {{ template "prometheus.server.fullname" . }} + namespace: {{ include "prometheus.namespace" . }} +spec: + {{- if and $ingressSupportsIngressClassName .Values.server.ingress.ingressClassName }} + ingressClassName: {{ .Values.server.ingress.ingressClassName }} + {{- end }} + rules: + {{- range .Values.server.ingress.hosts }} + {{- $url := splitList "/" . }} + - host: {{ tpl (first $url) $ }} + http: + paths: +{{ if $extraPaths }} +{{ tpl (toYaml $extraPaths | indent 10) $ }} +{{- end }} + - path: {{ tpl ($ingressPath) $ }} + {{- if $ingressSupportsPathType }} + pathType: {{ $ingressPathType }} + {{- end }} + backend: + {{- if $ingressApiIsStable }} + service: + name: {{ $serviceName }} + port: + number: {{ $servicePort }} + {{- else }} + serviceName: {{ $serviceName }} + servicePort: {{ $servicePort }} + {{- end }} + {{- end -}} +{{- if .Values.server.ingress.tls }} + tls: +{{ tpl (toYaml .Values.server.ingress.tls | indent 4) $ }} + {{- end -}} +{{- end -}} diff --git a/charts/kasten/k10/7.5.401/charts/prometheus/templates/network-policy.yaml b/charts/kasten/k10/7.5.401/charts/prometheus/templates/network-policy.yaml new file mode 100644 index 0000000000..3254ffc04f --- /dev/null +++ b/charts/kasten/k10/7.5.401/charts/prometheus/templates/network-policy.yaml @@ -0,0 +1,16 @@ +{{- if .Values.networkPolicy.enabled }} +apiVersion: {{ template "prometheus.networkPolicy.apiVersion" . }} +kind: NetworkPolicy +metadata: + name: {{ template "prometheus.server.fullname" . }} + namespace: {{ include "prometheus.namespace" . }} + labels: + {{- include "prometheus.server.labels" . | nindent 4 }} +spec: + podSelector: + matchLabels: + {{- include "prometheus.server.matchLabels" . | nindent 6 }} + ingress: + - ports: + - port: 9090 +{{- end }} diff --git a/charts/kasten/k10/7.5.401/charts/prometheus/templates/pdb.yaml b/charts/kasten/k10/7.5.401/charts/prometheus/templates/pdb.yaml new file mode 100644 index 0000000000..fffbdc62fc --- /dev/null +++ b/charts/kasten/k10/7.5.401/charts/prometheus/templates/pdb.yaml @@ -0,0 +1,26 @@ +{{- if .Values.server.podDisruptionBudget.enabled }} +{{- $pdbSpec := omit .Values.server.podDisruptionBudget "enabled" }} +apiVersion: {{ template "prometheus.podDisruptionBudget.apiVersion" . }} +kind: PodDisruptionBudget +metadata: + name: {{ template "prometheus.server.fullname" . }} + namespace: {{ include "prometheus.namespace" . }} + labels: + {{- include "prometheus.server.labels" . | nindent 4 }} +spec: + selector: + matchLabels: + {{- include "prometheus.server.matchLabels" . | nindent 6 }} + {{- if not (or (hasKey $pdbSpec "minAvailable") (hasKey $pdbSpec "maxUnavailable")) }} + maxUnavailable: 1 + {{- end }} + {{- if hasKey $pdbSpec "minAvailable" }} + minAvailable: {{ $pdbSpec.minAvailable }} + {{- end }} + {{- if hasKey $pdbSpec "maxUnavailable" }} + maxUnavailable: {{ $pdbSpec.maxUnavailable }} + {{- end }} + {{- if hasKey $pdbSpec "unhealthyPodEvictionPolicy" }} + unhealthyPodEvictionPolicy: {{ $pdbSpec.unhealthyPodEvictionPolicy }} + {{- end }} +{{- end }} diff --git a/charts/kasten/k10/7.5.401/charts/prometheus/templates/psp.yaml b/charts/kasten/k10/7.5.401/charts/prometheus/templates/psp.yaml new file mode 100644 index 0000000000..5776e25410 --- /dev/null +++ b/charts/kasten/k10/7.5.401/charts/prometheus/templates/psp.yaml @@ -0,0 +1,53 @@ +{{- if and .Values.rbac.create .Values.podSecurityPolicy.enabled }} +{{- if .Capabilities.APIVersions.Has "policy/v1beta1/PodSecurityPolicy" }} +apiVersion: policy/v1beta1 +kind: PodSecurityPolicy +metadata: + name: {{ template "prometheus.server.fullname" . }} + labels: + {{- include "prometheus.server.labels" . | nindent 4 }} + {{- with .Values.server.podSecurityPolicy.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + privileged: false + allowPrivilegeEscalation: false + allowedCapabilities: + - 'CHOWN' + volumes: + - 'configMap' + - 'persistentVolumeClaim' + - 'emptyDir' + - 'secret' + - 'hostPath' + allowedHostPaths: + - pathPrefix: /etc + readOnly: true + - pathPrefix: {{ .Values.server.persistentVolume.mountPath }} + {{- range .Values.server.extraHostPathMounts }} + - pathPrefix: {{ .hostPath }} + readOnly: {{ .readOnly }} + {{- end }} + hostNetwork: false + hostPID: false + hostIPC: false + runAsUser: + rule: 'RunAsAny' + seLinux: + rule: 'RunAsAny' + supplementalGroups: + rule: 'MustRunAs' + ranges: + # Forbid adding the root group. + - min: 1 + max: 65535 + fsGroup: + rule: 'MustRunAs' + ranges: + # Forbid adding the root group. + - min: 1 + max: 65535 + readOnlyRootFilesystem: false +{{- end }} +{{- end }} diff --git a/charts/kasten/k10/7.5.401/charts/prometheus/templates/pvc.yaml b/charts/kasten/k10/7.5.401/charts/prometheus/templates/pvc.yaml new file mode 100644 index 0000000000..a9dc4fce08 --- /dev/null +++ b/charts/kasten/k10/7.5.401/charts/prometheus/templates/pvc.yaml @@ -0,0 +1,43 @@ +{{- if not .Values.server.statefulSet.enabled -}} +{{- if .Values.server.persistentVolume.enabled -}} +{{- if not .Values.server.persistentVolume.existingClaim -}} +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + {{- if .Values.server.persistentVolume.annotations }} + annotations: +{{ toYaml .Values.server.persistentVolume.annotations | indent 4 }} + {{- end }} + labels: + {{- include "prometheus.server.labels" . | nindent 4 }} + {{- with .Values.server.persistentVolume.labels }} + {{- toYaml . | nindent 4 }} + {{- end }} + name: {{ template "prometheus.server.fullname" . }} + namespace: {{ include "prometheus.namespace" . }} +spec: + accessModes: +{{ toYaml .Values.server.persistentVolume.accessModes | indent 4 }} +{{- if .Values.server.persistentVolume.storageClass }} +{{- if (eq "-" .Values.server.persistentVolume.storageClass) }} + storageClassName: "" +{{- else }} + storageClassName: "{{ .Values.server.persistentVolume.storageClass }}" +{{- end }} +{{- end }} +{{- if .Values.server.persistentVolume.volumeBindingMode }} + volumeBindingMode: "{{ .Values.server.persistentVolume.volumeBindingMode }}" +{{- end }} + resources: + requests: + storage: "{{ .Values.server.persistentVolume.size }}" +{{- if .Values.server.persistentVolume.selector }} + selector: + {{- toYaml .Values.server.persistentVolume.selector | nindent 4 }} +{{- end -}} +{{- if .Values.server.persistentVolume.volumeName }} + volumeName: "{{ .Values.server.persistentVolume.volumeName }}" +{{- end -}} +{{- end -}} +{{- end -}} +{{- end -}} diff --git a/charts/kasten/k10/7.5.401/charts/prometheus/templates/rolebinding.yaml b/charts/kasten/k10/7.5.401/charts/prometheus/templates/rolebinding.yaml new file mode 100644 index 0000000000..721b38816a --- /dev/null +++ b/charts/kasten/k10/7.5.401/charts/prometheus/templates/rolebinding.yaml @@ -0,0 +1,18 @@ +{{- range include "prometheus.namespaces" . | fromJsonArray }} +--- +apiVersion: {{ template "rbac.apiVersion" $ }} +kind: RoleBinding +metadata: + labels: + {{- include "prometheus.server.labels" $ | nindent 4 }} + name: {{ template "prometheus.server.fullname" $ }} + namespace: {{ . }} +subjects: + - kind: ServiceAccount + name: {{ template "prometheus.serviceAccountName.server" $ }} + namespace: {{ include "prometheus.namespace" $ }} +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: {{ $.Values.server.useExistingClusterRoleName }} +{{ end -}} diff --git a/charts/kasten/k10/7.5.401/charts/prometheus/templates/service.yaml b/charts/kasten/k10/7.5.401/charts/prometheus/templates/service.yaml new file mode 100644 index 0000000000..069f3270d8 --- /dev/null +++ b/charts/kasten/k10/7.5.401/charts/prometheus/templates/service.yaml @@ -0,0 +1,63 @@ +{{- if .Values.server.service.enabled -}} +apiVersion: v1 +kind: Service +metadata: +{{- if .Values.server.service.annotations }} + annotations: +{{ toYaml .Values.server.service.annotations | indent 4 }} +{{- end }} + labels: + {{- include "prometheus.server.labels" . | nindent 4 }} +{{- if .Values.server.service.labels }} +{{ toYaml .Values.server.service.labels | indent 4 }} +{{- end }} + name: {{ template "prometheus.server.fullname" . }} + namespace: {{ include "prometheus.namespace" . }} +spec: +{{- if .Values.server.service.clusterIP }} + clusterIP: {{ .Values.server.service.clusterIP }} +{{- end }} +{{- if .Values.server.service.externalIPs }} + externalIPs: +{{ toYaml .Values.server.service.externalIPs | indent 4 }} +{{- end }} +{{- if .Values.server.service.loadBalancerIP }} + loadBalancerIP: {{ .Values.server.service.loadBalancerIP }} +{{- end }} +{{- if .Values.server.service.loadBalancerSourceRanges }} + loadBalancerSourceRanges: + {{- range $cidr := .Values.server.service.loadBalancerSourceRanges }} + - {{ $cidr }} + {{- end }} +{{- end }} + ports: + - name: http + port: {{ .Values.server.service.servicePort }} + protocol: TCP + targetPort: 9090 + {{- if .Values.server.service.nodePort }} + nodePort: {{ .Values.server.service.nodePort }} + {{- end }} + {{- if .Values.server.service.gRPC.enabled }} + - name: grpc + port: {{ .Values.server.service.gRPC.servicePort }} + protocol: TCP + targetPort: 10901 + {{- if .Values.server.service.gRPC.nodePort }} + nodePort: {{ .Values.server.service.gRPC.nodePort }} + {{- end }} + {{- end }} +{{- if .Values.server.service.additionalPorts }} +{{ toYaml .Values.server.service.additionalPorts | indent 4 }} +{{- end }} + selector: + {{- if and .Values.server.statefulSet.enabled .Values.server.service.statefulsetReplica.enabled }} + statefulset.kubernetes.io/pod-name: {{ template "prometheus.server.fullname" . }}-{{ .Values.server.service.statefulsetReplica.replica }} + {{- else -}} + {{- include "prometheus.server.matchLabels" . | nindent 4 }} +{{- if .Values.server.service.sessionAffinity }} + sessionAffinity: {{ .Values.server.service.sessionAffinity }} +{{- end }} + {{- end }} + type: "{{ .Values.server.service.type }}" +{{- end -}} diff --git a/charts/kasten/k10/7.5.401/charts/prometheus/templates/serviceaccount.yaml b/charts/kasten/k10/7.5.401/charts/prometheus/templates/serviceaccount.yaml new file mode 100644 index 0000000000..6d5ab0c7d9 --- /dev/null +++ b/charts/kasten/k10/7.5.401/charts/prometheus/templates/serviceaccount.yaml @@ -0,0 +1,16 @@ +{{- if .Values.serviceAccounts.server.create }} +apiVersion: v1 +kind: ServiceAccount +metadata: + labels: + {{- include "prometheus.server.labels" . | nindent 4 }} + name: {{ template "prometheus.serviceAccountName.server" . }} + namespace: {{ include "prometheus.namespace" . }} + annotations: +{{ toYaml .Values.serviceAccounts.server.annotations | indent 4 }} +{{- if kindIs "bool" .Values.server.automountServiceAccountToken }} +automountServiceAccountToken: {{ .Values.server.automountServiceAccountToken }} +{{- else if kindIs "bool" .Values.serviceAccounts.server.automountServiceAccountToken }} +automountServiceAccountToken: {{ .Values.serviceAccounts.server.automountServiceAccountToken }} +{{- end }} +{{- end }} diff --git a/charts/kasten/k10/7.5.401/charts/prometheus/templates/sts.yaml b/charts/kasten/k10/7.5.401/charts/prometheus/templates/sts.yaml new file mode 100644 index 0000000000..6200555df1 --- /dev/null +++ b/charts/kasten/k10/7.5.401/charts/prometheus/templates/sts.yaml @@ -0,0 +1,436 @@ +{{- if .Values.server.statefulSet.enabled -}} +apiVersion: apps/v1 +kind: StatefulSet +metadata: +{{- if .Values.server.statefulSet.annotations }} + annotations: + {{ toYaml .Values.server.statefulSet.annotations | nindent 4 }} +{{- end }} + labels: + {{- include "prometheus.server.labels" . | nindent 4 }} + {{- if .Values.server.statefulSet.labels}} + {{ toYaml .Values.server.statefulSet.labels | nindent 4 }} + {{- end}} + name: {{ template "prometheus.server.fullname" . }} + namespace: {{ include "prometheus.namespace" . }} +spec: + {{- if semverCompare ">= 1.27.x" (include "prometheus.kubeVersion" .) }} + persistentVolumeClaimRetentionPolicy: + whenDeleted: {{ ternary "Delete" "Retain" .Values.server.statefulSet.pvcDeleteOnStsDelete }} + whenScaled: {{ ternary "Delete" "Retain" .Values.server.statefulSet.pvcDeleteOnStsScale }} + {{- end }} + serviceName: {{ template "prometheus.server.fullname" . }}-headless + selector: + matchLabels: + {{- include "prometheus.server.matchLabels" . | nindent 6 }} + replicas: {{ .Values.server.replicaCount }} + revisionHistoryLimit: {{ .Values.server.revisionHistoryLimit }} + podManagementPolicy: {{ .Values.server.statefulSet.podManagementPolicy }} + template: + metadata: + {{- if .Values.server.podAnnotations }} + annotations: + {{ toYaml .Values.server.podAnnotations | nindent 8 }} + {{- end }} + labels: + {{- include "prometheus.server.labels" . | nindent 8 }} + {{- if .Values.server.podLabels}} + {{ toYaml .Values.server.podLabels | nindent 8 }} + {{- end}} + spec: +{{- if .Values.server.priorityClassName }} + priorityClassName: "{{ .Values.server.priorityClassName }}" +{{- end }} +{{- if .Values.server.schedulerName }} + schedulerName: "{{ .Values.server.schedulerName }}" +{{- end }} +{{- if semverCompare ">=1.13-0" .Capabilities.KubeVersion.GitVersion }} + {{- if or (.Values.server.enableServiceLinks) (eq (.Values.server.enableServiceLinks | toString) "") }} + enableServiceLinks: true + {{- else }} + enableServiceLinks: false + {{- end }} +{{- end }} + serviceAccountName: {{ template "prometheus.serviceAccountName.server" . }} +{{- if kindIs "bool" .Values.server.automountServiceAccountToken }} + automountServiceAccountToken: {{ .Values.server.automountServiceAccountToken }} +{{- end }} + {{- if .Values.server.extraInitContainers }} + initContainers: +{{ toYaml .Values.server.extraInitContainers | indent 8 }} + {{- end }} + containers: + {{- if .Values.configmapReload.prometheus.enabled }} + - name: {{ template "prometheus.name" . }}-{{ .Values.server.name }}-{{ .Values.configmapReload.prometheus.name }} + {{- if .Values.configmapReload.prometheus.image.digest }} + image: "{{ .Values.configmapReload.prometheus.image.repository }}@{{ .Values.configmapReload.prometheus.image.digest }}" + {{- else }} + image: "{{ .Values.configmapReload.prometheus.image.repository }}:{{ .Values.configmapReload.prometheus.image.tag }}" + {{- end }} + imagePullPolicy: "{{ .Values.configmapReload.prometheus.image.pullPolicy }}" + {{- with .Values.configmapReload.prometheus.containerSecurityContext }} + securityContext: + {{- toYaml . | nindent 12 }} + {{- end }} + args: + - --watched-dir=/etc/config + {{- $default_url := "http://127.0.0.1:9090/-/reload" }} + {{- with .Values.server.prefixURL }} + {{- $default_url = printf "http://127.0.0.1:9090%s/-/reload" . }} + {{- end }} + {{- if .Values.configmapReload.prometheus.containerPort }} + - --listen-address=0.0.0.0:{{ .Values.configmapReload.prometheus.containerPort }} + {{- end }} + - --reload-url={{ default $default_url .Values.configmapReload.reloadUrl }} + {{- range $key, $value := .Values.configmapReload.prometheus.extraArgs }} + {{- if $value }} + - --{{ $key }}={{ $value }} + {{- else }} + - --{{ $key }} + {{- end }} + {{- end }} + {{- range .Values.configmapReload.prometheus.extraVolumeDirs }} + - --watched-dir={{ . }} + {{- end }} + {{- with .Values.configmapReload.env }} + env: + {{- toYaml . | nindent 12 }} + {{- end }} + {{- if .Values.configmapReload.prometheus.containerPort }} + ports: + - containerPort: {{ .Values.configmapReload.prometheus.containerPort }} + {{- if .Values.configmapReload.prometheus.containerPortName }} + name: {{ .Values.configmapReload.prometheus.containerPortName }} + {{- end }} + {{- end }} + {{- with .Values.configmapReload.prometheus.livenessProbe }} + livenessProbe: + {{- toYaml . | nindent 12 }} + {{- end }} + {{- with .Values.configmapReload.prometheus.readinessProbe }} + readinessProbe: + {{- toYaml . | nindent 12 }} + {{- end }} + {{- if .Values.configmapReload.prometheus.startupProbe }} + {{- $startupProbe := omit .Values.configmapReload.prometheus.startupProbe "enabled" }} + startupProbe: + {{- toYaml $startupProbe | nindent 12 }} + {{- end }} + {{- with .Values.configmapReload.prometheus.resources }} + resources: + {{- toYaml . | nindent 12 }} + {{- end }} + volumeMounts: + - name: config-volume + mountPath: /etc/config + readOnly: true + {{- with .Values.configmapReload.prometheus.extraVolumeMounts }} + {{- toYaml . | nindent 12 }} + {{- end }} + {{- range .Values.configmapReload.prometheus.extraConfigmapMounts }} + - name: {{ $.Values.configmapReload.prometheus.name }}-{{ .name }} + mountPath: {{ .mountPath }} + subPath: {{ .subPath }} + readOnly: {{ .readOnly }} + {{- end }} + {{- end }} + + - name: {{ template "prometheus.name" . }}-{{ .Values.server.name }} + {{- if .Values.server.image.digest }} + image: "{{ .Values.server.image.repository }}@{{ .Values.server.image.digest }}" + {{- else }} + image: "{{ .Values.server.image.repository }}:{{ .Values.server.image.tag | default .Chart.AppVersion }}" + {{- end }} + imagePullPolicy: "{{ .Values.server.image.pullPolicy }}" + {{- with .Values.server.command }} + command: + {{- toYaml . | nindent 12 }} + {{- end }} + {{- if .Values.server.env }} + env: +{{ toYaml .Values.server.env | indent 12}} + {{- end }} + args: + {{- if .Values.server.defaultFlagsOverride }} + {{ toYaml .Values.server.defaultFlagsOverride | nindent 12}} + {{- else }} + {{- if .Values.server.prefixURL }} + - --web.route-prefix={{ .Values.server.prefixURL }} + {{- end }} + {{- if .Values.server.retention }} + - --storage.tsdb.retention.time={{ .Values.server.retention }} + {{- end }} + {{- if .Values.server.retentionSize }} + - --storage.tsdb.retention.size={{ .Values.server.retentionSize }} + {{- end }} + - --config.file={{ .Values.server.configPath }} + {{- if .Values.server.storagePath }} + - --storage.tsdb.path={{ .Values.server.storagePath }} + {{- else }} + - --storage.tsdb.path={{ .Values.server.persistentVolume.mountPath }} + {{- end }} + - --web.console.libraries=/etc/prometheus/console_libraries + - --web.console.templates=/etc/prometheus/consoles + {{- range .Values.server.extraFlags }} + - --{{ . }} + {{- end }} + {{- range $key, $value := .Values.server.extraArgs }} + {{- if $value }} + - --{{ $key }}={{ $value }} + {{- else }} + - --{{ $key }} + {{- end }} + {{- end }} + {{- if .Values.server.baseURL }} + - --web.external-url={{ .Values.server.baseURL }} + {{- end }} + {{- end }} + ports: + - containerPort: 9090 + {{- if .Values.server.portName }} + name: {{ .Values.server.portName }} + {{- end }} + {{- if .Values.server.hostPort }} + hostPort: {{ .Values.server.hostPort }} + {{- end }} + readinessProbe: + {{- if not .Values.server.tcpSocketProbeEnabled }} + httpGet: + path: {{ .Values.server.prefixURL }}/-/ready + port: 9090 + scheme: {{ .Values.server.probeScheme }} + {{- with .Values.server.probeHeaders }} + httpHeaders: +{{- toYaml . | nindent 14 }} + {{- end }} + {{- else }} + tcpSocket: + port: 9090 + {{- end }} + initialDelaySeconds: {{ .Values.server.readinessProbeInitialDelay }} + periodSeconds: {{ .Values.server.readinessProbePeriodSeconds }} + timeoutSeconds: {{ .Values.server.readinessProbeTimeout }} + failureThreshold: {{ .Values.server.readinessProbeFailureThreshold }} + successThreshold: {{ .Values.server.readinessProbeSuccessThreshold }} + livenessProbe: + {{- if not .Values.server.tcpSocketProbeEnabled }} + httpGet: + path: {{ .Values.server.prefixURL }}/-/healthy + port: 9090 + scheme: {{ .Values.server.probeScheme }} + {{- with .Values.server.probeHeaders }} + httpHeaders: +{{- toYaml . | nindent 14 }} + {{- end }} + {{- else }} + tcpSocket: + port: 9090 + {{- end }} + initialDelaySeconds: {{ .Values.server.livenessProbeInitialDelay }} + periodSeconds: {{ .Values.server.livenessProbePeriodSeconds }} + timeoutSeconds: {{ .Values.server.livenessProbeTimeout }} + failureThreshold: {{ .Values.server.livenessProbeFailureThreshold }} + successThreshold: {{ .Values.server.livenessProbeSuccessThreshold }} + {{- if .Values.server.startupProbe.enabled }} + startupProbe: + {{- if not .Values.server.tcpSocketProbeEnabled }} + httpGet: + path: {{ .Values.server.prefixURL }}/-/healthy + port: 9090 + scheme: {{ .Values.server.probeScheme }} + {{- if .Values.server.probeHeaders }} + httpHeaders: + {{- range .Values.server.probeHeaders}} + - name: {{ .name }} + value: {{ .value }} + {{- end }} + {{- end }} + {{- else }} + tcpSocket: + port: 9090 + {{- end }} + failureThreshold: {{ .Values.server.startupProbe.failureThreshold }} + periodSeconds: {{ .Values.server.startupProbe.periodSeconds }} + timeoutSeconds: {{ .Values.server.startupProbe.timeoutSeconds }} + {{- end }} + {{- with .Values.server.resources }} + resources: + {{- toYaml . | nindent 12 }} + {{- end }} + volumeMounts: + - name: config-volume + mountPath: /etc/config + - name: {{ ternary .Values.server.persistentVolume.statefulSetNameOverride "storage-volume" (and .Values.server.persistentVolume.enabled (not (empty .Values.server.persistentVolume.statefulSetNameOverride))) }} + mountPath: {{ .Values.server.persistentVolume.mountPath }} + subPath: "{{ .Values.server.persistentVolume.subPath }}" + {{- range .Values.server.extraHostPathMounts }} + - name: {{ .name }} + mountPath: {{ .mountPath }} + subPath: {{ .subPath }} + readOnly: {{ .readOnly }} + {{- end }} + {{- range .Values.server.extraConfigmapMounts }} + - name: {{ $.Values.server.name }}-{{ .name }} + mountPath: {{ .mountPath }} + subPath: {{ .subPath }} + readOnly: {{ .readOnly }} + {{- end }} + {{- range .Values.server.extraSecretMounts }} + - name: {{ .name }} + mountPath: {{ .mountPath }} + subPath: {{ .subPath }} + readOnly: {{ .readOnly }} + {{- end }} + {{- if .Values.server.extraVolumeMounts }} + {{ toYaml .Values.server.extraVolumeMounts | nindent 12 }} + {{- end }} + {{- with .Values.server.containerSecurityContext }} + securityContext: + {{- toYaml . | nindent 12 }} + {{- end }} + {{- if .Values.server.sidecarContainers }} + {{- range $name, $spec := .Values.server.sidecarContainers }} + - name: {{ $name }} + {{- if kindIs "string" $spec }} + {{- tpl $spec $ | nindent 10 }} + {{- else }} + {{- toYaml $spec | nindent 10 }} + {{- end }} + {{- end }} + {{- end }} + hostNetwork: {{ .Values.server.hostNetwork }} + {{- if .Values.server.dnsPolicy }} + dnsPolicy: {{ .Values.server.dnsPolicy }} + {{- end }} + {{- if .Values.imagePullSecrets }} + imagePullSecrets: +{{ toYaml .Values.imagePullSecrets | indent 8 }} + {{- end }} + {{- if .Values.server.nodeSelector }} + nodeSelector: +{{ toYaml .Values.server.nodeSelector | indent 8 }} + {{- end }} + {{- if .Values.server.hostAliases }} + hostAliases: +{{ toYaml .Values.server.hostAliases | indent 8 }} + {{- end }} + {{- if .Values.server.dnsConfig }} + dnsConfig: +{{ toYaml .Values.server.dnsConfig | indent 8 }} + {{- end }} + {{- with .Values.server.securityContext }} + securityContext: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- if .Values.server.tolerations }} + tolerations: +{{ toYaml .Values.server.tolerations | indent 8 }} + {{- end }} + {{- if or .Values.server.affinity .Values.server.podAntiAffinity }} + affinity: + {{- end }} + {{- with .Values.server.affinity }} + {{- toYaml . | nindent 8 }} + {{- end }} + {{- if eq .Values.server.podAntiAffinity "hard" }} + podAntiAffinity: + requiredDuringSchedulingIgnoredDuringExecution: + - topologyKey: {{ .Values.server.podAntiAffinityTopologyKey }} + labelSelector: + matchExpressions: + - {key: app.kubernetes.io/name, operator: In, values: [{{ template "prometheus.name" . }}]} + {{- else if eq .Values.server.podAntiAffinity "soft" }} + podAntiAffinity: + preferredDuringSchedulingIgnoredDuringExecution: + - weight: 100 + podAffinityTerm: + topologyKey: {{ .Values.server.podAntiAffinityTopologyKey }} + labelSelector: + matchExpressions: + - {key: app.kubernetes.io/name, operator: In, values: [{{ template "prometheus.name" . }}]} + {{- end }} + {{- with .Values.server.topologySpreadConstraints }} + topologySpreadConstraints: + {{- toYaml . | nindent 8 }} + {{- end }} + terminationGracePeriodSeconds: {{ .Values.server.terminationGracePeriodSeconds }} + volumes: + - name: config-volume + {{- if empty .Values.server.configFromSecret }} + configMap: + name: {{ if .Values.server.configMapOverrideName }}{{ .Release.Name }}-{{ .Values.server.configMapOverrideName }}{{- else }}{{ template "prometheus.server.fullname" . }}{{- end }} + {{- else }} + secret: + secretName: {{ .Values.server.configFromSecret }} + {{- end }} + {{- range .Values.server.extraHostPathMounts }} + - name: {{ .name }} + hostPath: + path: {{ .hostPath }} + {{- end }} + {{- range .Values.configmapReload.prometheus.extraConfigmapMounts }} + - name: {{ $.Values.configmapReload.prometheus.name }}-{{ .name }} + configMap: + name: {{ .configMap }} + {{- end }} + {{- range .Values.server.extraConfigmapMounts }} + - name: {{ $.Values.server.name }}-{{ .name }} + configMap: + name: {{ .configMap }} + {{- end }} + {{- range .Values.server.extraSecretMounts }} + - name: {{ .name }} + secret: + secretName: {{ .secretName }} + {{- with .optional }} + optional: {{ . }} + {{- end }} + {{- end }} + {{- range .Values.configmapReload.prometheus.extraConfigmapMounts }} + - name: {{ .name }} + configMap: + name: {{ .configMap }} + {{- with .optional }} + optional: {{ . }} + {{- end }} + {{- end }} +{{- if .Values.server.extraVolumes }} +{{ toYaml .Values.server.extraVolumes | indent 8}} +{{- end }} +{{- if .Values.server.persistentVolume.enabled }} + volumeClaimTemplates: + - apiVersion: v1 + kind: PersistentVolumeClaim + metadata: + name: {{ .Values.server.persistentVolume.statefulSetNameOverride | default "storage-volume" }} + {{- if .Values.server.persistentVolume.annotations }} + annotations: +{{ toYaml .Values.server.persistentVolume.annotations | indent 10 }} + {{- end }} + {{- if .Values.server.persistentVolume.labels }} + labels: +{{ toYaml .Values.server.persistentVolume.labels | indent 10 }} + {{- end }} + spec: + accessModes: +{{ toYaml .Values.server.persistentVolume.accessModes | indent 10 }} + resources: + requests: + storage: "{{ .Values.server.persistentVolume.size }}" + {{- if .Values.server.persistentVolume.storageClass }} + {{- if (eq "-" .Values.server.persistentVolume.storageClass) }} + storageClassName: "" + {{- else }} + storageClassName: "{{ .Values.server.persistentVolume.storageClass }}" + {{- end }} + {{- end }} +{{- else }} + - name: storage-volume + emptyDir: + {{- if .Values.server.emptyDir.sizeLimit }} + sizeLimit: {{ .Values.server.emptyDir.sizeLimit }} + {{- else }} + {} + {{- end -}} +{{- end }} +{{- end }} diff --git a/charts/kasten/k10/7.5.401/charts/prometheus/templates/vpa.yaml b/charts/kasten/k10/7.5.401/charts/prometheus/templates/vpa.yaml new file mode 100644 index 0000000000..cd07ad8b7d --- /dev/null +++ b/charts/kasten/k10/7.5.401/charts/prometheus/templates/vpa.yaml @@ -0,0 +1,26 @@ +{{- if .Values.server.verticalAutoscaler.enabled -}} +{{- if .Capabilities.APIVersions.Has "autoscaling.k8s.io/v1/VerticalPodAutoscaler" }} +apiVersion: autoscaling.k8s.io/v1 +{{- else }} +apiVersion: autoscaling.k8s.io/v1beta2 +{{- end }} +kind: VerticalPodAutoscaler +metadata: + name: {{ template "prometheus.server.fullname" . }}-vpa + namespace: {{ include "prometheus.namespace" . }} + labels: + {{- include "prometheus.server.labels" . | nindent 4 }} +spec: + targetRef: + apiVersion: "apps/v1" +{{- if .Values.server.statefulSet.enabled }} + kind: StatefulSet +{{- else }} + kind: Deployment +{{- end }} + name: {{ template "prometheus.server.fullname" . }} + updatePolicy: + updateMode: {{ .Values.server.verticalAutoscaler.updateMode | default "Off" | quote }} + resourcePolicy: + containerPolicies: {{ .Values.server.verticalAutoscaler.containerPolicies | default list | toYaml | trim | nindent 4 }} +{{- end -}} diff --git a/charts/kasten/k10/7.5.401/charts/prometheus/values.schema.json b/charts/kasten/k10/7.5.401/charts/prometheus/values.schema.json new file mode 100644 index 0000000000..b2d8af26c3 --- /dev/null +++ b/charts/kasten/k10/7.5.401/charts/prometheus/values.schema.json @@ -0,0 +1,752 @@ +{ + "$schema": "http://json-schema.org/schema#", + "type": "object", + "properties": { + "alertRelabelConfigs": { + "type": "object" + }, + "alertmanager": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean" + }, + "persistence": { + "type": "object", + "properties": { + "size": { + "type": "string" + } + } + }, + "podSecurityContext": { + "type": "object", + "properties": { + "fsGroup": { + "type": "integer" + }, + "runAsGroup": { + "type": "integer" + }, + "runAsNonRoot": { + "type": "boolean" + }, + "runAsUser": { + "type": "integer" + } + } + } + } + }, + "configmapReload": { + "type": "object", + "properties": { + "env": { + "type": "array" + }, + "prometheus": { + "type": "object", + "properties": { + "containerSecurityContext": { + "type": "object" + }, + "enabled": { + "type": "boolean" + }, + "extraArgs": { + "type": "object" + }, + "extraConfigmapMounts": { + "type": "array" + }, + "extraVolumeDirs": { + "type": "array" + }, + "extraVolumeMounts": { + "type": "array" + }, + "image": { + "type": "object", + "properties": { + "digest": { + "type": "string" + }, + "pullPolicy": { + "type": "string" + }, + "repository": { + "type": "string" + }, + "tag": { + "type": "string" + } + } + }, + "name": { + "type": "string" + }, + "resources": { + "type": "object" + } + } + }, + "reloadUrl": { + "type": "string" + } + } + }, + "extraManifests": { + "type": "array" + }, + "extraScrapeConfigs": { + "type": "string" + }, + "forceNamespace": { + "type": "string" + }, + "imagePullSecrets": { + "type": "array" + }, + "kube-state-metrics": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean" + } + } + }, + "networkPolicy": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean" + } + } + }, + "podSecurityPolicy": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean" + } + } + }, + "prometheus-node-exporter": { + "type": "object", + "properties": { + "containerSecurityContext": { + "type": "object", + "properties": { + "allowPrivilegeEscalation": { + "type": "boolean" + } + } + }, + "enabled": { + "type": "boolean" + }, + "rbac": { + "type": "object", + "properties": { + "pspEnabled": { + "type": "boolean" + } + } + } + } + }, + "prometheus-pushgateway": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean" + }, + "serviceAnnotations": { + "type": "object", + "properties": { + "prometheus.io/probe": { + "type": "string" + } + } + } + } + }, + "rbac": { + "type": "object", + "properties": { + "create": { + "type": "boolean" + } + } + }, + "ruleFiles": { + "type": "object" + }, + "server": { + "type": "object", + "properties": { + "affinity": { + "type": "object" + }, + "alertmanagers": { + "type": "array" + }, + "baseURL": { + "type": "string" + }, + "clusterRoleNameOverride": { + "type": "string" + }, + "command": { + "type": "array" + }, + "configMapAnnotations": { + "type": "object" + }, + "configMapOverrideName": { + "type": "string" + }, + "configPath": { + "type": "string" + }, + "containerSecurityContext": { + "type": "object" + }, + "defaultFlagsOverride": { + "type": "array" + }, + "deploymentAnnotations": { + "type": "object" + }, + "dnsConfig": { + "type": "object" + }, + "dnsPolicy": { + "type": "string" + }, + "emptyDir": { + "type": "object", + "properties": { + "sizeLimit": { + "type": "string" + } + } + }, + "enableServiceLinks": { + "type": "boolean" + }, + "env": { + "type": "array" + }, + "exemplars": { + "type": "object" + }, + "extraArgs": { + "type": "object" + }, + "extraConfigmapLabels": { + "type": "object" + }, + "extraConfigmapMounts": { + "type": "array" + }, + "extraFlags": { + "type": "array", + "items": { + "type": "string" + } + }, + "extraHostPathMounts": { + "type": "array" + }, + "extraInitContainers": { + "type": "array" + }, + "extraSecretMounts": { + "type": "array" + }, + "extraVolumeMounts": { + "type": "array" + }, + "extraVolumes": { + "type": "array" + }, + "fullnameOverride": { + "type": "string" + }, + "global": { + "type": "object", + "properties": { + "evaluation_interval": { + "type": "string" + }, + "scrape_interval": { + "type": "string" + }, + "scrape_timeout": { + "type": "string" + } + } + }, + "hostAliases": { + "type": "array" + }, + "hostNetwork": { + "type": "boolean" + }, + "image": { + "type": "object", + "properties": { + "digest": { + "type": "string" + }, + "pullPolicy": { + "type": "string" + }, + "repository": { + "type": "string" + }, + "tag": { + "type": "string" + } + } + }, + "ingress": { + "type": "object", + "properties": { + "annotations": { + "type": "object" + }, + "enabled": { + "type": "boolean" + }, + "extraLabels": { + "type": "object" + }, + "extraPaths": { + "type": "array" + }, + "hosts": { + "type": "array" + }, + "path": { + "type": "string" + }, + "pathType": { + "type": "string" + }, + "tls": { + "type": "array" + } + } + }, + "livenessProbeFailureThreshold": { + "type": "integer" + }, + "livenessProbeInitialDelay": { + "type": "integer" + }, + "livenessProbePeriodSeconds": { + "type": "integer" + }, + "livenessProbeSuccessThreshold": { + "type": "integer" + }, + "livenessProbeTimeout": { + "type": "integer" + }, + "name": { + "type": "string" + }, + "nodeSelector": { + "type": "object" + }, + "persistentVolume": { + "type": "object", + "properties": { + "accessModes": { + "type": "array", + "items": { + "type": "string" + } + }, + "annotations": { + "type": "object" + }, + "enabled": { + "type": "boolean" + }, + "existingClaim": { + "type": "string" + }, + "labels": { + "type": "object" + }, + "mountPath": { + "type": "string" + }, + "size": { + "type": "string" + }, + "statefulSetNameOverride": { + "type": "string" + }, + "subPath": { + "type": "string" + } + } + }, + "podAnnotations": { + "type": "object" + }, + "podAntiAffinity": { + "type": "string", + "enum": ["", "soft", "hard"], + "default": "" + }, + "podAntiAffinityTopologyKey": { + "type": "string" + }, + "podDisruptionBudget": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean" + }, + "maxUnavailable": { + "type": [ + "string", + "integer" + ] + } + } + }, + "podLabels": { + "type": "object" + }, + "podSecurityPolicy": { + "type": "object", + "properties": { + "annotations": { + "type": "object" + } + } + }, + "portName": { + "type": "string" + }, + "prefixURL": { + "type": "string" + }, + "priorityClassName": { + "type": "string" + }, + "probeHeaders": { + "type": "array" + }, + "probeScheme": { + "type": "string" + }, + "readinessProbeFailureThreshold": { + "type": "integer" + }, + "readinessProbeInitialDelay": { + "type": "integer" + }, + "readinessProbePeriodSeconds": { + "type": "integer" + }, + "readinessProbeSuccessThreshold": { + "type": "integer" + }, + "readinessProbeTimeout": { + "type": "integer" + }, + "releaseNamespace": { + "type": "boolean" + }, + "remoteRead": { + "type": "array" + }, + "remoteWrite": { + "type": "array" + }, + "replicaCount": { + "type": "integer" + }, + "resources": { + "type": "object" + }, + "retention": { + "type": "string" + }, + "retentionSize": { + "type": "string" + }, + "revisionHistoryLimit": { + "type": "integer" + }, + "securityContext": { + "type": "object", + "properties": { + "fsGroup": { + "type": "integer" + }, + "runAsGroup": { + "type": "integer" + }, + "runAsNonRoot": { + "type": "boolean" + }, + "runAsUser": { + "type": "integer" + } + } + }, + "service": { + "type": "object", + "properties": { + "additionalPorts": { + "type": "array" + }, + "annotations": { + "type": "object" + }, + "clusterIP": { + "type": "string" + }, + "enabled": { + "type": "boolean" + }, + "externalIPs": { + "type": "array" + }, + "gRPC": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean" + }, + "servicePort": { + "type": "integer" + } + } + }, + "labels": { + "type": "object" + }, + "loadBalancerIP": { + "type": "string" + }, + "loadBalancerSourceRanges": { + "type": "array" + }, + "servicePort": { + "type": "integer" + }, + "sessionAffinity": { + "type": "string" + }, + "statefulsetReplica": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean" + }, + "replica": { + "type": "integer" + } + } + }, + "type": { + "type": "string" + } + } + }, + "sidecarContainers": { + "type": "object" + }, + "sidecarTemplateValues": { + "type": "object" + }, + "startupProbe": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean" + }, + "failureThreshold": { + "type": "integer" + }, + "periodSeconds": { + "type": "integer" + }, + "timeoutSeconds": { + "type": "integer" + } + } + }, + "statefulSet": { + "type": "object", + "properties": { + "annotations": { + "type": "object" + }, + "enabled": { + "type": "boolean" + }, + "headless": { + "type": "object", + "properties": { + "annotations": { + "type": "object" + }, + "gRPC": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean" + }, + "servicePort": { + "type": "integer" + } + } + }, + "labels": { + "type": "object" + }, + "servicePort": { + "type": "integer" + } + } + }, + "labels": { + "type": "object" + }, + "podManagementPolicy": { + "type": "string" + }, + "pvcDeleteOnStsDelete": { + "type": "boolean" + }, + "pvcDeleteOnStsScale": { + "type": "boolean" + } + } + }, + "storagePath": { + "type": "string" + }, + "strategy": { + "type": "object", + "properties": { + "type": { + "type": "string" + } + } + }, + "tcpSocketProbeEnabled": { + "type": "boolean" + }, + "terminationGracePeriodSeconds": { + "type": "integer" + }, + "tolerations": { + "type": "array" + }, + "topologySpreadConstraints": { + "type": "array" + }, + "tsdb": { + "type": "object" + }, + "verticalAutoscaler": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean" + } + } + } + } + }, + "scrapeConfigFiles": { + "type": "array" + }, + "serverFiles": { + "type": "object", + "properties": { + "alerting_rules.yml": { + "type": "object" + }, + "alerts": { + "type": "object" + }, + "prometheus.yml": { + "type": "object", + "properties": { + "rule_files": { + "type": "array", + "items": { + "type": "string" + } + }, + "scrape_configs": { + "type": "array", + "items": { + "type": "object", + "properties": { + "job_name": { + "type": "string" + }, + "static_configs": { + "type": "array", + "items": { + "type": "object", + "properties": { + "targets": { + "type": "array", + "items": { + "type": "string" + } + } + } + } + } + } + } + } + } + }, + "recording_rules.yml": { + "type": "object" + }, + "rules": { + "type": "object" + } + } + }, + "serviceAccounts": { + "type": "object", + "properties": { + "server": { + "type": "object", + "properties": { + "annotations": { + "type": "object" + }, + "create": { + "type": "boolean" + }, + "name": { + "type": "string" + }, + "automountServiceAccountToken": { + "type": "boolean" + } + } + } + } + } + } +} diff --git a/charts/kasten/k10/7.5.401/charts/prometheus/values.yaml b/charts/kasten/k10/7.5.401/charts/prometheus/values.yaml new file mode 100644 index 0000000000..a9bc875b2b --- /dev/null +++ b/charts/kasten/k10/7.5.401/charts/prometheus/values.yaml @@ -0,0 +1,1315 @@ +# yaml-language-server: $schema=values.schema.json +# Default values for prometheus. +# This is a YAML-formatted file. +# Declare variables to be passed into your templates. + +rbac: + create: true + +podSecurityPolicy: + enabled: false + +imagePullSecrets: [] +# - name: "image-pull-secret" + +## Define serviceAccount names for components. Defaults to component's fully qualified name. +## +serviceAccounts: + server: + create: true + name: "" + annotations: {} + + ## Opt out of automounting Kubernetes API credentials. + ## It will be overriden by server.automountServiceAccountToken value, if set. + # automountServiceAccountToken: false + +## Additional labels to attach to all resources +commonMetaLabels: {} + +## Monitors ConfigMap changes and POSTs to a URL +## Ref: https://github.com/prometheus-operator/prometheus-operator/tree/main/cmd/prometheus-config-reloader +## +configmapReload: + ## URL for configmap-reload to use for reloads + ## + reloadUrl: "" + + ## env sets environment variables to pass to the container. Can be set as name/value pairs, + ## read from secrets or configmaps. + env: [] + # - name: SOMEVAR + # value: somevalue + # - name: PASSWORD + # valueFrom: + # secretKeyRef: + # name: mysecret + # key: password + # optional: false + + prometheus: + ## If false, the configmap-reload container will not be deployed + ## + enabled: true + + ## configmap-reload container name + ## + name: configmap-reload + + ## configmap-reload container image + ## + image: + repository: quay.io/prometheus-operator/prometheus-config-reloader + tag: v0.79.0 + # When digest is set to a non-empty value, images will be pulled by digest (regardless of tag value). + digest: "" + pullPolicy: IfNotPresent + + ## config-reloader's container port and port name for probes and metrics + containerPort: 8080 + containerPortName: metrics + + ## Additional configmap-reload container arguments + ## Set to null for argumentless flags + ## + extraArgs: {} + + ## Additional configmap-reload volume directories + ## + extraVolumeDirs: [] + + ## Additional configmap-reload volume mounts + ## + extraVolumeMounts: [] + + ## Additional configmap-reload mounts + ## + extraConfigmapMounts: [] + # - name: prometheus-alerts + # mountPath: /etc/alerts.d + # subPath: "" + # configMap: prometheus-alerts + # readOnly: true + + ## Security context to be added to configmap-reload container + containerSecurityContext: {} + + ## Settings for Prometheus reloader's readiness, liveness and startup probes + ## Ref: https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/ + ## + + livenessProbe: + httpGet: + path: /healthz + port: metrics + scheme: HTTP + periodSeconds: 10 + initialDelaySeconds: 2 + + readinessProbe: + httpGet: + path: /healthz + port: metrics + scheme: HTTP + periodSeconds: 10 + + startupProbe: + enabled: false + httpGet: + path: /healthz + port: metrics + scheme: HTTP + periodSeconds: 10 + + ## configmap-reload resource requests and limits + ## Ref: http://kubernetes.io/docs/user-guide/compute-resources/ + ## + resources: + requests: + cpu: 10m + memory: 50Mi + +server: + ## Prometheus server container name + ## + name: server + + ## Opt out of automounting Kubernetes API credentials. + ## If set it will override serviceAccounts.server.automountServiceAccountToken value for ServiceAccount. + # automountServiceAccountToken: false + + ## Use a ClusterRole (and ClusterRoleBinding) + ## - If set to false - we define a RoleBinding in the defined namespaces ONLY + ## + ## NB: because we need a Role with nonResourceURL's ("/metrics") - you must get someone with Cluster-admin privileges to define this role for you, before running with this setting enabled. + ## This makes prometheus work - for users who do not have ClusterAdmin privs, but wants prometheus to operate on their own namespaces, instead of clusterwide. + ## + ## You MUST also set namespaces to the ones you have access to and want monitored by Prometheus. + ## + # useExistingClusterRoleName: nameofclusterrole + + ## If set it will override prometheus.server.fullname value for ClusterRole and ClusterRoleBinding + ## + clusterRoleNameOverride: "" + + # Enable only the release namespace for monitoring. By default all namespaces are monitored. + # If releaseNamespace and namespaces are both set a merged list will be monitored. + releaseNamespace: false + + ## namespaces to monitor (instead of monitoring all - clusterwide). Needed if you want to run without Cluster-admin privileges. + # namespaces: + # - yournamespace + + # sidecarContainers - add more containers to prometheus server + # Key/Value where Key is the sidecar `- name: ` + # Example: + # sidecarContainers: + # webserver: + # image: nginx + # OR for adding OAuth authentication to Prometheus + # sidecarContainers: + # oauth-proxy: + # image: quay.io/oauth2-proxy/oauth2-proxy:v7.1.2 + # args: + # - --upstream=http://127.0.0.1:9090 + # - --http-address=0.0.0.0:8081 + # - ... + # ports: + # - containerPort: 8081 + # name: oauth-proxy + # protocol: TCP + # resources: {} + sidecarContainers: {} + + # sidecarTemplateValues - context to be used in template for sidecarContainers + # Example: + # sidecarTemplateValues: *your-custom-globals + # sidecarContainers: + # webserver: |- + # {{ include "webserver-container-template" . }} + # Template for `webserver-container-template` might looks like this: + # image: "{{ .Values.server.sidecarTemplateValues.repository }}:{{ .Values.server.sidecarTemplateValues.tag }}" + # ... + # + sidecarTemplateValues: {} + + ## Prometheus server container image + ## + image: + repository: quay.io/prometheus/prometheus + # if not set appVersion field from Chart.yaml is used + tag: "" + # When digest is set to a non-empty value, images will be pulled by digest (regardless of tag value). + digest: "" + pullPolicy: IfNotPresent + + ## Prometheus server command + ## + command: [] + + ## prometheus server priorityClassName + ## + priorityClassName: "" + + ## EnableServiceLinks indicates whether information about services should be injected + ## into pod's environment variables, matching the syntax of Docker links. + ## WARNING: the field is unsupported and will be skipped in K8s prior to v1.13.0. + ## + enableServiceLinks: true + + ## The URL prefix at which the container can be accessed. Useful in the case the '-web.external-url' includes a slug + ## so that the various internal URLs are still able to access as they are in the default case. + ## (Optional) + prefixURL: "" + + ## External URL which can access prometheus + ## Maybe same with Ingress host name + baseURL: "" + + ## Additional server container environment variables + ## + ## You specify this manually like you would a raw deployment manifest. + ## This means you can bind in environment variables from secrets. + ## + ## e.g. static environment variable: + ## - name: DEMO_GREETING + ## value: "Hello from the environment" + ## + ## e.g. secret environment variable: + ## - name: USERNAME + ## valueFrom: + ## secretKeyRef: + ## name: mysecret + ## key: username + env: [] + + # List of flags to override default parameters, e.g: + # - --enable-feature=agent + # - --storage.agent.retention.max-time=30m + # - --config.file=/etc/config/prometheus.yml + defaultFlagsOverride: [] + + extraFlags: + - web.enable-lifecycle + ## web.enable-admin-api flag controls access to the administrative HTTP API which includes functionality such as + ## deleting time series. This is disabled by default. + # - web.enable-admin-api + ## + ## storage.tsdb.no-lockfile flag controls BD locking + # - storage.tsdb.no-lockfile + ## + ## storage.tsdb.wal-compression flag enables compression of the write-ahead log (WAL) + # - storage.tsdb.wal-compression + + ## Path to a configuration file on prometheus server container FS + configPath: /etc/config/prometheus.yml + + ### The data directory used by prometheus to set --storage.tsdb.path + ### When empty server.persistentVolume.mountPath is used instead + storagePath: "" + + global: + ## How frequently to scrape targets by default + ## + scrape_interval: 1m + ## How long until a scrape request times out + ## + scrape_timeout: 10s + ## How frequently to evaluate rules + ## + evaluation_interval: 1m + ## https://prometheus.io/docs/prometheus/latest/configuration/configuration/#remote_write + ## + remoteWrite: [] + ## https://prometheus.io/docs/prometheus/latest/configuration/configuration/#remote_read + ## + remoteRead: [] + + ## https://prometheus.io/docs/prometheus/latest/configuration/configuration/#tsdb + ## + tsdb: {} + # out_of_order_time_window: 0s + + ## https://prometheus.io/docs/prometheus/latest/configuration/configuration/#exemplars + ## Must be enabled via --enable-feature=exemplar-storage + ## + exemplars: {} + # max_exemplars: 100000 + + ## Custom HTTP headers for Liveness/Readiness/Startup Probe + ## + ## Useful for providing HTTP Basic Auth to healthchecks + probeHeaders: [] + # - name: "Authorization" + # value: "Bearer ABCDEabcde12345" + + ## Additional Prometheus server container arguments + ## Set to null for argumentless flags + ## + extraArgs: {} + # web.enable-remote-write-receiver: null + + ## Additional InitContainers to initialize the pod + ## + extraInitContainers: [] + + ## Additional Prometheus server Volume mounts + ## + extraVolumeMounts: [] + + ## Additional Prometheus server Volumes + ## + extraVolumes: [] + + ## Additional Prometheus server hostPath mounts + ## + extraHostPathMounts: [] + # - name: certs-dir + # mountPath: /etc/kubernetes/certs + # subPath: "" + # hostPath: /etc/kubernetes/certs + # readOnly: true + + extraConfigmapMounts: [] + # - name: certs-configmap + # mountPath: /prometheus + # subPath: "" + # configMap: certs-configmap + # readOnly: true + + ## Additional Prometheus server Secret mounts + # Defines additional mounts with secrets. Secrets must be manually created in the namespace. + extraSecretMounts: [] + # - name: secret-files + # mountPath: /etc/secrets + # subPath: "" + # secretName: prom-secret-files + # readOnly: true + + ## ConfigMap override where fullname is {{.Release.Name}}-{{.Values.server.configMapOverrideName}} + ## Defining configMapOverrideName will cause templates/server-configmap.yaml + ## to NOT generate a ConfigMap resource + ## + configMapOverrideName: "" + + ## Extra labels for Prometheus server ConfigMap (ConfigMap that holds serverFiles) + extraConfigmapLabels: {} + + ## Override the prometheus.server.fullname for all objects related to the Prometheus server + fullnameOverride: "" + + ingress: + ## If true, Prometheus server Ingress will be created + ## + enabled: false + + # For Kubernetes >= 1.18 you should specify the ingress-controller via the field ingressClassName + # See https://kubernetes.io/blog/2020/04/02/improvements-to-the-ingress-api-in-kubernetes-1.18/#specifying-the-class-of-an-ingress + # ingressClassName: nginx + + ## Prometheus server Ingress annotations + ## + annotations: {} + # kubernetes.io/ingress.class: nginx + # kubernetes.io/tls-acme: 'true' + + ## Prometheus server Ingress additional labels + ## + extraLabels: {} + + ## Redirect ingress to an additional defined port on the service + # servicePort: 8081 + + ## Prometheus server Ingress hostnames with optional path (passed through tpl) + ## Must be provided if Ingress is enabled + ## + hosts: [] + # - prometheus.domain.com + # - domain.com/prometheus + + path: / + + # pathType is only for k8s >= 1.18 + pathType: Prefix + + ## Extra paths to prepend to every host configuration. This is useful when working with annotation based services. (passed through tpl) + extraPaths: [] + # - path: /* + # backend: + # serviceName: ssl-redirect + # servicePort: use-annotation + + ## Prometheus server Ingress TLS configuration (hosts passed through tpl) + ## Secrets must be manually created in the namespace + ## + tls: [] + # - secretName: prometheus-server-tls + # hosts: + # - prometheus.domain.com + + ## Server Deployment Strategy type + strategy: + type: Recreate + + ## hostAliases allows adding entries to /etc/hosts inside the containers + hostAliases: [] + # - ip: "127.0.0.1" + # hostnames: + # - "example.com" + + ## Node tolerations for server scheduling to nodes with taints + ## Ref: https://kubernetes.io/docs/concepts/scheduling-eviction/taint-and-toleration/ + ## + tolerations: [] + # - key: "key" + # operator: "Equal|Exists" + # value: "value" + # effect: "NoSchedule|PreferNoSchedule|NoExecute(1.6 only)" + + ## Node labels for Prometheus server pod assignment + ## Ref: https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/ + ## + nodeSelector: {} + + ## Pod affinity + ## + affinity: {} + + ## Pod anti-affinity can prevent the scheduler from placing Prometheus server replicas on the same node. + ## The value "soft" means that the scheduler should *prefer* to not schedule two replica pods onto the same node but no guarantee is provided. + ## The value "hard" means that the scheduler is *required* to not schedule two replica pods onto the same node. + ## The default value "" will disable pod anti-affinity so that no anti-affinity rules will be configured (unless set in `server.affinity`). + ## + podAntiAffinity: "" + + ## If anti-affinity is enabled sets the topologyKey to use for anti-affinity. + ## This can be changed to, for example, failure-domain.beta.kubernetes.io/zone + ## + podAntiAffinityTopologyKey: kubernetes.io/hostname + + ## Pod topology spread constraints + ## ref. https://kubernetes.io/docs/concepts/scheduling-eviction/topology-spread-constraints/ + topologySpreadConstraints: [] + + ## PodDisruptionBudget settings + ## ref: https://kubernetes.io/docs/concepts/workloads/pods/disruptions/ + ## + podDisruptionBudget: + enabled: false + # maxUnavailable: 1 + # minAvailable: 1 + ## unhealthyPodEvictionPolicy is available since 1.27.0 (beta) + ## https://kubernetes.io/docs/tasks/run-application/configure-pdb/#unhealthy-pod-eviction-policy + # unhealthyPodEvictionPolicy: IfHealthyBudget + + ## Use an alternate scheduler, e.g. "stork". + ## ref: https://kubernetes.io/docs/tasks/administer-cluster/configure-multiple-schedulers/ + ## + # schedulerName: + + persistentVolume: + ## If true, Prometheus server will create/use a Persistent Volume Claim + ## If false, use emptyDir + ## + enabled: true + + ## If set it will override the name of the created persistent volume claim + ## generated by the stateful set. + ## + statefulSetNameOverride: "" + + ## Prometheus server data Persistent Volume access modes + ## Must match those of existing PV or dynamic provisioner + ## Ref: http://kubernetes.io/docs/user-guide/persistent-volumes/ + ## + accessModes: + - ReadWriteOnce + + ## Prometheus server data Persistent Volume labels + ## + labels: {} + + ## Prometheus server data Persistent Volume annotations + ## + annotations: {} + + ## Prometheus server data Persistent Volume existing claim name + ## Requires server.persistentVolume.enabled: true + ## If defined, PVC must be created manually before volume will be bound + existingClaim: "" + + ## Prometheus server data Persistent Volume mount root path + ## + mountPath: /data + + ## Prometheus server data Persistent Volume size + ## + size: 8Gi + + ## Prometheus server data Persistent Volume Storage Class + ## If defined, storageClassName: + ## If set to "-", storageClassName: "", which disables dynamic provisioning + ## If undefined (the default) or set to null, no storageClassName spec is + ## set, choosing the default provisioner. (gp2 on AWS, standard on + ## GKE, AWS & OpenStack) + ## + # storageClass: "-" + + ## Prometheus server data Persistent Volume Binding Mode + ## If defined, volumeBindingMode: + ## If undefined (the default) or set to null, no volumeBindingMode spec is + ## set, choosing the default mode. + ## + # volumeBindingMode: "" + + ## Subdirectory of Prometheus server data Persistent Volume to mount + ## Useful if the volume's root directory is not empty + ## + subPath: "" + + ## Persistent Volume Claim Selector + ## Useful if Persistent Volumes have been provisioned in advance + ## Ref: https://kubernetes.io/docs/concepts/storage/persistent-volumes/#selector + ## + # selector: + # matchLabels: + # release: "stable" + # matchExpressions: + # - { key: environment, operator: In, values: [ dev ] } + + ## Persistent Volume Name + ## Useful if Persistent Volumes have been provisioned in advance and you want to use a specific one + ## + # volumeName: "" + + emptyDir: + ## Prometheus server emptyDir volume size limit + ## + sizeLimit: "" + + ## Annotations to be added to Prometheus server pods + ## + podAnnotations: {} + # iam.amazonaws.com/role: prometheus + + ## Labels to be added to Prometheus server pods + ## + podLabels: {} + + ## Prometheus AlertManager configuration + ## + alertmanagers: [] + + ## Specify if a Pod Security Policy for node-exporter must be created + ## Ref: https://kubernetes.io/docs/concepts/policy/pod-security-policy/ + ## + podSecurityPolicy: + annotations: {} + ## Specify pod annotations + ## Ref: https://kubernetes.io/docs/concepts/policy/pod-security-policy/#apparmor + ## Ref: https://kubernetes.io/docs/concepts/policy/pod-security-policy/#seccomp + ## Ref: https://kubernetes.io/docs/concepts/policy/pod-security-policy/#sysctl + ## + # seccomp.security.alpha.kubernetes.io/allowedProfileNames: '*' + # seccomp.security.alpha.kubernetes.io/defaultProfileName: 'docker/default' + # apparmor.security.beta.kubernetes.io/defaultProfileName: 'runtime/default' + + ## Use a StatefulSet if replicaCount needs to be greater than 1 (see below) + ## + replicaCount: 1 + + ## Number of old history to retain to allow rollback + ## Default Kubernetes value is set to 10 + ## + revisionHistoryLimit: 10 + + ## Annotations to be added to ConfigMap + ## + configMapAnnotations: {} + + ## Annotations to be added to deployment + ## + deploymentAnnotations: {} + + statefulSet: + ## If true, use a statefulset instead of a deployment for pod management. + ## This allows to scale replicas to more than 1 pod + ## + enabled: false + + annotations: {} + labels: {} + podManagementPolicy: OrderedReady + + ## Alertmanager headless service to use for the statefulset + ## + headless: + annotations: {} + labels: {} + servicePort: 80 + ## Enable gRPC port on service to allow auto discovery with thanos-querier + gRPC: + enabled: false + servicePort: 10901 + # nodePort: 10901 + + ## Statefulset's persistent volume claim retention policy + ## pvcDeleteOnStsDelete and pvcDeleteOnStsScale determine whether + ## statefulset's PVCs are deleted (true) or retained (false) on scaling down + ## and deleting statefulset, respectively. Requires 1.27.0+. + ## Ref: https://kubernetes.io/docs/concepts/workloads/controllers/statefulset/#persistentvolumeclaim-retention + ## + pvcDeleteOnStsDelete: false + pvcDeleteOnStsScale: false + + ## Prometheus server readiness and liveness probe initial delay and timeout + ## Ref: https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/ + ## + tcpSocketProbeEnabled: false + probeScheme: HTTP + readinessProbeInitialDelay: 30 + readinessProbePeriodSeconds: 5 + readinessProbeTimeout: 4 + readinessProbeFailureThreshold: 3 + readinessProbeSuccessThreshold: 1 + livenessProbeInitialDelay: 30 + livenessProbePeriodSeconds: 15 + livenessProbeTimeout: 10 + livenessProbeFailureThreshold: 3 + livenessProbeSuccessThreshold: 1 + startupProbe: + enabled: false + periodSeconds: 5 + failureThreshold: 30 + timeoutSeconds: 10 + + ## Prometheus server resource requests and limits + ## Ref: http://kubernetes.io/docs/user-guide/compute-resources/ + ## + resources: + requests: + cpu: 20m + memory: 200Mi + # limits: + # cpu: 500m + # memory: 512Mi + # requests: + # cpu: 500m + # memory: 512Mi + + # Required for use in managed kubernetes clusters (such as AWS EKS) with custom CNI (such as calico), + # because control-plane managed by AWS cannot communicate with pods' IP CIDR and admission webhooks are not working + ## + hostNetwork: false + + # When hostNetwork is enabled, this will set to ClusterFirstWithHostNet automatically + dnsPolicy: ClusterFirst + + # Use hostPort + # hostPort: 9090 + + # Use portName + portName: "" + + ## Vertical Pod Autoscaler config + ## Ref: https://github.com/kubernetes/autoscaler/tree/master/vertical-pod-autoscaler + verticalAutoscaler: + ## If true a VPA object will be created for the controller (either StatefulSet or Deployemnt, based on above configs) + enabled: false + # updateMode: "Auto" + # containerPolicies: + # - containerName: 'prometheus-server' + + # Custom DNS configuration to be added to prometheus server pods + dnsConfig: {} + # nameservers: + # - 1.2.3.4 + # searches: + # - ns1.svc.cluster-domain.example + # - my.dns.search.suffix + # options: + # - name: ndots + # value: "2" + # - name: edns0 + + ## Security context to be added to server pods + ## + securityContext: + runAsUser: 65534 + runAsNonRoot: true + runAsGroup: 65534 + fsGroup: 65534 + + ## Security context to be added to server container + ## + containerSecurityContext: {} + + service: + ## If false, no Service will be created for the Prometheus server + ## + enabled: true + + annotations: {} + labels: {} + clusterIP: "" + + ## List of IP addresses at which the Prometheus server service is available + ## Ref: https://kubernetes.io/docs/concepts/services-networking/service/#external-ips + ## + externalIPs: [] + + loadBalancerIP: "" + loadBalancerSourceRanges: [] + servicePort: 80 + sessionAffinity: None + type: ClusterIP + + ## Enable gRPC port on service to allow auto discovery with thanos-querier + gRPC: + enabled: false + servicePort: 10901 + # nodePort: 10901 + + ## If using a statefulSet (statefulSet.enabled=true), configure the + ## service to connect to a specific replica to have a consistent view + ## of the data. + statefulsetReplica: + enabled: false + replica: 0 + + ## Additional port to define in the Service + additionalPorts: [] + # additionalPorts: + # - name: authenticated + # port: 8081 + # targetPort: 8081 + + ## Prometheus server pod termination grace period + ## + terminationGracePeriodSeconds: 300 + + ## Prometheus data retention period (default if not specified is 15 days) + ## + retention: "15d" + + ## Prometheus' data retention size. Supported units: B, KB, MB, GB, TB, PB, EB. + ## + retentionSize: "" + +## Prometheus server ConfigMap entries for rule files (allow prometheus labels interpolation) +ruleFiles: {} + +## Prometheus server ConfigMap entries for scrape_config_files +## (allows scrape configs defined in additional files) +## +scrapeConfigFiles: [] + +## Prometheus server ConfigMap entries +## +serverFiles: + ## Alerts configuration + ## Ref: https://prometheus.io/docs/prometheus/latest/configuration/alerting_rules/ + alerting_rules.yml: {} + # groups: + # - name: Instances + # rules: + # - alert: InstanceDown + # expr: up == 0 + # for: 5m + # labels: + # severity: page + # annotations: + # description: '{{ $labels.instance }} of job {{ $labels.job }} has been down for more than 5 minutes.' + # summary: 'Instance {{ $labels.instance }} down' + ## DEPRECATED DEFAULT VALUE, unless explicitly naming your files, please use alerting_rules.yml + alerts: {} + + ## Records configuration + ## Ref: https://prometheus.io/docs/prometheus/latest/configuration/recording_rules/ + recording_rules.yml: {} + ## DEPRECATED DEFAULT VALUE, unless explicitly naming your files, please use recording_rules.yml + rules: {} + + prometheus.yml: + rule_files: + - /etc/config/recording_rules.yml + - /etc/config/alerting_rules.yml + ## Below two files are DEPRECATED will be removed from this default values file + - /etc/config/rules + - /etc/config/alerts + + scrape_configs: + - job_name: prometheus + static_configs: + - targets: + - localhost:9090 + + # A scrape configuration for running Prometheus on a Kubernetes cluster. + # This uses separate scrape configs for cluster components (i.e. API server, node) + # and services to allow each to use different authentication configs. + # + # Kubernetes labels will be added as Prometheus labels on metrics via the + # `labelmap` relabeling action. + + # Scrape config for API servers. + # + # Kubernetes exposes API servers as endpoints to the default/kubernetes + # service so this uses `endpoints` role and uses relabelling to only keep + # the endpoints associated with the default/kubernetes service using the + # default named port `https`. This works for single API server deployments as + # well as HA API server deployments. + - job_name: 'kubernetes-apiservers' + + kubernetes_sd_configs: + - role: endpoints + + # Default to scraping over https. If required, just disable this or change to + # `http`. + scheme: https + + # This TLS & bearer token file config is used to connect to the actual scrape + # endpoints for cluster components. This is separate to discovery auth + # configuration because discovery & scraping are two separate concerns in + # Prometheus. The discovery auth config is automatic if Prometheus runs inside + # the cluster. Otherwise, more config options have to be provided within the + # . + tls_config: + ca_file: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt + # If your node certificates are self-signed or use a different CA to the + # master CA, then disable certificate verification below. Note that + # certificate verification is an integral part of a secure infrastructure + # so this should only be disabled in a controlled environment. You can + # disable certificate verification by uncommenting the line below. + # + insecure_skip_verify: true + bearer_token_file: /var/run/secrets/kubernetes.io/serviceaccount/token + + # Keep only the default/kubernetes service endpoints for the https port. This + # will add targets for each API server which Kubernetes adds an endpoint to + # the default/kubernetes service. + relabel_configs: + - source_labels: [__meta_kubernetes_namespace, __meta_kubernetes_service_name, __meta_kubernetes_endpoint_port_name] + action: keep + regex: default;kubernetes;https + + - job_name: 'kubernetes-nodes' + + # Default to scraping over https. If required, just disable this or change to + # `http`. + scheme: https + + # This TLS & bearer token file config is used to connect to the actual scrape + # endpoints for cluster components. This is separate to discovery auth + # configuration because discovery & scraping are two separate concerns in + # Prometheus. The discovery auth config is automatic if Prometheus runs inside + # the cluster. Otherwise, more config options have to be provided within the + # . + tls_config: + ca_file: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt + # If your node certificates are self-signed or use a different CA to the + # master CA, then disable certificate verification below. Note that + # certificate verification is an integral part of a secure infrastructure + # so this should only be disabled in a controlled environment. You can + # disable certificate verification by uncommenting the line below. + # + insecure_skip_verify: true + bearer_token_file: /var/run/secrets/kubernetes.io/serviceaccount/token + + kubernetes_sd_configs: + - role: node + + relabel_configs: + - action: labelmap + regex: __meta_kubernetes_node_label_(.+) + - target_label: __address__ + replacement: kubernetes.default.svc:443 + - source_labels: [__meta_kubernetes_node_name] + regex: (.+) + target_label: __metrics_path__ + replacement: /api/v1/nodes/$1/proxy/metrics + + + - job_name: 'kubernetes-nodes-cadvisor' + + # Default to scraping over https. If required, just disable this or change to + # `http`. + scheme: https + + # This TLS & bearer token file config is used to connect to the actual scrape + # endpoints for cluster components. This is separate to discovery auth + # configuration because discovery & scraping are two separate concerns in + # Prometheus. The discovery auth config is automatic if Prometheus runs inside + # the cluster. Otherwise, more config options have to be provided within the + # . + tls_config: + ca_file: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt + # If your node certificates are self-signed or use a different CA to the + # master CA, then disable certificate verification below. Note that + # certificate verification is an integral part of a secure infrastructure + # so this should only be disabled in a controlled environment. You can + # disable certificate verification by uncommenting the line below. + # + insecure_skip_verify: true + bearer_token_file: /var/run/secrets/kubernetes.io/serviceaccount/token + + kubernetes_sd_configs: + - role: node + + # This configuration will work only on kubelet 1.7.3+ + # As the scrape endpoints for cAdvisor have changed + # if you are using older version you need to change the replacement to + # replacement: /api/v1/nodes/$1:4194/proxy/metrics + # more info here https://github.com/coreos/prometheus-operator/issues/633 + relabel_configs: + - action: labelmap + regex: __meta_kubernetes_node_label_(.+) + - target_label: __address__ + replacement: kubernetes.default.svc:443 + - source_labels: [__meta_kubernetes_node_name] + regex: (.+) + target_label: __metrics_path__ + replacement: /api/v1/nodes/$1/proxy/metrics/cadvisor + + # Metric relabel configs to apply to samples before ingestion. + # [Metric Relabeling](https://prometheus.io/docs/prometheus/latest/configuration/configuration/#metric_relabel_configs) + # metric_relabel_configs: + # - action: labeldrop + # regex: (kubernetes_io_hostname|failure_domain_beta_kubernetes_io_region|beta_kubernetes_io_os|beta_kubernetes_io_arch|beta_kubernetes_io_instance_type|failure_domain_beta_kubernetes_io_zone) + + # Scrape config for service endpoints. + # + # The relabeling allows the actual service scrape endpoint to be configured + # via the following annotations: + # + # * `prometheus.io/scrape`: Only scrape services that have a value of + # `true`, except if `prometheus.io/scrape-slow` is set to `true` as well. + # * `prometheus.io/scheme`: If the metrics endpoint is secured then you will need + # to set this to `https` & most likely set the `tls_config` of the scrape config. + # * `prometheus.io/path`: If the metrics path is not `/metrics` override this. + # * `prometheus.io/port`: If the metrics are exposed on a different port to the + # service then set this appropriately. + # * `prometheus.io/param_`: If the metrics endpoint uses parameters + # then you can set any parameter + - job_name: 'kubernetes-service-endpoints' + honor_labels: true + + kubernetes_sd_configs: + - role: endpoints + + relabel_configs: + - source_labels: [__meta_kubernetes_service_annotation_prometheus_io_scrape] + action: keep + regex: true + - source_labels: [__meta_kubernetes_service_annotation_prometheus_io_scrape_slow] + action: drop + regex: true + - source_labels: [__meta_kubernetes_service_annotation_prometheus_io_scheme] + action: replace + target_label: __scheme__ + regex: (https?) + - source_labels: [__meta_kubernetes_service_annotation_prometheus_io_path] + action: replace + target_label: __metrics_path__ + regex: (.+) + - source_labels: [__address__, __meta_kubernetes_service_annotation_prometheus_io_port] + action: replace + target_label: __address__ + regex: (.+?)(?::\d+)?;(\d+) + replacement: $1:$2 + - action: labelmap + regex: __meta_kubernetes_service_annotation_prometheus_io_param_(.+) + replacement: __param_$1 + - action: labelmap + regex: __meta_kubernetes_service_label_(.+) + - source_labels: [__meta_kubernetes_namespace] + action: replace + target_label: namespace + - source_labels: [__meta_kubernetes_service_name] + action: replace + target_label: service + - source_labels: [__meta_kubernetes_pod_node_name] + action: replace + target_label: node + + # Scrape config for slow service endpoints; same as above, but with a larger + # timeout and a larger interval + # + # The relabeling allows the actual service scrape endpoint to be configured + # via the following annotations: + # + # * `prometheus.io/scrape-slow`: Only scrape services that have a value of `true` + # * `prometheus.io/scheme`: If the metrics endpoint is secured then you will need + # to set this to `https` & most likely set the `tls_config` of the scrape config. + # * `prometheus.io/path`: If the metrics path is not `/metrics` override this. + # * `prometheus.io/port`: If the metrics are exposed on a different port to the + # service then set this appropriately. + # * `prometheus.io/param_`: If the metrics endpoint uses parameters + # then you can set any parameter + - job_name: 'kubernetes-service-endpoints-slow' + honor_labels: true + + scrape_interval: 5m + scrape_timeout: 30s + + kubernetes_sd_configs: + - role: endpoints + + relabel_configs: + - source_labels: [__meta_kubernetes_service_annotation_prometheus_io_scrape_slow] + action: keep + regex: true + - source_labels: [__meta_kubernetes_service_annotation_prometheus_io_scheme] + action: replace + target_label: __scheme__ + regex: (https?) + - source_labels: [__meta_kubernetes_service_annotation_prometheus_io_path] + action: replace + target_label: __metrics_path__ + regex: (.+) + - source_labels: [__address__, __meta_kubernetes_service_annotation_prometheus_io_port] + action: replace + target_label: __address__ + regex: (.+?)(?::\d+)?;(\d+) + replacement: $1:$2 + - action: labelmap + regex: __meta_kubernetes_service_annotation_prometheus_io_param_(.+) + replacement: __param_$1 + - action: labelmap + regex: __meta_kubernetes_service_label_(.+) + - source_labels: [__meta_kubernetes_namespace] + action: replace + target_label: namespace + - source_labels: [__meta_kubernetes_service_name] + action: replace + target_label: service + - source_labels: [__meta_kubernetes_pod_node_name] + action: replace + target_label: node + + - job_name: 'prometheus-pushgateway' + honor_labels: true + + kubernetes_sd_configs: + - role: service + + relabel_configs: + - source_labels: [__meta_kubernetes_service_annotation_prometheus_io_probe] + action: keep + regex: pushgateway + + # Example scrape config for probing services via the Blackbox Exporter. + # + # The relabeling allows the actual service scrape endpoint to be configured + # via the following annotations: + # + # * `prometheus.io/probe`: Only probe services that have a value of `true` + - job_name: 'kubernetes-services' + honor_labels: true + + metrics_path: /probe + params: + module: [http_2xx] + + kubernetes_sd_configs: + - role: service + + relabel_configs: + - source_labels: [__meta_kubernetes_service_annotation_prometheus_io_probe] + action: keep + regex: true + - source_labels: [__address__] + target_label: __param_target + - target_label: __address__ + replacement: blackbox + - source_labels: [__param_target] + target_label: instance + - action: labelmap + regex: __meta_kubernetes_service_label_(.+) + - source_labels: [__meta_kubernetes_namespace] + target_label: namespace + - source_labels: [__meta_kubernetes_service_name] + target_label: service + + # Example scrape config for pods + # + # The relabeling allows the actual pod scrape endpoint to be configured via the + # following annotations: + # + # * `prometheus.io/scrape`: Only scrape pods that have a value of `true`, + # except if `prometheus.io/scrape-slow` is set to `true` as well. + # * `prometheus.io/scheme`: If the metrics endpoint is secured then you will need + # to set this to `https` & most likely set the `tls_config` of the scrape config. + # * `prometheus.io/path`: If the metrics path is not `/metrics` override this. + # * `prometheus.io/port`: Scrape the pod on the indicated port instead of the default of `9102`. + - job_name: 'kubernetes-pods' + honor_labels: true + + kubernetes_sd_configs: + - role: pod + + relabel_configs: + - source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_scrape] + action: keep + regex: true + - source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_scrape_slow] + action: drop + regex: true + - source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_scheme] + action: replace + regex: (https?) + target_label: __scheme__ + - source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_path] + action: replace + target_label: __metrics_path__ + regex: (.+) + - source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_port, __meta_kubernetes_pod_ip] + action: replace + regex: (\d+);(([A-Fa-f0-9]{1,4}::?){1,7}[A-Fa-f0-9]{1,4}) + replacement: '[$2]:$1' + target_label: __address__ + - source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_port, __meta_kubernetes_pod_ip] + action: replace + regex: (\d+);((([0-9]+?)(\.|$)){4}) + replacement: $2:$1 + target_label: __address__ + - action: labelmap + regex: __meta_kubernetes_pod_annotation_prometheus_io_param_(.+) + replacement: __param_$1 + - action: labelmap + regex: __meta_kubernetes_pod_label_(.+) + - source_labels: [__meta_kubernetes_namespace] + action: replace + target_label: namespace + - source_labels: [__meta_kubernetes_pod_name] + action: replace + target_label: pod + - source_labels: [__meta_kubernetes_pod_phase] + regex: Pending|Succeeded|Failed|Completed + action: drop + - source_labels: [__meta_kubernetes_pod_node_name] + action: replace + target_label: node + + # Example Scrape config for pods which should be scraped slower. An useful example + # would be stackriver-exporter which queries an API on every scrape of the pod + # + # The relabeling allows the actual pod scrape endpoint to be configured via the + # following annotations: + # + # * `prometheus.io/scrape-slow`: Only scrape pods that have a value of `true` + # * `prometheus.io/scheme`: If the metrics endpoint is secured then you will need + # to set this to `https` & most likely set the `tls_config` of the scrape config. + # * `prometheus.io/path`: If the metrics path is not `/metrics` override this. + # * `prometheus.io/port`: Scrape the pod on the indicated port instead of the default of `9102`. + - job_name: 'kubernetes-pods-slow' + honor_labels: true + + scrape_interval: 5m + scrape_timeout: 30s + + kubernetes_sd_configs: + - role: pod + + relabel_configs: + - source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_scrape_slow] + action: keep + regex: true + - source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_scheme] + action: replace + regex: (https?) + target_label: __scheme__ + - source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_path] + action: replace + target_label: __metrics_path__ + regex: (.+) + - source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_port, __meta_kubernetes_pod_ip] + action: replace + regex: (\d+);(([A-Fa-f0-9]{1,4}::?){1,7}[A-Fa-f0-9]{1,4}) + replacement: '[$2]:$1' + target_label: __address__ + - source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_port, __meta_kubernetes_pod_ip] + action: replace + regex: (\d+);((([0-9]+?)(\.|$)){4}) + replacement: $2:$1 + target_label: __address__ + - action: labelmap + regex: __meta_kubernetes_pod_annotation_prometheus_io_param_(.+) + replacement: __param_$1 + - action: labelmap + regex: __meta_kubernetes_pod_label_(.+) + - source_labels: [__meta_kubernetes_namespace] + action: replace + target_label: namespace + - source_labels: [__meta_kubernetes_pod_name] + action: replace + target_label: pod + - source_labels: [__meta_kubernetes_pod_phase] + regex: Pending|Succeeded|Failed|Completed + action: drop + - source_labels: [__meta_kubernetes_pod_node_name] + action: replace + target_label: node + +# adds additional scrape configs to prometheus.yml +# must be a string so you have to add a | after extraScrapeConfigs: +# example adds prometheus-blackbox-exporter scrape config +extraScrapeConfigs: "" + # - job_name: 'prometheus-blackbox-exporter' + # metrics_path: /probe + # params: + # module: [http_2xx] + # static_configs: + # - targets: + # - https://example.com + # relabel_configs: + # - source_labels: [__address__] + # target_label: __param_target + # - source_labels: [__param_target] + # target_label: instance + # - target_label: __address__ + # replacement: prometheus-blackbox-exporter:9115 + +# Adds option to add alert_relabel_configs to avoid duplicate alerts in alertmanager +# useful in H/A prometheus with different external labels but the same alerts +alertRelabelConfigs: {} + # alert_relabel_configs: + # - source_labels: [dc] + # regex: (.+)\d+ + # target_label: dc + +networkPolicy: + ## Enable creation of NetworkPolicy resources. + ## + ## Customized for K10 + enabled: true + +# Force namespace of namespaced resources +forceNamespace: "" + +# Extra manifests to deploy as an array +extraManifests: [] + # - | + # apiVersion: v1 + # kind: ConfigMap + # metadata: + # labels: + # name: prometheus-extra + # data: + # extra-data: "value" + +# Configuration of subcharts defined in Chart.yaml + +## alertmanager sub-chart configurable values +## Please see https://github.com/prometheus-community/helm-charts/tree/main/charts/alertmanager +## +alertmanager: + ## If false, alertmanager will not be installed + ## + ## Customized for K10 + enabled: false + + persistence: + size: 2Gi + + podSecurityContext: + runAsUser: 65534 + runAsNonRoot: true + runAsGroup: 65534 + fsGroup: 65534 + +## kube-state-metrics sub-chart configurable values +## Please see https://github.com/prometheus-community/helm-charts/tree/main/charts/kube-state-metrics +## +kube-state-metrics: + ## If false, kube-state-metrics sub-chart will not be installed + ## + ## Customized for K10 + enabled: false + +## prometheus-node-exporter sub-chart configurable values +## Please see https://github.com/prometheus-community/helm-charts/tree/main/charts/prometheus-node-exporter +## +prometheus-node-exporter: + ## If false, node-exporter will not be installed + ## + ## Customized for K10 + enabled: false + + rbac: + pspEnabled: false + + containerSecurityContext: + allowPrivilegeEscalation: false + +## prometheus-pushgateway sub-chart configurable values +## Please see https://github.com/prometheus-community/helm-charts/tree/main/charts/prometheus-pushgateway +## +prometheus-pushgateway: + ## If false, pushgateway will not be installed + ## + ## Customized for K10 + enabled: false + + # Optional service annotations + serviceAnnotations: + prometheus.io/probe: pushgateway diff --git a/charts/kasten/k10/7.5.401/config.json b/charts/kasten/k10/7.5.401/config.json new file mode 100644 index 0000000000..e69de29bb2 diff --git a/charts/kasten/k10/7.5.401/eula.txt b/charts/kasten/k10/7.5.401/eula.txt new file mode 100644 index 0000000000..19f9fc0763 --- /dev/null +++ b/charts/kasten/k10/7.5.401/eula.txt @@ -0,0 +1,459 @@ +KASTEN END USER LICENSE AGREEMENT + +This End User License Agreement is a binding agreement between Kasten, Inc., a +Delaware Corporation ("Kasten"), and you ("Licensee"), and establishes the terms +under which Licensee may use the Software and Documentation (as defined below), +including without limitation terms and conditions relating to license grant, +intellectual property rights, disclaimers /exclusions / limitations of warranty, +indemnity and liability, governing law and limitation periods. All components +collectively are referred to herein as the "Agreement." + +LICENSEE ACKNOWLEDGES IT HAS HAD THE OPPORTUNITY TO REVIEW THE AGREEMENT, PRIOR +TO ACCEPTANCE OF THIS AGREEMENT. LICENSEE'S ACCEPTANCE OF THIS AGREEMENT IS +EVIDENCED BY LICENSEE'S DOWNLOADING, COPYING, INSTALLING OR USING THE KASTEN +SOFTWARE. IF YOU ARE ACTING ON BEHALF OF A COMPANY, YOU REPRESENT THAT YOU ARE +AUTHORIZED TO BIND THE COMPANY. IF YOU DO NOT AGREE TO ALL TERMS OF THIS +AGREEMENT, DO NOT DOWNLOAD, COPY, INSTALL, OR USE THE SOFTWARE, AND PERMANENTLY +DELETE THE SOFTWARE. + +1. DEFINITIONS + +1.1 "Authorized Persons" means trained technical employees and contractors of +Licensee who are subject to a written agreement with Licensee that includes use +and confidentiality restrictions that are at least as protective as those set +forth in this Agreement. + +1.2 "Authorized Reseller" means a distributor or reseller, including cloud +computing platform providers, authorized by Kasten to resell licenses to the +Software through the channel through or in the territory in which Licensee is +purchasing. + +1.3 "Confidential Information" means all non-public information disclosed in +written, oral or visual form by either party to the other. Confidential +Information may include, but is not limited to, services, pricing information, +computer programs, source code, names and expertise of employees and +consultants, know-how, and other technical, business, financial and product +development information. "Confidential Information" does not include any +information that the receiving party can demonstrate by its written records (1) +was rightfully known to it without obligation of confidentiality prior to its +disclosure hereunder by the disclosing party; (2) is or becomes publicly known +through no wrongful act of the receiving party; (3) has been rightfully received +without obligation of confidentiality from a third party authorized to make such +a disclosure; or (4) is independently developed by the receiving party without +reference to confidential information disclosed hereunder. + +1.4 "Documentation" means any administration guides, installation and user +guides, and release notes that are provided by Kasten to Licensee with the +Software. + +1.5 "Intellectual Property Rights" means patents, design patents, copyrights, +trademarks, Confidential Information, know-how, trade secrets, moral rights, and +any other intellectual property rights recognized in any country or jurisdiction +in the world. + +1.6 "Node" means a single physical or virtual computing machine recognizable by +the Software as a unique device. Nodes must be owned or leased by Licensee or an +entity controlled by, controlling or under common control with Licensee. + +1.7 "Edition" means a unique identifier for each distinct product that is made +available by Kasten and that can be licensed, including summary information +regarding any associated functionality, features, or restrictions specific to +the Edition. + +1.8 "Open Source Software" means software delivered to Licensee hereunder that +is subject to the provisions of any open source license agreement. + +1.9 "Purchase Agreement" means a separate commercial agreement, if applicable, +between Kasten and the Licensee that contains the terms for the licensing of a +specific Edition of the Software. + +1.10 "Software" means any and all software product Editions licensed to Licensee +under this Agreement, all as developed by Kasten and delivered to Licensee +hereunder. Software also includes any Updates provided by Kasten to Licensee. +For the avoidance of doubt, the definition of Software shall exclude any +Third-Party Software and Open Source Software. + +1.11 "Third-Party Software" means certain software Kasten licenses from third +parties and provides to Licensee with the Software, which may include Open +Source Software. + +1.12 "Update" means a revision of the Software that Kasten makes available to +customers at no additional cost. The Update includes, if and when applicable and +available, bug fix patches, maintenance release, minor release, or new major +releases. Updates are limited only to the Software licensed by Licensee, and +specifically exclude new product offerings, features, options or functionality +of the Software that Kasten may choose to license separately, or for an +additional fee. + +1.13 "Use" means to install activate the processing capabilities of the +Software, load, execute, access, employ the Software, or display information +resulting from such capabilities. + + +2. LICENSE GRANT AND RESTRICTIONS + +2.1 Enterprise License. Subject to Licensee"s compliance with the terms and +conditions of this Agreement (including any additional restrictions on +Licensee"s use of the Software set forth in the Purchase Agreement, if one +exists, between Licensee and Kasten), Kasten grants to Licensee a non-exclusive, +non-transferable (except in connection with a permitted assignment of this +Agreement under Section 14.10 (Assignment), non-sublicensable, limited term +license to install and use the Software, in object code form only, solely for +Licensee"s use, unless terminated in accordance with Section 4 (Term and +Termination). + +2.2 Starter License. This section shall only apply when the Licensee licenses +Starter Edition of the Software. The license granted herein is for a maximum of +5 Nodes and for a period of 12 months from the date of the Software release that +embeds the specific license instance. Updating to a newer Software (minor or +major) release will always extend the validity of the license by 12 months. If +the Licensee wishes to upgrade to an Enterprise License instead, the Licensee +will have to enter into a Purchase Agreement with Kasten which will supersede +this Agreement. The Licensee is required to provide accurate email and company +information, if representing a company, when accepting this Agreement. Under no +circumstances will a Starter License be construed to mean that the Licensee is +authorized to distribute the Software to any third party for any reason +whatsoever. + +2.3 Evaluation License. This section shall only apply when the Licensee has +licensed the Software for an initial evaluation period. The license granted +herein is valid only one time 30 days, starting from date of installation, +unless otherwise explicitly designated by Kasten ("Evaluation Period"). Under +this license the Software can only be used for evaluation purposes. Under no +circumstances will an Evaluation License be construed to mean that the Licensee +is authorized to distribute the Software to any third party for any reason +whatsoever. If the Licensee wishes to upgrade to an Enterprise License instead, +the Licensee will have to enter into a Purchase Agreement with Kasten which will +supersede this Agreement.. If the Licensee does not wish to upgrade to an +Enterprise License at the end of the Evaluation Period the Licensee"s rights +under the Agreement shall terminate, and the Licensee shall delete all Kasten +Software. + +2.4 License Restrictions. Except to the extent permitted under this Agreement, +Licensee will not nor will Licensee allow any third party to: (i) copy, modify, +adapt, translate or otherwise create derivative works of the Software or the +Documentation; (ii) reverse engineer, decompile, disassemble or otherwise +attempt to discover the source code of the Software; (iii) rent, lease, sell, +assign or otherwise transfer rights in or to the Software or Documentation; (iv) +remove any proprietary notices or labels from the Software or Documentation; (v) +publicly disseminate performance information or analysis (including, without +limitation, benchmarks) relating to the Software. Licensee will comply with all +applicable laws and regulations in Licensee"s use of and access to the Software +and Documentation. + +2.5 Responsibility for Use. The Software and Documentation may be used only by +Authorized Persons and in conformance with this Agreement. Licensee shall be +responsible for the proper use and protection of the Software and Documentation +and is responsible for: (i) installing, managing, operating, and physically +controlling the Software and the results obtained from using the Software; (ii) +using the Software within the operating environment specified in the +Documentation; and; (iii) establishing and maintaining such recovery and data +protection and security procedures as necessary for Licensee's service and +operation and/or as may be specified by Kasten from time to time. + +2.6 United States Government Users. The Software licensed under this Agreement +is "commercial computer software" as that term is described in DFAR +252.227-7014(a)(1). If acquired by or on behalf of a civilian agency, the U.S. +Government acquires this commercial computer software and/or commercial computer +software documentation subject to the terms and this Agreement as specified in +48 C.F.R. 12.212 (Computer Software) and 12.211 (Technical Data) of the Federal +Acquisition Regulations ("FAR") and its successors. If acquired by or on behalf +of any agency within the Department of Defense ("DOD"), the U.S. Government +acquires this commercial computer software and/or commercial computer software +documentation subject to the terms of this Agreement as specified in 48 C.F.R. +227.7202 of the DOD FAR Supplement and its successors. + + +3. SUPPORT + +3.1 During the Term (as defined below) and subject to Licensee’s compliance +with the terms and conditions of this Agreement, Licensee may submit queries and +requests for support for Enterprise Licenses by submitting Service Requests via Veeam +Support Portal (https://my.veeam.com). Support is not provided for Starter and Evaluation +Licenses. Licensee shall be entitled to the support service-level agreement specified +in the relevant order form or purchase order (“Order Form”) between Licensee and the +Reseller and as set forth in Kasten’s Support Policy, a copy of which can be found +at https://www.kasten.io/support-services-policy. Licensee shall also be permitted to +download and install all Updates released by Kasten during the Term and made generally +available to users of the Software. Software versions with all updates and upgrades +installed is supported for six months from the date of release of that version. + +3.2 Starter Edition Support. If the Licensee has licensed Starter Edition of +the Software, you will have access to the Kasten K10 Support Community +(https://community.veeam.com/groups/kasten-k10-support-92), but Kasten cannot guarantee +a service level of any sort. Should a higher level of support be needed, Licensee has +the option to consider entering into a Purchase Agreement with Kasten for licensing a +different Edition of the Software. + + + +4. TERM AND TERMINATION + +4.1 Term. The term of this Agreement, except for Starter and Evaluation +Licenses, shall commence on the Effective Date and shall, unless terminated +earlier in accordance with the provisions of Section 4.2 below, remain in force +for the Subscription Period as set forth in the applicable Order Form(s) (the +"Term"). The parties may extend the Term of this Agreement beyond the +Subscription Period by executing additional Order Form(s) and Licensee"s payment +of additional licensing fees. The term of this Agreement for the Starter and +Evaluation Licenses will coincide with the term for Starter Edition (as stated +in section 2.2) and the term for Evaluation Period (as stated in section 2.3), +respectively + +4.2 Termination. Either party may immediately terminate this +Agreement and the licenses granted hereunder if the other party (1) becomes +insolvent and"becomes unwilling or unable to meet its obligations under this +Agreement, (2) files a petition in bankruptcy, (3) is subject to the filing of +an involuntary petition for bankruptcy which is not rescinded within a period of +forty-five (45) days, (4) fails to cure a material breach of any material term +or condition of this Agreement within thirty (30) days of receipt of written +notice specifying such breach, or (5) materially breaches its obligations of +confidentiality hereunder. + +4.3 Effects of Termination. Upon expiration or +termination of this Agreement for any reason, (i) any amounts owed to Kasten +under this Agreement will be immediately due and payable; (ii) all licensed +rights granted in this Agreement will immediately cease; and (iii) Licensee will +promptly discontinue all use of the Software and Documentation and return to +Kasten any Kasten Confidential Information in Licensee"s possession or control. + +4.4 Survival. The following Sections of this Agreement will remain in effect +following the expiration or termination of these General Terms for any reason: +4.3 (Effects of Termination), 4.4 (Survival), 5 (Third Party Software) 5 +(Confidentiality), 9 (Ownership), 10.2 (Third-Party Software), 10.3 (Warranty +Disclaimer), 11 (Limitations of Liability), 12.2 (Exceptions to Kasten +Obligation), 13 (Export) and 14 (General). + + +5. THIRD PARTY AND OPEN SOURCE SOFTWARE Certain Third-Party Software or Open +Source Software (Kasten can provide a list upon request) that may be provided +with the Software may be subject to various other terms and conditions imposed +by the licensors of such Third-Party Software or Open Source Software. The +terms of Licensee"s use of the Third-Party Software or Open Source Software is +subject to and governed by the respective Third-Party Software and Open Source +licenses, except that this Section 5 (Third-Party Software), Section 10.2 (Third +Party Software), 10.3 (Warranty Disclaimer), Section 11 (Limitations of +Liability), and Section 14 (General) of this Agreement also govern Licensee"s +use of the Third-Party Software. To the extent applicable to Licensee"s use of +such Third-Party Software and Open Source, Licensee agrees to comply with the +terms and conditions contained in all such Third-Party Software and Open Source +licenses. + + +6. CONFIDENTIALITY Neither party will use any Confidential Information of the +other party except as expressly permitted by this Agreement or as expressly +authorized in writing by the disclosing party. The receiving party shall use +the same degree of care to protect the disclosing party"s Confidential +Information as it uses to protect its own Confidential Information of like +nature, but in no circumstances less than a commercially reasonable standard of +care. The receiving party may not disclose the disclosing party"s Confidential +Information to any person or entity other than to (i) (a) Authorized Persons in +the case the receiving party is Licensee, and (b) Kasten"s employees and +contractors in the case the receiving party is Kasten, and (ii) who need access +to such Confidential Information solely for the purpose of fulfilling that +party"s obligations or exercising that party"s rights hereunder. The foregoing +obligations will not restrict the receiving party from disclosing Confidential +Information of the disclosing party: (1) pursuant to the order or requirement of +a court, administrative agency, or other governmental body, provided that the +receiving party required to make such a disclosure gives reasonable notice to +the disclosing party prior to such disclosure; and (2) on a confidential basis +to its legal and financial advisors. Kasten may identify Licensee in its +customer lists in online and print marketing materials. + + +7. FEES Fees for Enterprise License shall be set forth in separate Order Form(s) +attached to a Purchase Agreement, between the Licensee and Kasten. + +If Licensee has obtained the Software through an Authorized Reseller, fees for +licensing shall be invoiced directly by the Authorized Reseller. + +If no Purchase Agreement exists, during the term of this Agreement, Kasten +shall license the Starter Edition only and no other Edition of the Software +"at no charge" to Licensee. + + +8. USAGE DATA Kasten may collect, accumulate, and aggregate certain usage +statistics in order to analyze usage of the Software, make improvements, and +potentially develop new products. Kasten may use aggregated anonymized data for +any purpose that Kasten, at its own discretion, may consider appropriate. + + +9. OWNERSHIP As between Kasten and Licensee, all right, title and interest in +the Software, Documentation and any other Kasten materials furnished or made +available hereunder, all modifications and enhancements thereof, and all +suggestions, ideas and feedback proposed by Licensee regarding the Software and +Documentation, including all copyright rights, patent rights and other +Intellectual Property Rights in each of the foregoing, belong to and are +retained solely by Kasten or Kasten"s licensors and providers, as applicable. +Licensee hereby does and will irrevocably assign to Kasten all evaluations, +ideas, feedback and suggestions made by Licensee to Kasten regarding the +Software and Documentation (collectively, "Feedback") and all Intellectual +Property Rights in and to the Feedback. Except as expressly provided herein, no +licenses of any kind are granted hereunder, whether by implication, estoppel, or +otherwise. + + +10. LIMITED WARRANTY AND DISCLAIMERS + +10.1 Limited Warranty. Kasten warrants for a period of thirty (30) days from +the Effective Date that the Software will materially conform to Kasten"s +then-current Documentation (the "Warranty Period") when properly installed on a +computer for which a license is granted hereunder. Licensee"s exclusive remedy +for a breach of this Section 10.1 is that Kasten shall, at its option, use +commercially reasonable efforts to correct or replace the Software, or refund +all or a portion of the fees paid by Licensee pursuant to the Purchase +Agreement. Kasten, in its sole discretion, may revise this limited warranty from +time to time. + +10.2 Third-Party Software. Except as expressly set forth in this Agreement, +Third-Party Software (including any Open Source Software) are provided on an +"as-is" basis at the sole risk of Licensee. Notwithstanding any language to the +contrary in this Agreement, Kasten makes no express or implied warranties of any +kind with respect to Third-Party Software provided to Licensee and shall not be +liable for any damages regarding the use or operation of the Third-Party +Software furnished under this Agreement. Any and all express or implied +warranties, if any, arising from the license of Third-Party Software shall be +those warranties running from the third party manufacturer or licensor to +Licensee. + +10.3 Warranty Disclaimer. EXCEPT FOR THE LIMITED WARRANTY PROVIDED ABOVE, +KASTEN AND ITS SUPPLIERS MAKE NO WARRANTY OF ANY KIND, WHETHER EXPRESS, IMPLIED, +STATUTORY OR OTHERWISE, RELATING TO THE SOFTWARE OR TO KASTEN"S MAINTENANCE, +PROFESSIONAL OR OTHER SERVICES. KASTEN SPECIFICALLY DISCLAIMS ALL IMPLIED +WARRANTIES OF DESIGN, MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE +AND NON-INFRINGEMENT. KASTEN AND ITS SUPPLIERS AND LICENSORS DO NOT WARRANT OR +REPRESENT THAT THE SOFTWARE WILL BE FREE FROM BUGS OR THAT ITS USE WILL BE +UNINTERRUPTED OR ERROR-FREE. THIS DISCLAIMER SHALL APPLY NOTWITHSTANDING THE +FAILURE OF THE ESSENTIAL PURPOSE OF ANY LIMITED REMEDY PROVIDED HEREIN. EXCEPT +AS STATED ABOVE, KASTEN AND ITS SUPPLIERS PROVIDE THE SOFTWARE ON AN "AS IS" +BASIS. KASTEN PROVIDES NO WARRANTIES WITH RESPECT TO THIRD PARTY SOFTWARE AND +OPEN SOURCE SOFTWARE. + + +11. LIMITATIONS OF LIABILITY + +11.1 EXCLUSION OF CERTAIN DAMAGES. EXCEPT FOR BREACHES OF SECTION 6 +(CONFIDENTIALITY) OR SECTION 9 (OWNERSHIP), IN NO EVENT WILL EITHER PARTY BE +LIABLE FOR ANY INDIRECT, CONSEQUENTIAL, EXEMPLARY, SPECIAL, INCIDENTAL OR +RELIANCE DAMAGES, INCLUDING ANY LOST DATA, LOSS OF USE AND LOST PROFITS, ARISING +FROM OR RELATING TO THIS AGREEMENT, THE SOFTWARE OR DOCUMENTATION, EVEN IF SUCH +PARTY KNEW OR SHOULD HAVE KNOWN OF THE POSSIBILITY OF, OR COULD REASONABLY HAVE +PREVENTED, SUCH DAMAGES. + +11.2 LIMITATION OF DAMAGES. EXCEPT FOR THE BREACHES OF SECTION 6 +(CONFIDENTIALITY) OR SECTION 9 (OWNERSHIP), EACH PARTY"S TOTAL CUMULATIVE +LIABILITY ARISING FROM OR RELATED TO THIS AGREEMENT OR THE SOFTWARE, +DOCUMENTATION, OR SERVICES PROVIDED BY KASTEN, WILL NOT EXCEED THE AMOUNT OF +FEES PAID OR PAYABLE BY LICENSEE FOR THE SOFTWARE, DOCUMENTATION OR SERVICES +GIVING RISE TO THE CLAIM IN THE TWELVE (12) MONTHS FOLLOWING THE EFFECTIVE DATE. +LICENSEE AGREES THAT KASTEN"S SUPPLIERS AND LICENSORS WILL HAVE NO LIABILITY OF +ANY KIND UNDER OR AS A RESULT OF THIS AGREEMENT. IN THE CASE OF KASTEN"S +INDEMNIFICATION OBLIGATIONS, KASTEN"S CUMULATIVE LIABILITY UNDER THIS AGREEMENT +SHALL BE LIMITED TO THE SUM OF THE LICENSE FEES PAID OR PAYABLE BY LICENSEE FOR +THE SOFTWARE, DOCUMENTATION OR SERVICES GIVING RISE TO THE CLAIM IN THE TWELVE +(12) MONTHS FOLLOWING THE EFFECTIVE DATE. + +11.3 THIRD PARTY SOFTWARE. NOTWITHSTANDING ANY LANGUAGE TO THE CONTRARY IN THIS +AGREEMENT, KASTEN SHALL NOT BE LIABLE FOR ANY DAMAGES REGARDING THE USE OR +OPERATION OF ANY THIRD-PARTY SOFTWARE FURNISHED UNDER THIS AGREEMENT. + +11.4 LIMITATION OF ACTIONS. IN NO EVENT MAY LICENSEE BRING ANY CAUSE OF ACTION +RELATED TO THIS AGREEMENT MORE THAN ONE (1) YEAR AFTER THE OCCURRENCE OF THE +EVENT GIVING RISE TO THE LIABILITY. + + +12. EXPORT +The Software, Documentation and related technical data may be subject +to U.S. export control laws, including without limitation the U.S. Export +Administration Act and its associated regulations, and may be subject to export +or import regulations in other countries. Licensee shall comply with all such +regulations and agrees to obtain all necessary licenses to export, re-export, or +import the Software, Documentation and related technical data. + + +13. GENERAL + +13.1 No Agency. Kasten and Licensee each acknowledge and agree that the +relationship established by this Agreement is that of independent contractors, +and nothing contained in this Agreement shall be construed to: (1) give either +party the power to direct or control the daytoday activities of the other; (2) +deem the parties to be acting as partners, joint venturers, coowners or +otherwise as participants in a joint undertaking; or (3) permit either party or +any of either party"s officers, directors, employees, agents or representatives +to create or assume any obligation on behalf of or for the account of the other +party for any purpose whatsoever. + +13.2 Compliance with Laws. Each party agrees to comply with all applicable +laws, regulations, and ordinances relating to their performance hereunder. +Without limiting the foregoing, Licensee warrants and covenants that it will +comply with all then current laws and regulations of the United States and other +jurisdictions relating or applicable to Licensee"s use of the Software and +Documentation including, without limitation, those concerning Intellectual +Property Rights, invasion of privacy, defamation, and the import and export of +Software and Documentation. + +13.3 Force Majeure. Except for the duty to pay money, neither party shall be +liable hereunder by reason of any failure or delay in the performance of its +obligations hereunder on account of strikes, riots, fires, flood, storm, +explosions, acts of God, war, governmental action, earthquakes, or any other +cause which is beyond the reasonable control of such party. + +13.4 Governing Law; Venue and Jurisdiction. This Agreement shall be interpreted +according to the laws of the State of California without regard to or +application of choiceoflaw rules or principles. The parties expressly agree +that the United Nations Convention on Contracts for the International Sale of +Goods and the Uniform Computer Information Transactions Act will not apply. Any +legal action or proceeding arising under this Agreement will be brought +exclusively in the federal or state courts located in Santa Clara County, +California and the parties hereby consent to the personal jurisdiction and venue +therein. + +13.5 Injunctive Relief. The parties agree that monetary damages would not be an +adequate remedy for the breach of certain provisions of this Agreement, +including, without limitation, all provisions concerning infringement, +confidentiality and nondisclosure, or limitation on permitted use of the +Software or Documentation. The parties further agree that, in the event of such +breach, injunctive relief would be necessary to prevent irreparable injury. +Accordingly, either party shall have the right to seek injunctive relief or +similar equitable remedies to enforce such party's rights under the pertinent +provisions of this Agreement, without limiting its right to pursue any other +legal remedies available to it. + +13.6 Entire Agreement and Waiver. This Agreement and any exhibits hereto shall +constitute the entire agreement and contains all terms and conditions between +Kasten and Licensee with respect to the subject matter hereof and all prior +agreements, representations, and statement with respect to such subject matter +are superseded hereby. This Agreement may be changed only by written agreement +signed by both Kasten and Licensee. No failure of either party to exercise or +enforce any of its rights under this Agreement shall act as a waiver of +subsequent breaches; and the waiver of any breach shall not act as a waiver of +subsequent breaches. + +13.7 Severability. In the event any provision of this Agreement is held by a +court or other tribunal of competent jurisdiction to be unenforceable, that +provision will be enforced to the maximum extent permissible under applicable +law and the other provisions of this Agreement will remain in full force and +effect. The parties further agree that in the event such provision is an +essential part of this Agreement, they will begin negotiations for a suitable +replacement provision. + +13.8 Counterparts. This Agreement may be executed in any number of +counterparts, each of which, when so executed and delivered (including by +facsimile), shall be deemed an original, and all of which shall constitute one +and the same agreement. + +13.9 Binding Effect. This Agreement shall be binding upon and shall inure to +the benefit of the respective parties hereto, their respective successors and +permitted assigns. + +13.10 Assignment. Neither party may, without the prior written consent of the +other party (which shall not be unreasonably withheld), assign this Agreement, +in whole or in part, either voluntarily or by operation of law, and any attempt +to do so shall be a material default of this Agreement and shall be void. +Notwithstanding the foregoing, Kasten may assign its rights and benefits and +delegate its duties and obligations under this Agreement without the consent of +Licensee in connection with a merger, reorganization or sale of all or +substantially all relevant assets of the assigning party; in each case provided +that such successor assumes the assigning party"s obligations under this +Agreement. + diff --git a/charts/kasten/k10/7.5.401/files/favicon.png b/charts/kasten/k10/7.5.401/files/favicon.png new file mode 100644 index 0000000000000000000000000000000000000000..fb617ce12c6949ed2dd1bec208c179644bcec0d4 GIT binary patch literal 1802 zcmY*adt8!d8-9^Q;U~4MX*yHXJYWfUAO(S@qG@7bGxIP_4N#l{pHRT^D`su#Je8*P z)jUsR^SG>~RHnI>c|c94qVrIzB^}VzOi6TLef53s^LwBBdhYvruIstqKb|*(x_>Vm zW(orU0PgQcKB$Qp?W+&b%!hTB(=-9ZJ-F8ksFRr~Gz%&{)SnR;2smi4KA;0K1i)H~ zW&mkSV8c2F09#E20B|YjW3^Q0LlsjB{)n~2`_H@#H6mfm;80#@AO(MvorH>^v192d zK@vwx00;uS1}4#YF$h6YB8!U`5Uti3cn#L3(N>6c3hyhTRcIg;;muB_Bd{n}6vm1K zLm&`@WEum1knH<@yJkhSis$h-cr=>N=cD*8D0Xrj+6jllp)t;AXJD;5qOb(C9W+Ak?F|q7pJffAA*673Y?wmX(E=lC6DR@JYxrwGA?Yh`eb0}ft~q6 zS-@*H1iGV44=Z|xFY0HMKAkSj8vYM_RRZ#kFlhRKW%GYVP4jgFbD><&8I zv^XWN3}J45EHCNcSEWmiz#JQ&b_X2DY7W>@rOIn$xI_AF1rucFE!8<*l-JhgP|zUu z+v2>;RYq7)Qz|9=2ic-oiBNT`YySLfGuu*-{BD{G zZ`#7`+d>_8F5q}{&U~MZ3}zw|pk|7l4gHa4YjiMY7q8V@-1>riyq$4w9JQ$$Ns6tq zxd-``i1XUF(Zhxs5>eTjYk5i4=>QP3)TGI-P};Tjlnsrg>Ob|`j%;5zFDWN%Z%y}> zY-2O{QzLFqHcH_=D4@)S!_(U(fBl~u1%3WwFcSoL%=~ti|K!Gg=!?<&BiS1cWo-Ue zp`<5vcckA#0?*<~V+R9zV$)}H3(GPURF7yqh^aJg<5$e=A_Gg=tKXdR^>+X-8i@05 z-W|YKG@-|>d@Rl0EXk!sWgcb=S=69-eXoeAWmDQo`1zqnC%X~N=Q=;%4Jswh+Z*A5 z_QLU|!F?nmyQ=b@PNZ>RYn00lH7DvV?U|jj4cUb+==2H!O$R1Yl8wWsOWOscs1$Bw zfc<=a`~uD1BPLFG({Y3AcZa2KGL47TpB~`fk?V@~uPwJt!HuqMf)rtIAKWE$l-m4I z-8}Vmw4&_^O#!OXE6P{?)(Ar|cJ$gmhpaFSa!;`8Pg<|Un<-(>JS+8#5^g`R6}5@= zzOv3hZ|;|;hDWxNbDT~90D zl9pZ@$4O0bmu6AVD3Q&bJ}tfmgsRiZViC;Sy?NhWUV}}+6_;d(vWq`)XZE-`>^laO zJaBz=d5^@Wk`L}45!R+%ABk{HD(>Tmhm)Y6qZt&F7+WRS-7>Rl^~aR?HcHXO0`D7- z|2?nnC98m0i_>!5t;H_a{jzpHtzcD`KaNE9%mjUU_f3WQ!tf#S@TpG-m{oq%DKX5c z`Ra+*XdEGM1-sL%7~UOCaiTAM7Ylh*66f)}w|=c*El=@R!izK=A4n%&O>e+nGDg0% tn7HN~o~pnSpxO)Prh(w&4OH~a8i>le=(TeuS4aD2@%PzJuJejc{Re;E|Hl9T literal 0 HcmV?d00001 diff --git a/charts/kasten/k10/7.5.401/files/kasten-logo.svg b/charts/kasten/k10/7.5.401/files/kasten-logo.svg new file mode 100644 index 0000000000..0d0ef14eee --- /dev/null +++ b/charts/kasten/k10/7.5.401/files/kasten-logo.svg @@ -0,0 +1,24 @@ + + + + + + diff --git a/charts/kasten/k10/7.5.401/files/styles.css b/charts/kasten/k10/7.5.401/files/styles.css new file mode 100644 index 0000000000..2d92057119 --- /dev/null +++ b/charts/kasten/k10/7.5.401/files/styles.css @@ -0,0 +1,113 @@ +.theme-body { + background-color: #efefef; + color: #333; + font-family: 'Source Sans Pro', Helvetica, sans-serif; +} + +.theme-navbar { + background-color: #fff; + box-shadow: 0 2px 2px rgba(0, 0, 0, 0.2); + color: #333; + font-size: 13px; + font-weight: 100; + height: 46px; + overflow: hidden; + padding: 0 10px; +} + +.theme-navbar__logo-wrap { + display: inline-block; + height: 100%; + overflow: hidden; + padding: 10px 15px; + width: 300px; +} + +.theme-navbar__logo { + height: 100%; + max-height: 25px; +} + +.theme-heading { + font-size: 20px; + font-weight: 500; + margin-bottom: 10px; + margin-top: 0; +} + +.theme-panel { + background-color: #fff; + box-shadow: 0 5px 15px rgba(0, 0, 0, 0.5); + padding: 30px; +} + +.theme-btn-provider { + background-color: #fff; + color: #333; + min-width: 250px; +} + +.theme-btn-provider:hover { + color: #999; +} + +.theme-btn--primary { + background-color: #333; + border: none; + color: #fff; + min-width: 200px; + padding: 6px 12px; +} + +.theme-btn--primary:hover { + background-color: #666; + color: #fff; +} + +.theme-btn--success { + background-color: #2FC98E; + color: #fff; + width: 250px; +} + +.theme-btn--success:hover { + background-color: #49E3A8; +} + +.theme-form-row { + display: block; + margin: 20px auto; +} + +.theme-form-input { + border-radius: 4px; + border: 1px solid #CCC; + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); + color: #666; + display: block; + font-size: 14px; + height: 36px; + line-height: 1.42857143; + margin: auto; + padding: 6px 12px; + width: 250px; +} + +.theme-form-input:focus, +.theme-form-input:active { + border-color: #66AFE9; + outline: none; +} + +.theme-form-label { + font-size: 13px; + font-weight: 600; + margin: 4px auto; + position: relative; + text-align: left; + width: 250px; +} + +.theme-link-back { + margin-top: 4px; +} diff --git a/charts/kasten/k10/7.5.401/grafana/dashboards/default/default.json b/charts/kasten/k10/7.5.401/grafana/dashboards/default/default.json new file mode 100644 index 0000000000..3837b25e45 --- /dev/null +++ b/charts/kasten/k10/7.5.401/grafana/dashboards/default/default.json @@ -0,0 +1,6337 @@ +{ + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": "-- Grafana --", + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "target": { + "limit": 100, + "matchAny": false, + "tags": [], + "type": "dashboard" + }, + "type": "dashboard" + } + ] + }, + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 0, + "id": 12, + "links": [], + "liveNow": false, + "panels": [ + { + "collapsed": false, + "datasource": "Prometheus", + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 0 + }, + "id": 53, + "panels": [], + "targets": [ + { + "datasource": "Prometheus", + "refId": "A" + } + ], + "title": "K10 System Resource Usage", + "type": "row" + }, + { + "datasource": "Prometheus", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 1 + }, + "id": 55, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": "Prometheus", + "editorMode": "builder", + "expr": "sum(rate(process_cpu_seconds_total[5m]))", + "legendFormat": "Total CPU seconds", + "range": true, + "refId": "A" + } + ], + "title": "K10 CPU total seconds ", + "type": "timeseries" + }, + { + "datasource": "Prometheus", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "decbytes" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 1 + }, + "id": 57, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": "Prometheus", + "editorMode": "builder", + "expr": "sum(process_resident_memory_bytes)", + "hide": false, + "legendFormat": "Total memory consumption", + "range": true, + "refId": "C" + } + ], + "title": "K10 total memory consumption", + "type": "timeseries" + }, + { + "datasource": "Prometheus", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 9 + }, + "id": 81, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": "Prometheus", + "editorMode": "builder", + "expr": "rate(process_cpu_seconds_total{job=\"httpServiceDiscovery\"}[5m])", + "legendFormat": "{{service}}", + "range": true, + "refId": "A" + }, + { + "datasource": "Prometheus", + "editorMode": "builder", + "expr": "sum(rate(process_cpu_seconds_total{job=\"k10-pods\"}[5m]))", + "hide": false, + "legendFormat": "executor", + "range": true, + "refId": "B" + }, + { + "datasource": "Prometheus", + "editorMode": "builder", + "expr": "sum(rate(process_cpu_seconds_total{job=\"pushAggregator\"}[5m]))", + "hide": false, + "legendFormat": "ephemeral pods", + "range": true, + "refId": "C" + }, + { + "datasource": "Prometheus", + "editorMode": "builder", + "expr": "sum(rate(process_cpu_seconds_total{job=\"prometheus\"}[5m]))", + "hide": false, + "legendFormat": "prometheus", + "range": true, + "refId": "D" + } + ], + "title": "CPU total seconds per service", + "type": "timeseries" + }, + { + "datasource": "Prometheus", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "decbytes" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 9 + }, + "id": 82, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": "Prometheus", + "editorMode": "builder", + "expr": "sum(process_resident_memory_bytes{job=\"pushAggregator\"})", + "hide": false, + "legendFormat": "ephemeral pods", + "range": true, + "refId": "C" + }, + { + "datasource": "Prometheus", + "editorMode": "builder", + "expr": "process_resident_memory_bytes{job=\"httpServiceDiscovery\"}", + "hide": false, + "legendFormat": "{{service}}", + "range": true, + "refId": "A" + }, + { + "datasource": "Prometheus", + "editorMode": "builder", + "expr": "sum(process_resident_memory_bytes{job=\"k10-pods\"})", + "hide": false, + "legendFormat": "executor", + "range": true, + "refId": "B" + }, + { + "datasource": "Prometheus", + "editorMode": "builder", + "expr": "sum(process_resident_memory_bytes{job=\"prometheus\"})", + "hide": false, + "legendFormat": "prometheus", + "range": true, + "refId": "D" + } + ], + "title": "Memory consumption by service", + "type": "timeseries" + }, + { + "collapsed": false, + "datasource": "Prometheus", + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 17 + }, + "id": 18, + "panels": [], + "targets": [ + { + "datasource": "Prometheus", + "refId": "A" + } + ], + "title": "Applications", + "type": "row" + }, + { + "datasource": "Prometheus", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "noValue": "0", + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "yellow", + "value": null + }, + { + "color": "green", + "value": 1 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 5, + "x": 0, + "y": 18 + }, + "id": 24, + "interval": "1m", + "options": { + "colorMode": "value", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "last" + ], + "fields": "", + "values": false + }, + "text": {}, + "textMode": "auto" + }, + "pluginVersion": "9.1.5", + "targets": [ + { + "datasource": "Prometheus", + "exemplar": false, + "expr": "sum(round(increase(action_backup_ended_overall{cluster=\"$cluster\", state=\"succeeded\"}[$__range])))", + "hide": false, + "interval": "", + "legendFormat": "", + "refId": "B" + } + ], + "title": "Backups Completed", + "type": "stat" + }, + { + "datasource": "Prometheus", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [ + { + "options": { + "0": { + "index": 0, + "text": "-" + } + }, + "type": "value" + } + ], + "noValue": "-", + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "text", + "value": null + }, + { + "color": "red", + "value": 1 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 3, + "x": 5, + "y": 18 + }, + "id": 33, + "interval": "1m", + "options": { + "colorMode": "value", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "last" + ], + "fields": "", + "values": false + }, + "text": {}, + "textMode": "auto" + }, + "pluginVersion": "9.1.5", + "targets": [ + { + "datasource": "Prometheus", + "exemplar": false, + "expr": "sum(round(increase(action_backup_ended_overall{cluster=\"$cluster\", state=~\"failed|cancelled\"}[$__range])))", + "hide": false, + "interval": "", + "legendFormat": "", + "refId": "B" + } + ], + "title": "Backups Failed", + "type": "stat" + }, + { + "datasource": "Prometheus", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [ + { + "options": { + "0": { + "index": 0, + "text": "-" + } + }, + "type": "value" + } + ], + "noValue": "-", + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "text", + "value": null + }, + { + "color": "#EAB839", + "value": 1 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 3, + "x": 8, + "y": 18 + }, + "id": 34, + "interval": "1m", + "options": { + "colorMode": "value", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "last" + ], + "fields": "", + "values": false + }, + "text": {}, + "textMode": "auto" + }, + "pluginVersion": "9.1.5", + "targets": [ + { + "datasource": "Prometheus", + "exemplar": false, + "expr": "sum(round(increase(action_backup_skipped_overall{cluster=\"$cluster\"}[$__range])))", + "hide": false, + "interval": "", + "legendFormat": "", + "refId": "B" + } + ], + "title": "Backups Skipped", + "type": "stat" + }, + { + "datasource": "Prometheus", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [ + { + "options": { + "0": { + "index": 0, + "text": "-" + } + }, + "type": "value" + } + ], + "noValue": "-", + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "text", + "value": null + }, + { + "color": "green", + "value": 1 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 5, + "x": 13, + "y": 18 + }, + "id": 35, + "interval": "1m", + "options": { + "colorMode": "value", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "last" + ], + "fields": "", + "values": false + }, + "text": {}, + "textMode": "auto" + }, + "pluginVersion": "9.1.5", + "targets": [ + { + "datasource": "Prometheus", + "exemplar": false, + "expr": "sum(round(increase(action_restore_ended_overall{cluster=\"$cluster\", state=\"succeeded\"}[$__range])))", + "hide": false, + "interval": "", + "legendFormat": "", + "refId": "B" + } + ], + "title": "Restores Completed", + "type": "stat" + }, + { + "datasource": "Prometheus", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [ + { + "options": { + "0": { + "index": 0, + "text": "-" + } + }, + "type": "value" + } + ], + "noValue": "-", + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "text", + "value": null + }, + { + "color": "red", + "value": 1 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 3, + "x": 18, + "y": 18 + }, + "id": 36, + "interval": "1m", + "options": { + "colorMode": "value", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "last" + ], + "fields": "", + "values": false + }, + "text": {}, + "textMode": "auto" + }, + "pluginVersion": "9.1.5", + "targets": [ + { + "datasource": "Prometheus", + "exemplar": false, + "expr": "sum(round(increase(action_restore_ended_overall{cluster=\"$cluster\", state=~\"failed|cancelled\"}[$__range])))", + "hide": false, + "interval": "", + "legendFormat": "", + "refId": "B" + } + ], + "title": "Restores Failed", + "type": "stat" + }, + { + "datasource": "Prometheus", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [ + { + "options": { + "0": { + "index": 0, + "text": "-" + } + }, + "type": "value" + } + ], + "noValue": "-", + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "text", + "value": null + }, + { + "color": "#EAB839", + "value": 1 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 3, + "x": 21, + "y": 18 + }, + "id": 23, + "interval": "1m", + "options": { + "colorMode": "value", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "last" + ], + "fields": "", + "values": false + }, + "text": {}, + "textMode": "auto" + }, + "pluginVersion": "9.1.5", + "targets": [ + { + "datasource": "Prometheus", + "exemplar": false, + "expr": "sum(round(increase(action_restore_skipped_overall{cluster=\"$cluster\"}[$__range])))", + "hide": false, + "interval": "", + "legendFormat": "", + "refId": "B" + } + ], + "title": "Restores Skipped", + "type": "stat" + }, + { + "collapsed": false, + "datasource": "Prometheus", + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 25 + }, + "id": 16, + "panels": [], + "targets": [ + { + "datasource": "Prometheus", + "refId": "A" + } + ], + "title": "Cluster", + "type": "row" + }, + { + "datasource": "Prometheus", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "noValue": "0", + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "yellow", + "value": null + }, + { + "color": "green", + "value": 1 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 5, + "x": 0, + "y": 26 + }, + "id": 10, + "interval": "1m", + "options": { + "colorMode": "value", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "last" + ], + "fields": "", + "values": false + }, + "text": {}, + "textMode": "auto" + }, + "pluginVersion": "9.1.5", + "targets": [ + { + "datasource": "Prometheus", + "exemplar": false, + "expr": "sum(round(increase(action_backup_cluster_ended_overall{cluster=\"$cluster\", state=\"succeeded\"}[$__range])))", + "hide": false, + "interval": "", + "legendFormat": "", + "refId": "B" + } + ], + "title": "Cluster Backups Completed", + "type": "stat" + }, + { + "datasource": "Prometheus", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [ + { + "options": { + "0": { + "index": 0, + "text": "-" + } + }, + "type": "value" + } + ], + "noValue": "-", + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "text", + "value": null + }, + { + "color": "red", + "value": 1 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 3, + "x": 5, + "y": 26 + }, + "id": 19, + "interval": "1m", + "options": { + "colorMode": "value", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "last" + ], + "fields": "", + "values": false + }, + "text": {}, + "textMode": "auto" + }, + "pluginVersion": "9.1.5", + "targets": [ + { + "datasource": "Prometheus", + "exemplar": false, + "expr": "sum(round(increase(action_backup_cluster_ended_overall{cluster=\"$cluster\", state=~\"failed|cancelled\"}[$__range])))", + "hide": false, + "interval": "", + "legendFormat": "", + "refId": "B" + } + ], + "title": "Cluster Backups Failed", + "type": "stat" + }, + { + "datasource": "Prometheus", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [ + { + "options": { + "0": { + "index": 0, + "text": "-" + } + }, + "type": "value" + } + ], + "noValue": "-", + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "text", + "value": null + }, + { + "color": "#EAB839", + "value": 1 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 3, + "x": 8, + "y": 26 + }, + "id": 28, + "interval": "1m", + "options": { + "colorMode": "value", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "last" + ], + "fields": "", + "values": false + }, + "text": {}, + "textMode": "auto" + }, + "pluginVersion": "9.1.5", + "targets": [ + { + "datasource": "Prometheus", + "exemplar": false, + "expr": "sum(round(increase(action_backup_cluster_skipped_overall{cluster=\"$cluster\"}[$__range])))", + "hide": false, + "interval": "", + "legendFormat": "", + "refId": "B" + } + ], + "title": "Cluster Backups Skipped", + "type": "stat" + }, + { + "datasource": "Prometheus", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [ + { + "options": { + "0": { + "index": 0, + "text": "-" + } + }, + "type": "value" + } + ], + "noValue": "-", + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "text", + "value": null + }, + { + "color": "green", + "value": 1 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 5, + "x": 13, + "y": 26 + }, + "id": 21, + "interval": "1m", + "options": { + "colorMode": "value", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "last" + ], + "fields": "", + "values": false + }, + "text": {}, + "textMode": "auto" + }, + "pluginVersion": "9.1.5", + "targets": [ + { + "datasource": "Prometheus", + "exemplar": false, + "expr": "sum(round(increase(action_restore_cluster_ended_overall{cluster=\"$cluster\", state=\"succeeded\"}[$__range])))", + "hide": false, + "interval": "", + "legendFormat": "", + "refId": "B" + } + ], + "title": "Cluster Restores Completed", + "type": "stat" + }, + { + "datasource": "Prometheus", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [ + { + "options": { + "0": { + "index": 0, + "text": "-" + } + }, + "type": "value" + } + ], + "noValue": "-", + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "text", + "value": null + }, + { + "color": "red", + "value": 1 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 3, + "x": 18, + "y": 26 + }, + "id": 22, + "interval": "1m", + "options": { + "colorMode": "value", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "last" + ], + "fields": "", + "values": false + }, + "text": {}, + "textMode": "auto" + }, + "pluginVersion": "9.1.5", + "targets": [ + { + "datasource": "Prometheus", + "exemplar": false, + "expr": "sum(round(increase(action_restore_cluster_ended_overall{cluster=\"$cluster\", state=~\"failed|cancelled\"}[$__range])))", + "hide": false, + "interval": "", + "legendFormat": "", + "refId": "B" + } + ], + "title": "Cluster Restores Failed", + "type": "stat" + }, + { + "datasource": "Prometheus", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [ + { + "options": { + "0": { + "index": 0, + "text": "-" + } + }, + "type": "value" + } + ], + "noValue": "-", + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "text", + "value": null + }, + { + "color": "#EAB839", + "value": 1 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 3, + "x": 21, + "y": 26 + }, + "id": 25, + "interval": "1m", + "options": { + "colorMode": "value", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "last" + ], + "fields": "", + "values": false + }, + "text": {}, + "textMode": "auto" + }, + "pluginVersion": "9.1.5", + "targets": [ + { + "datasource": "Prometheus", + "exemplar": false, + "expr": "sum(round(increase(action_restore_cluster_skipped_overall{cluster=\"$cluster\"}[$__range])))", + "hide": false, + "interval": "", + "legendFormat": "", + "refId": "B" + } + ], + "title": "Cluster Restores Skipped", + "type": "stat" + }, + { + "collapsed": false, + "datasource": "Prometheus", + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 33 + }, + "id": 31, + "panels": [], + "targets": [ + { + "datasource": "Prometheus", + "refId": "A" + } + ], + "title": "Backup Exports", + "type": "row" + }, + { + "datasource": "Prometheus", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "noValue": "0", + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "text", + "value": null + }, + { + "color": "green", + "value": 1 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 6, + "w": 5, + "x": 0, + "y": 34 + }, + "id": 38, + "interval": "1m", + "options": { + "colorMode": "value", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "last" + ], + "fields": "", + "values": false + }, + "text": {}, + "textMode": "auto" + }, + "pluginVersion": "9.1.5", + "targets": [ + { + "datasource": "Prometheus", + "exemplar": false, + "expr": "sum(round(increase(action_export_ended_overall{cluster=\"$cluster\", state=\"succeeded\"}[$__range])))", + "hide": false, + "interval": "", + "legendFormat": "", + "refId": "B" + } + ], + "title": "Exports Completed", + "type": "stat" + }, + { + "datasource": "Prometheus", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [ + { + "options": { + "0": { + "index": 0, + "text": "-" + } + }, + "type": "value" + } + ], + "noValue": "-", + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "text", + "value": null + }, + { + "color": "red", + "value": 1 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 6, + "w": 3, + "x": 5, + "y": 34 + }, + "id": 29, + "interval": "1m", + "options": { + "colorMode": "value", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "last" + ], + "fields": "", + "values": false + }, + "text": {}, + "textMode": "auto" + }, + "pluginVersion": "9.1.5", + "targets": [ + { + "datasource": "Prometheus", + "exemplar": false, + "expr": "sum(round(increase(action_export_ended_overall{cluster=\"$cluster\", state=~\"failed|cancelled\"}[$__range])))", + "hide": false, + "interval": "", + "legendFormat": "", + "refId": "B" + } + ], + "title": "Exports Failed", + "type": "stat" + }, + { + "datasource": "Prometheus", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [ + { + "options": { + "0": { + "index": 0, + "text": "-" + } + }, + "type": "value" + } + ], + "noValue": "-", + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "text", + "value": null + }, + { + "color": "#EAB839", + "value": 1 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 6, + "w": 3, + "x": 8, + "y": 34 + }, + "id": 20, + "interval": "1m", + "options": { + "colorMode": "value", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "last" + ], + "fields": "", + "values": false + }, + "text": {}, + "textMode": "auto" + }, + "pluginVersion": "9.1.5", + "targets": [ + { + "datasource": "Prometheus", + "exemplar": false, + "expr": "sum(round(increase(action_export_skipped_overall{cluster=\"$cluster\"}[$__range])))", + "hide": false, + "interval": "", + "legendFormat": "", + "refId": "B" + } + ], + "title": "Exports Skipped", + "type": "stat" + }, + { + "datasource": "Prometheus", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "noValue": "0", + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "text", + "value": null + }, + { + "color": "green", + "value": 1 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 6, + "w": 5, + "x": 13, + "y": 34 + }, + "id": 27, + "interval": "1m", + "options": { + "colorMode": "value", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "last" + ], + "fields": "", + "values": false + }, + "text": {}, + "textMode": "auto" + }, + "pluginVersion": "9.1.5", + "targets": [ + { + "datasource": "Prometheus", + "exemplar": false, + "expr": "sum(round(increase(action_import_ended_overall{cluster=\"$cluster\", state=\"succeeded\"}[$__range])))", + "hide": false, + "interval": "", + "legendFormat": "", + "refId": "B" + } + ], + "title": "Imports Completed", + "type": "stat" + }, + { + "datasource": "Prometheus", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [ + { + "options": { + "0": { + "index": 0, + "text": "-" + } + }, + "type": "value" + } + ], + "noValue": "-", + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "text", + "value": null + }, + { + "color": "red", + "value": 1 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 6, + "w": 3, + "x": 18, + "y": 34 + }, + "id": 39, + "interval": "1m", + "options": { + "colorMode": "value", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "last" + ], + "fields": "", + "values": false + }, + "text": {}, + "textMode": "auto" + }, + "pluginVersion": "9.1.5", + "targets": [ + { + "datasource": "Prometheus", + "exemplar": false, + "expr": "sum(round(increase(action_import_ended_overall{cluster=\"$cluster\", state=~\"failed|cancelled\"}[$__range])))", + "hide": false, + "interval": "", + "legendFormat": "", + "refId": "B" + } + ], + "title": "Imports Failed", + "type": "stat" + }, + { + "datasource": "Prometheus", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [ + { + "options": { + "0": { + "index": 0, + "text": "-" + } + }, + "type": "value" + } + ], + "noValue": "-", + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "text", + "value": null + }, + { + "color": "#EAB839", + "value": 1 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 6, + "w": 3, + "x": 21, + "y": 34 + }, + "id": 37, + "interval": "1m", + "options": { + "colorMode": "value", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "last" + ], + "fields": "", + "values": false + }, + "text": {}, + "textMode": "auto" + }, + "pluginVersion": "9.1.5", + "targets": [ + { + "datasource": "Prometheus", + "exemplar": false, + "expr": "sum(round(increase(action_import_skipped_overall{cluster=\"$cluster\"}[$__range])))", + "hide": false, + "interval": "", + "legendFormat": "", + "refId": "B" + } + ], + "title": "Imports Skipped", + "type": "stat" + }, + { + "collapsed": false, + "datasource": "Prometheus", + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 40 + }, + "id": 14, + "panels": [], + "targets": [ + { + "datasource": "Prometheus", + "refId": "A" + } + ], + "title": "System", + "type": "row" + }, + { + "datasource": "Prometheus", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [ + { + "options": { + "0": { + "index": 0, + "text": "-" + } + }, + "type": "value" + } + ], + "noValue": "-", + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "text", + "value": null + }, + { + "color": "green", + "value": 1 + } + ] + }, + "unit": "runs" + }, + "overrides": [] + }, + "gridPos": { + "h": 6, + "w": 3, + "x": 0, + "y": 41 + }, + "id": 12, + "interval": "1m", + "options": { + "colorMode": "value", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "last" + ], + "fields": "", + "values": false + }, + "text": {}, + "textMode": "auto" + }, + "pluginVersion": "9.1.5", + "targets": [ + { + "datasource": "Prometheus", + "exemplar": false, + "expr": "sum(round(increase(action_run_ended_overall{cluster=\"$cluster\", state=\"succeeded\"}[$__range])))", + "format": "time_series", + "interval": "", + "legendFormat": "", + "queryType": "randomWalk", + "refId": "A" + } + ], + "title": "Policy Runs", + "type": "stat" + }, + { + "datasource": "Prometheus", + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [ + { + "options": { + "0": { + "index": 0, + "text": "-" + } + }, + "type": "value" + } + ], + "noValue": "-", + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "text", + "value": null + }, + { + "color": "yellow", + "value": 1 + } + ] + }, + "unit": "runs" + }, + "overrides": [] + }, + "gridPos": { + "h": 6, + "w": 3, + "x": 3, + "y": 41 + }, + "id": 40, + "interval": "1m", + "options": { + "colorMode": "value", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "last" + ], + "fields": "", + "values": false + }, + "text": {}, + "textMode": "auto" + }, + "pluginVersion": "9.1.5", + "targets": [ + { + "datasource": "Prometheus", + "exemplar": false, + "expr": "sum(round(increase(action_run_skipped_overall{cluster=\"$cluster\"}[$__range])))", + "format": "time_series", + "interval": "", + "legendFormat": "", + "queryType": "randomWalk", + "refId": "A" + } + ], + "title": "Policy Runs Skipped", + "type": "stat" + }, + { + "datasource": "Prometheus", + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "noValue": "-", + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "#ccccdc", + "value": null + } + ] + }, + "unit": "bytes" + }, + "overrides": [] + }, + "gridPos": { + "h": 6, + "w": 3, + "x": 6, + "y": 41 + }, + "id": 6, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "text": {}, + "textMode": "auto" + }, + "pluginVersion": "9.1.5", + "targets": [ + { + "datasource": "Prometheus", + "exemplar": true, + "expr": "catalog_persistent_volume_disk_space_used_bytes{cluster=\"$cluster\"}", + "interval": "", + "legendFormat": "", + "queryType": "randomWalk", + "refId": "A" + } + ], + "title": "Catalog Volume Used", + "type": "stat" + }, + { + "datasource": "Prometheus", + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "max": 100, + "min": 0, + "noValue": "-", + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "yellow", + "value": 70 + }, + { + "color": "orange", + "value": 80 + }, + { + "color": "red", + "value": 90 + } + ] + }, + "unit": "percent" + }, + "overrides": [] + }, + "gridPos": { + "h": 6, + "w": 3, + "x": 9, + "y": 41 + }, + "id": 2, + "options": { + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showThresholdLabels": false, + "showThresholdMarkers": true, + "text": {} + }, + "pluginVersion": "9.1.5", + "targets": [ + { + "datasource": "Prometheus", + "exemplar": true, + "expr": "100-catalog_persistent_volume_free_space_percent{cluster=\"$cluster\"}", + "interval": "", + "legendFormat": "", + "queryType": "randomWalk", + "refId": "A" + } + ], + "title": "Catalog Volume Used Space", + "type": "gauge" + }, + { + "datasource": "Prometheus", + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "noValue": "-", + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "#ccccdc", + "value": null + } + ] + }, + "unit": "bytes" + }, + "overrides": [] + }, + "gridPos": { + "h": 6, + "w": 3, + "x": 12, + "y": 41 + }, + "id": 8, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "text": {}, + "textMode": "auto" + }, + "pluginVersion": "9.1.5", + "targets": [ + { + "datasource": "Prometheus", + "exemplar": true, + "expr": "jobs_persistent_volume_disk_space_used_bytes{cluster=\"$cluster\"}", + "interval": "", + "legendFormat": "", + "queryType": "randomWalk", + "refId": "A" + } + ], + "title": "Jobs Volume Used", + "type": "stat" + }, + { + "datasource": "Prometheus", + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "max": 100, + "min": 0, + "noValue": "-", + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "yellow", + "value": 70 + }, + { + "color": "orange", + "value": 80 + }, + { + "color": "red", + "value": 90 + } + ] + }, + "unit": "percent" + }, + "overrides": [] + }, + "gridPos": { + "h": 6, + "w": 3, + "x": 15, + "y": 41 + }, + "id": 4, + "options": { + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showThresholdLabels": false, + "showThresholdMarkers": true, + "text": {} + }, + "pluginVersion": "9.1.5", + "targets": [ + { + "datasource": "Prometheus", + "exemplar": true, + "expr": "100-jobs_persistent_volume_free_space_percent{cluster=\"$cluster\"}", + "interval": "", + "legendFormat": "", + "queryType": "randomWalk", + "refId": "A" + } + ], + "title": "Jobs Volume Used Space", + "type": "gauge" + }, + { + "datasource": "Prometheus", + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "noValue": "-", + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "#ccccdc", + "value": null + } + ] + }, + "unit": "bytes" + }, + "overrides": [] + }, + "gridPos": { + "h": 6, + "w": 3, + "x": 18, + "y": 41 + }, + "id": 7, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "text": {}, + "textMode": "auto" + }, + "pluginVersion": "9.1.5", + "targets": [ + { + "datasource": "Prometheus", + "exemplar": true, + "expr": "logging_persistent_volume_disk_space_used_bytes{cluster=\"$cluster\"}", + "interval": "", + "legendFormat": "", + "queryType": "randomWalk", + "refId": "A" + } + ], + "title": "Logging Volume Used", + "type": "stat" + }, + { + "datasource": "Prometheus", + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "max": 100, + "min": 0, + "noValue": "-", + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "yellow", + "value": 70 + }, + { + "color": "orange", + "value": 80 + }, + { + "color": "red", + "value": 90 + } + ] + }, + "unit": "percent" + }, + "overrides": [] + }, + "gridPos": { + "h": 6, + "w": 3, + "x": 21, + "y": 41 + }, + "id": 3, + "options": { + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showThresholdLabels": false, + "showThresholdMarkers": true, + "text": {} + }, + "pluginVersion": "9.1.5", + "targets": [ + { + "datasource": "Prometheus", + "exemplar": true, + "expr": "100-logging_persistent_volume_free_space_percent{cluster=\"$cluster\"}", + "interval": "", + "legendFormat": "", + "queryType": "randomWalk", + "refId": "A" + } + ], + "title": "Logging Volume Used Space", + "type": "gauge" + }, + { + "datasource": "Prometheus", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "noValue": "0", + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "text", + "value": null + }, + { + "color": "green", + "value": 1 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 6, + "w": 3, + "x": 0, + "y": 47 + }, + "id": 41, + "interval": "1m", + "options": { + "colorMode": "value", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "last" + ], + "fields": "", + "values": false + }, + "text": {}, + "textMode": "auto" + }, + "pluginVersion": "9.1.5", + "targets": [ + { + "datasource": "Prometheus", + "exemplar": false, + "expr": "compliance_count{state=\"Compliant\"}", + "hide": false, + "interval": "", + "legendFormat": "", + "refId": "B" + } + ], + "title": "Compliant Applications", + "type": "stat" + }, + { + "datasource": "Prometheus", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "noValue": "0", + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 1 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 6, + "w": 3, + "x": 3, + "y": 47 + }, + "id": 42, + "interval": "1m", + "options": { + "colorMode": "value", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "last" + ], + "fields": "", + "values": false + }, + "text": {}, + "textMode": "auto" + }, + "pluginVersion": "9.1.5", + "targets": [ + { + "datasource": "Prometheus", + "exemplar": false, + "expr": "compliance_count{state=\"NotCompliant\"}", + "hide": false, + "interval": "", + "legendFormat": "", + "refId": "B" + } + ], + "title": "Non-Compliant Applications", + "type": "stat" + }, + { + "datasource": "Prometheus", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "noValue": "0", + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 1 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 6, + "w": 3, + "x": 6, + "y": 47 + }, + "id": 43, + "interval": "1m", + "options": { + "colorMode": "value", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "last" + ], + "fields": "", + "values": false + }, + "text": {}, + "textMode": "auto" + }, + "pluginVersion": "9.1.5", + "targets": [ + { + "datasource": "Prometheus", + "exemplar": false, + "expr": "compliance_count{state=\"Unmanaged\"}", + "hide": false, + "interval": "", + "legendFormat": "", + "refId": "B" + } + ], + "title": "Unmanaged Applications", + "type": "stat" + }, + { + "datasource": "Prometheus", + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "noValue": "-", + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "#ccccdc", + "value": null + } + ] + }, + "unit": "bytes" + }, + "overrides": [] + }, + "gridPos": { + "h": 6, + "w": 3, + "x": 12, + "y": 47 + }, + "id": 44, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "text": {}, + "textMode": "auto" + }, + "pluginVersion": "9.1.5", + "targets": [ + { + "datasource": "Prometheus", + "exemplar": true, + "expr": "snapshot_storage_size_bytes{cluster=\"$cluster\", type=\"physical\"}", + "interval": "", + "legendFormat": "", + "queryType": "randomWalk", + "refId": "A" + } + ], + "title": "Snapshot Size (Physical)", + "type": "stat" + }, + { + "datasource": "Prometheus", + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "noValue": "-", + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "#ccccdc", + "value": null + } + ] + }, + "unit": "bytes" + }, + "overrides": [] + }, + "gridPos": { + "h": 6, + "w": 3, + "x": 15, + "y": 47 + }, + "id": 45, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "text": {}, + "textMode": "auto" + }, + "pluginVersion": "9.1.5", + "targets": [ + { + "datasource": "Prometheus", + "exemplar": true, + "expr": "snapshot_storage_size_bytes{cluster=\"$cluster\", type=\"logical\"}", + "interval": "", + "legendFormat": "", + "queryType": "randomWalk", + "refId": "A" + } + ], + "title": "Snapshot Size (Logical)", + "type": "stat" + }, + { + "datasource": "Prometheus", + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "noValue": "-", + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "#ccccdc", + "value": null + } + ] + }, + "unit": "bytes" + }, + "overrides": [] + }, + "gridPos": { + "h": 6, + "w": 3, + "x": 18, + "y": 47 + }, + "id": 46, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "text": {}, + "textMode": "auto" + }, + "pluginVersion": "9.1.5", + "targets": [ + { + "datasource": "Prometheus", + "exemplar": true, + "expr": "export_storage_size_bytes{cluster=\"$cluster\", type=\"physical\"}", + "interval": "", + "legendFormat": "", + "queryType": "randomWalk", + "refId": "A" + } + ], + "title": "Export Size (Physical)", + "type": "stat" + }, + { + "datasource": "Prometheus", + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "noValue": "-", + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "#ccccdc", + "value": null + } + ] + }, + "unit": "bytes" + }, + "overrides": [] + }, + "gridPos": { + "h": 6, + "w": 3, + "x": 21, + "y": 47 + }, + "id": 47, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "text": {}, + "textMode": "auto" + }, + "pluginVersion": "9.1.5", + "targets": [ + { + "datasource": "Prometheus", + "exemplar": true, + "expr": "export_storage_size_bytes{cluster=\"$cluster\", type=\"logical\"}", + "interval": "", + "legendFormat": "", + "queryType": "randomWalk", + "refId": "A" + } + ], + "title": "Export Size (Logical)", + "type": "stat" + }, + { + "collapsed": true, + "datasource": "Prometheus", + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 53 + }, + "id": 49, + "panels": [ + { + "datasource": "Prometheus", + "fieldConfig": { + "defaults": { + "color": { + "fixedColor": "red", + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "Worker Count" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "dark-red", + "mode": "fixed" + } + } + ] + } + ] + }, + "gridPos": { + "h": 7, + "w": 12, + "x": 0, + "y": 54 + }, + "id": 57, + "interval": "5s", + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": "Prometheus", + "editorMode": "code", + "expr": "sum(exec_executor_worker_count)", + "legendFormat": "Worker Count", + "range": true, + "refId": "A" + }, + { + "datasource": "Prometheus", + "editorMode": "code", + "expr": "sum(exec_active_job_count) OR on() vector(0)", + "hide": false, + "legendFormat": "Worker Load", + "range": true, + "refId": "B" + } + ], + "title": "Executor Worker Load", + "type": "timeseries" + }, + { + "datasource": "Prometheus", + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineStyle": { + "fill": "solid" + }, + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "s" + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 12, + "x": 12, + "y": 54 + }, + "id": 68, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": "Prometheus", + "editorMode": "code", + "expr": "sum(rate(action_backup_duration_seconds_sum_overall[5m])) / sum(rate(action_backup_ended_overall[5m]))", + "hide": false, + "legendFormat": "Backup", + "range": true, + "refId": "A" + }, + { + "datasource": "Prometheus", + "editorMode": "code", + "expr": "sum(rate(action_backup_cluster_duration_seconds_overall_sum[5m])) / sum(rate(action_backup_cluster_ended_overall[5m]))", + "hide": false, + "legendFormat": "Backup Cluster", + "range": true, + "refId": "B" + }, + { + "datasource": "Prometheus", + "editorMode": "code", + "expr": "sum(rate(action_export_duration_seconds_sum_overall[5m])) / sum(rate(action_export_ended_overall[5m]))", + "hide": false, + "legendFormat": "Export", + "range": true, + "refId": "C" + }, + { + "datasource": "Prometheus", + "editorMode": "code", + "expr": "sum(rate(action_import_duration_seconds_sum_overall[5m])) / sum(rate(action_import_ended_overall[5m]))", + "hide": false, + "legendFormat": "Import", + "range": true, + "refId": "D" + }, + { + "datasource": "Prometheus", + "editorMode": "code", + "expr": "sum(rate(action_report_duration_seconds_sum_overall[5m])) / sum(rate(action_report_ended_overall[5m]))", + "hide": false, + "legendFormat": "Report", + "range": true, + "refId": "E" + }, + { + "datasource": "Prometheus", + "editorMode": "code", + "expr": "sum(rate(action_retire_duration_seconds_sum_overall[5m])) / sum(rate(action_retire_ended_overall[5m]))", + "hide": false, + "legendFormat": "Retire", + "range": true, + "refId": "F" + }, + { + "datasource": "Prometheus", + "editorMode": "code", + "expr": "sum(rate(action_restore_duration_seconds_sum_overall[5m])) / sum(rate(action_restore_ended_overall[5m]))", + "hide": false, + "legendFormat": "Restore", + "range": true, + "refId": "G" + }, + { + "datasource": "Prometheus", + "editorMode": "code", + "expr": "sum(rate(action_restore_cluster_duration_seconds_sum_overall[5m])) / sum(rate(action_restore_cluster_ended_overall[5m]))", + "hide": false, + "legendFormat": "Restore Cluster", + "range": true, + "refId": "H" + } + ], + "title": "Average Action Duration", + "type": "timeseries" + }, + { + "datasource": "Prometheus", + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "axisSoftMax": 0, + "axisSoftMin": 0, + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "decimals": 0, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "none" + }, + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "succeeded" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "semi-dark-green", + "mode": "fixed" + } + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "failed" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "semi-dark-red", + "mode": "fixed" + } + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "cancelled" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "semi-dark-orange", + "mode": "fixed" + } + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "skipped" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "semi-dark-blue", + "mode": "fixed" + } + } + ] + } + ] + }, + "gridPos": { + "h": 7, + "w": 6, + "x": 0, + "y": 61 + }, + "id": 74, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": "Prometheus", + "editorMode": "code", + "expr": "sum(round(increase(action_backup_ended_overall[1m:10s]))) by (state)", + "hide": false, + "legendFormat": "__auto", + "range": true, + "refId": "A" + } + ], + "title": "Finished Backups", + "transformations": [], + "type": "timeseries" + }, + { + "datasource": "Prometheus", + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "axisSoftMax": 0, + "axisSoftMin": 0, + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "decimals": 0, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "none" + }, + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "succeeded" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "semi-dark-green", + "mode": "fixed" + } + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "failed" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "semi-dark-red", + "mode": "fixed" + } + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "cancelled" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "semi-dark-orange", + "mode": "fixed" + } + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "skipped" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "semi-dark-blue", + "mode": "fixed" + } + } + ] + } + ] + }, + "gridPos": { + "h": 7, + "w": 6, + "x": 6, + "y": 61 + }, + "id": 69, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": "Prometheus", + "editorMode": "code", + "expr": "sum(round(increase(action_backup_cluster_ended_overall[1m:10s]))) by (state)", + "hide": false, + "legendFormat": "__auto", + "range": true, + "refId": "A" + } + ], + "title": "Finished Cluster Backups", + "transformations": [], + "type": "timeseries" + }, + { + "datasource": "Prometheus", + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "axisSoftMax": 0, + "axisSoftMin": 0, + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "decimals": 0, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "none" + }, + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "succeeded" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "semi-dark-green", + "mode": "fixed" + } + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "failed" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "semi-dark-red", + "mode": "fixed" + } + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "cancelled" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "semi-dark-orange", + "mode": "fixed" + } + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "skipped" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "semi-dark-blue", + "mode": "fixed" + } + } + ] + } + ] + }, + "gridPos": { + "h": 7, + "w": 6, + "x": 12, + "y": 61 + }, + "id": 75, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": "Prometheus", + "editorMode": "code", + "expr": "sum(round(increase(action_export_ended_overall[1m:10s]))) by (state)", + "hide": false, + "legendFormat": "__auto", + "range": true, + "refId": "A" + } + ], + "title": "Finished Exports", + "transformations": [], + "type": "timeseries" + }, + { + "datasource": "Prometheus", + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "axisSoftMax": 0, + "axisSoftMin": 0, + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "decimals": 0, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "none" + }, + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "succeeded" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "semi-dark-green", + "mode": "fixed" + } + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "failed" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "semi-dark-red", + "mode": "fixed" + } + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "cancelled" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "semi-dark-orange", + "mode": "fixed" + } + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "skipped" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "semi-dark-blue", + "mode": "fixed" + } + } + ] + } + ] + }, + "gridPos": { + "h": 7, + "w": 6, + "x": 18, + "y": 61 + }, + "id": 76, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": "Prometheus", + "editorMode": "code", + "expr": "sum(round(increase(action_import_ended_overall[1m:10s]))) by (state)", + "hide": false, + "legendFormat": "__auto", + "range": true, + "refId": "A" + } + ], + "title": "Finished Imports", + "transformations": [], + "type": "timeseries" + }, + { + "datasource": "Prometheus", + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "axisSoftMax": 0, + "axisSoftMin": 0, + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "decimals": 0, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "none" + }, + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "succeeded" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "semi-dark-green", + "mode": "fixed" + } + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "failed" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "semi-dark-red", + "mode": "fixed" + } + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "cancelled" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "semi-dark-orange", + "mode": "fixed" + } + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "skipped" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "semi-dark-blue", + "mode": "fixed" + } + } + ] + } + ] + }, + "gridPos": { + "h": 7, + "w": 6, + "x": 0, + "y": 68 + }, + "id": 77, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": "Prometheus", + "editorMode": "code", + "expr": "sum(round(increase(action_report_ended_overall[1m:10s]))) by (state)", + "hide": false, + "legendFormat": "__auto", + "range": true, + "refId": "A" + } + ], + "title": "Finished Reports", + "transformations": [], + "type": "timeseries" + }, + { + "datasource": "Prometheus", + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "axisSoftMax": 0, + "axisSoftMin": 0, + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "decimals": 0, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "none" + }, + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "succeeded" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "semi-dark-green", + "mode": "fixed" + } + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "failed" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "semi-dark-red", + "mode": "fixed" + } + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "cancelled" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "semi-dark-orange", + "mode": "fixed" + } + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "skipped" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "semi-dark-blue", + "mode": "fixed" + } + } + ] + } + ] + }, + "gridPos": { + "h": 7, + "w": 6, + "x": 6, + "y": 68 + }, + "id": 79, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": "Prometheus", + "editorMode": "code", + "expr": "sum(round(increase(action_retire_ended_overall[1m:10s]))) by (state)", + "hide": false, + "legendFormat": "__auto", + "range": true, + "refId": "A" + } + ], + "title": "Finished Retires", + "transformations": [], + "type": "timeseries" + }, + { + "datasource": "Prometheus", + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "axisSoftMax": 0, + "axisSoftMin": 0, + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "decimals": 0, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "none" + }, + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "succeeded" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "semi-dark-green", + "mode": "fixed" + } + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "failed" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "semi-dark-red", + "mode": "fixed" + } + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "cancelled" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "semi-dark-orange", + "mode": "fixed" + } + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "skipped" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "semi-dark-blue", + "mode": "fixed" + } + } + ] + } + ] + }, + "gridPos": { + "h": 7, + "w": 6, + "x": 12, + "y": 68 + }, + "id": 80, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": "Prometheus", + "editorMode": "code", + "expr": "sum(round(increase(action_restore_ended_overall[1m:10s]))) by (state)", + "hide": false, + "legendFormat": "__auto", + "range": true, + "refId": "A" + } + ], + "title": "Finished Restores", + "transformations": [], + "type": "timeseries" + }, + { + "datasource": "Prometheus", + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "axisSoftMax": 0, + "axisSoftMin": 0, + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "decimals": 0, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "none" + }, + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "succeeded" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "semi-dark-green", + "mode": "fixed" + } + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "failed" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "semi-dark-red", + "mode": "fixed" + } + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "cancelled" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "semi-dark-orange", + "mode": "fixed" + } + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "skipped" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "semi-dark-blue", + "mode": "fixed" + } + } + ] + } + ] + }, + "gridPos": { + "h": 7, + "w": 6, + "x": 18, + "y": 68 + }, + "id": 78, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": "Prometheus", + "editorMode": "code", + "expr": "sum(round(increase(action_restore_cluster_ended_overall[1m:10s]))) by (state)", + "hide": false, + "legendFormat": "__auto", + "range": true, + "refId": "A" + } + ], + "title": "Finished Cluster Restores", + "transformations": [], + "type": "timeseries" + }, + { + "datasource": "Prometheus", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "s" + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 24, + "x": 0, + "y": 75 + }, + "id": 63, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": "Prometheus", + "editorMode": "code", + "expr": "sum(rate(limiter_request_seconds_sum{stage=\"hold\"}[5m])) by (operation) / sum(rate(limiter_request_seconds_count{stage=\"hold\"}[5m])) by (operation) ", + "legendFormat": "__auto", + "range": true, + "refId": "A" + } + ], + "title": "Rate Limiter - avg operation duration", + "type": "timeseries" + }, + { + "datasource": "Prometheus", + "fieldConfig": { + "defaults": { + "color": { + "fixedColor": "red", + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "Limit" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "dark-red", + "mode": "fixed" + } + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "inflight" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "green", + "mode": "fixed" + } + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "pending" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "yellow", + "mode": "fixed" + } + } + ] + } + ] + }, + "gridPos": { + "h": 7, + "w": 4.8, + "x": 0, + "y": 82 + }, + "id": 51, + "maxPerRow": 6, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "repeat": "operation", + "repeatDirection": "h", + "targets": [ + { + "datasource": "Prometheus", + "editorMode": "code", + "expr": "limiter_inflight_count{operation=\"$operation\"}", + "legendFormat": "Inflight", + "range": true, + "refId": "A" + }, + { + "datasource": "Prometheus", + "editorMode": "code", + "expr": "limiter_pending_count{operation=\"$operation\"}", + "hide": false, + "legendFormat": "Pending", + "range": true, + "refId": "B" + }, + { + "datasource": "Prometheus", + "editorMode": "code", + "expr": "limiter_inflight_limit_value{operation=\"$operation\"}", + "hide": false, + "legendFormat": "Limit", + "range": true, + "refId": "C" + } + ], + "title": "Rate Limiter - $operation", + "type": "timeseries" + } + ], + "targets": [ + { + "datasource": "Prometheus", + "refId": "A" + } + ], + "title": "Execution Control", + "type": "row" + }, + { + "collapsed": true, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 54 + }, + "id": 84, + "panels": [ + { + "datasource": "Prometheus", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": true, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "percentunit" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 55 + }, + "id": 86, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": "Prometheus", + "editorMode": "code", + "expr": "sum(increase(action_export_transferred_bytes[5m:30s]))/sum((increase(action_export_processed_bytes[5m:30s])>0))", + "legendFormat": "Transferred/Processed across all actions", + "range": true, + "refId": "A" + } + ], + "title": "Transferred/Processed Ratio", + "type": "timeseries" + }, + { + "datasource": "Prometheus", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": true, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "percentunit" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 55 + }, + "id": 88, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": "Prometheus", + "editorMode": "code", + "expr": "(increase(action_export_transferred_bytes[5m:30s])/(increase(action_export_processed_bytes[5m:30s])>0))", + "legendFormat": "{{policy}}:{{app}}", + "range": true, + "refId": "A" + } + ], + "title": "Transferred/Processed Ratio per policy:app", + "type": "timeseries" + }, + { + "datasource": "Prometheus", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": true, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [ ], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "bytes" + }, + "overrides": [ ] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 63 + }, + "id": 89, + "options": { + "legend": { + "calcs": [ ], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": "Prometheus", + "editorMode": "code", + "expr": "increase(action_export_transferred_bytes[5m:30s]) > 0", + "legendFormat": "{{policy}}:{{app}}", + "range": true, + "refId": "A" + } + ], + "title": "Transferred bytes per policy:app", + "type": "timeseries" + }, + { + "datasource": "Prometheus", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": true, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [ ], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "bytes" + }, + "overrides": [ ] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 63 + }, + "id": 90, + "options": { + "legend": { + "calcs": [ ], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": "Prometheus", + "editorMode": "code", + "expr": "increase(action_export_processed_bytes[5m:30s]) > 0", + "legendFormat": "{{policy}}:{{app}}", + "range": true, + "refId": "A" + } + ], + "title": "Processed bytes per policy:app", + "type": "timeseries" + } + ], + "title": "Data reduction", + "type": "row" + }, + { + "collapsed": true, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 55 + }, + "id": 1013, + "panels": [ + { + "datasource": "Prometheus", + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisPlacement": "left", + "barAlignment": 0, + "drawStyle": "points", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "stepAfter", + "lineWidth": 1, + "pointSize": 4, + "scaleDistribution": { + "log": 2, + "type": "log" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "s", + "unitScale": true + }, + "overrides": [ + { + "matcher": { + "id": "byRegexp", + "options": "/#.*/" + }, + "properties": [ + { + "id": "unit", + "value": "none" + }, + { + "id": "custom.axisPlacement", + "value": "right" + }, + { + "id": "decimals", + "value": 0 + }, + { + "id": "custom.scaleDistribution", + "value": { + "type": "linear" + } + }, + { + "id": "custom.drawStyle", + "value": "line" + }, + { + "id": "custom.lineInterpolation", + "value": "stepAfter" + }, + { + "id": "custom.showPoints", + "value": "never" + }, + { + "id": "custom.axisSoftMin", + "value": 0 + }, + { + "id": "custom.axisLabel", + "value": "# volumes" + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "#Volumes" + }, + "properties": [ + { + "id": "displayName", + "value": "# Volumes Under Transfer" + }, + { + "id": "custom.lineStyle", + "value": { + "fill": "solid" + } + }, + { + "id": "custom.lineWidth", + "value": 0.4 + }, + { + "id": "custom.lineInterpolation", + "value": "stepAfter" + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "#UploadSessionVolumes" + }, + "properties": [ + { + "id": "displayName", + "value": "# VBR Session Volumes" + }, + { + "id": "custom.lineWidth", + "value": 0 + }, + { + "id": "custom.fillOpacity", + "value": 25 + }, + { + "id": "color", + "value": { + "fixedColor": "dark-blue", + "mode": "shades" + } + } + ] + } + ] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 8 + }, + "id": 1006, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": "Prometheus", + "disableTextWrap": false, + "editorMode": "code", + "expr": "sum (max_over_time(data_operation_volume_count{}[2m]))", + "fullMetaSearch": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "#Volumes", + "range": true, + "refId": "VOLUME_COUNT", + "useBackend": false + }, + { + "datasource": "Prometheus", + "disableTextWrap": false, + "editorMode": "code", + "expr": "sum by (repo_type) (max_over_time(data_upload_session_volume_count{repo_type=\"VBR\"}[2m]))", + "fullMetaSearch": false, + "hide": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "#UploadSessionVolumes", + "range": true, + "refId": "VBR_SESSION_COUNT", + "useBackend": false + }, + { + "datasource": "Prometheus", + "editorMode": "code", + "expr": "sum by (data_format,operation,storage_class,repo_name) (rate(data_operation_normalized_duration_sum{}[2m])) / sum by (data_format,operation,storage_class,repo_name) (rate(data_operation_normalized_duration_count{}[2m]))", + "hide": false, + "instant": false, + "legendFormat": "{{operation}} {{storage_class}}/{{repo_name}} ({{data_format}})", + "range": true, + "refId": "NORMALIZED_DURATION_BY_STORAGE_CLASS_LOC" + } + ], + "title": "Normalized operation duration by storage class, location and data format (time/MiB)", + "type": "timeseries" + }, + { + "datasource": "Prometheus", + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "left", + "barAlignment": 0, + "drawStyle": "points", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "stepAfter", + "lineWidth": 1, + "pointSize": 4, + "scaleDistribution": { + "log": 2, + "type": "log" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "s", + "unitScale": true + }, + "overrides": [ + { + "matcher": { + "id": "byRegexp", + "options": "/#.*/" + }, + "properties": [ + { + "id": "unit", + "value": "none" + }, + { + "id": "custom.axisPlacement", + "value": "right" + }, + { + "id": "decimals", + "value": 0 + }, + { + "id": "custom.scaleDistribution", + "value": { + "type": "linear" + } + }, + { + "id": "custom.drawStyle", + "value": "line" + }, + { + "id": "custom.lineInterpolation", + "value": "stepAfter" + }, + { + "id": "custom.showPoints", + "value": "never" + }, + { + "id": "custom.axisSoftMin", + "value": 0 + }, + { + "id": "custom.axisLabel", + "value": "# volumes" + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "#Volumes" + }, + "properties": [ + { + "id": "displayName", + "value": "# Volumes Under Transfer" + }, + { + "id": "custom.lineStyle", + "value": { + "fill": "solid" + } + }, + { + "id": "custom.lineWidth", + "value": 0.4 + }, + { + "id": "custom.lineInterpolation", + "value": "stepAfter" + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "#UploadSessionVolumes" + }, + "properties": [ + { + "id": "displayName", + "value": "# VBR Session Volumes" + }, + { + "id": "custom.lineWidth", + "value": 0 + }, + { + "id": "custom.fillOpacity", + "value": 25 + }, + { + "id": "color", + "value": { + "fixedColor": "dark-blue", + "mode": "shades" + } + } + ] + } + ] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 8 + }, + "id": 1012, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": "Prometheus", + "disableTextWrap": false, + "editorMode": "code", + "expr": "sum (max_over_time(data_operation_volume_count{}[2m]))", + "fullMetaSearch": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "#Volumes", + "range": true, + "refId": "VOLUME_COUNT", + "useBackend": false + }, + { + "datasource": "Prometheus", + "disableTextWrap": false, + "editorMode": "code", + "expr": "sum by (repo_type) (max_over_time(data_upload_session_volume_count{repo_type=\"VBR\"}[2m]))", + "fullMetaSearch": false, + "hide": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "#UploadSessionVolumes", + "range": true, + "refId": "VBR_SESSION_COUNT", + "useBackend": false + }, + { + "datasource": "Prometheus", + "editorMode": "code", + "expr": "sum by (data_format,operation,namespace,pvc_name) (rate(data_operation_duration_sum{}[2m])) / sum by (data_format,operation,namespace,pvc_name) (rate(data_operation_duration_count{}[2m]))", + "hide": false, + "instant": false, + "legendFormat": "{{operation}} {{namespace}}/{{pvc_name}} ({{data_format}})", + "range": true, + "refId": "DURATION_BY_PVC" + } + ], + "title": "Operation duration by pvc and data format", + "type": "timeseries" + }, + { + "datasource": "Prometheus", + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "left", + "barAlignment": 0, + "drawStyle": "points", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "stepAfter", + "lineWidth": 1, + "pointSize": 4, + "scaleDistribution": { + "log": 2, + "type": "log" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "binBps", + "unitScale": true + }, + "overrides": [ + { + "matcher": { + "id": "byRegexp", + "options": "/#.*/" + }, + "properties": [ + { + "id": "unit", + "value": "none" + }, + { + "id": "custom.axisPlacement", + "value": "right" + }, + { + "id": "decimals", + "value": 0 + }, + { + "id": "custom.scaleDistribution", + "value": { + "type": "linear" + } + }, + { + "id": "custom.drawStyle", + "value": "line" + }, + { + "id": "custom.lineInterpolation", + "value": "stepAfter" + }, + { + "id": "custom.showPoints", + "value": "never" + }, + { + "id": "custom.axisSoftMin", + "value": 0 + }, + { + "id": "custom.axisLabel", + "value": "# volumes" + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "#Volumes" + }, + "properties": [ + { + "id": "displayName", + "value": "# Volumes Under Transfer" + }, + { + "id": "custom.lineStyle", + "value": { + "fill": "solid" + } + }, + { + "id": "custom.lineWidth", + "value": 0.4 + }, + { + "id": "custom.lineInterpolation", + "value": "stepAfter" + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "#UploadSessionVolumes" + }, + "properties": [ + { + "id": "displayName", + "value": "# VBR Session Volumes" + }, + { + "id": "custom.lineWidth", + "value": 0 + }, + { + "id": "custom.fillOpacity", + "value": 25 + }, + { + "id": "color", + "value": { + "fixedColor": "dark-blue", + "mode": "shades" + } + } + ] + } + ] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 16 + }, + "id": 1011, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": "Prometheus", + "disableTextWrap": false, + "editorMode": "code", + "expr": "sum (max_over_time(data_operation_volume_count{}[2m]))", + "fullMetaSearch": false, + "hide": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "#Volumes", + "range": true, + "refId": "VOLUME_COUNT", + "useBackend": false + }, + { + "datasource": "Prometheus", + "disableTextWrap": false, + "editorMode": "code", + "expr": "sum by (repo_type) (max_over_time(data_upload_session_volume_count{repo_type=\"VBR\"}[2m]))", + "fullMetaSearch": false, + "hide": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "#UploadSessionVolumes", + "range": true, + "refId": "VBR_SESSION_COUNT", + "useBackend": false + }, + { + "datasource": "Prometheus", + "editorMode": "code", + "expr": "avg by (data_format, operation, storage_class, repo_name) (rate(data_operation_bytes{}[$__rate_interval]))", + "hide": false, + "instant": false, + "legendFormat": "{{operation}} {{storage_class}}/{{repo_name}} ({{data_format}})", + "range": true, + "refId": "RATE_BY_STORAGE_CLASS" + } + ], + "title": "Operation transfer rate by storage class, location and data format", + "type": "timeseries" + }, + { + "datasource": "Prometheus", + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "left", + "barAlignment": 0, + "drawStyle": "points", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "stepAfter", + "lineWidth": 1, + "pointSize": 4, + "scaleDistribution": { + "log": 2, + "type": "log" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "binBps", + "unitScale": true + }, + "overrides": [ + { + "matcher": { + "id": "byRegexp", + "options": "/#.*/" + }, + "properties": [ + { + "id": "unit", + "value": "none" + }, + { + "id": "custom.axisPlacement", + "value": "right" + }, + { + "id": "decimals", + "value": 0 + }, + { + "id": "custom.scaleDistribution", + "value": { + "type": "linear" + } + }, + { + "id": "custom.drawStyle", + "value": "line" + }, + { + "id": "custom.lineInterpolation", + "value": "stepAfter" + }, + { + "id": "custom.showPoints", + "value": "never" + }, + { + "id": "custom.axisSoftMin", + "value": 0 + }, + { + "id": "custom.axisLabel", + "value": "# volumes" + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "#Volumes" + }, + "properties": [ + { + "id": "displayName", + "value": "# Volumes Under Transfer" + }, + { + "id": "custom.lineStyle", + "value": { + "fill": "solid" + } + }, + { + "id": "custom.lineWidth", + "value": 0.4 + }, + { + "id": "custom.lineInterpolation", + "value": "stepAfter" + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "#UploadSessionVolumes" + }, + "properties": [ + { + "id": "displayName", + "value": "# VBR Session Volumes" + }, + { + "id": "custom.lineWidth", + "value": 0 + }, + { + "id": "custom.fillOpacity", + "value": 25 + }, + { + "id": "color", + "value": { + "fixedColor": "dark-blue", + "mode": "shades" + } + } + ] + } + ] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 16 + }, + "id": 1004, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": "Prometheus", + "disableTextWrap": false, + "editorMode": "code", + "expr": "sum (max_over_time(data_operation_volume_count{}[2m]))", + "fullMetaSearch": false, + "hide": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "#Volumes", + "range": true, + "refId": "VOLUME_COUNT", + "useBackend": false + }, + { + "datasource": "Prometheus", + "disableTextWrap": false, + "editorMode": "code", + "expr": "sum by (repo_type) (max_over_time(data_upload_session_volume_count{repo_type=\"VBR\"}[2m]))", + "fullMetaSearch": false, + "hide": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "#UploadSessionVolumes", + "range": true, + "refId": "VBR_SESSION_COUNT", + "useBackend": false + }, + { + "datasource": "Prometheus", + "editorMode": "code", + "expr": "avg by (data_format, operation, namespace, pvc_name) (rate(data_operation_bytes{}[$__rate_interval]))", + "hide": false, + "instant": false, + "legendFormat": "{{operation}} {{namespace}}/{{pvc_name}} ({{data_format}})", + "range": true, + "refId": "RATE_BY_PVC" + } + ], + "title": "Operation transfer rate by pvc and data format", + "type": "timeseries" + } + ], + "title": "Data transfer operations", + "type": "row" + }, + { + "collapsed": true, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 56 + }, + "id": 1016, + "panels": [ + { + "datasource": "Prometheus", + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "text", + "value": null + } + ] + }, + "unit": "bytes" + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 4, + "x": 0, + "y": 57 + }, + "id": 1031, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.1.5", + "targets": [ + { + "datasource": "Prometheus", + "disableTextWrap": false, + "editorMode": "code", + "exemplar": false, + "expr": "repository_data_pruned_bytes > 0", + "format": "time_series", + "fullMetaSearch": false, + "includeNullMetadata": true, + "instant": false, + "interval": "", + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Recently recovered storage", + "transformations": [ + { + "id": "reduce", + "options": { + "includeTimeField": false, + "mode": "reduceFields", + "reducers": [ + "lastNotNull" + ] + } + }, + { + "id": "merge", + "options": {} + }, + { + "id": "reduce", + "options": { + "includeTimeField": false, + "mode": "reduceFields", + "reducers": [ + "sum" + ] + } + } + ], + "type": "stat" + }, + { + "datasource": "Prometheus", + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + }, + "unit": "bytes" + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 8, + "x": 4, + "y": 57 + }, + "id": 1025, + "options": { + "displayMode": "basic", + "maxVizHeight": 300, + "minVizHeight": 16, + "minVizWidth": 8, + "namePlacement": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showUnfilled": true, + "sizing": "auto", + "valueMode": "text" + }, + "pluginVersion": "11.1.5", + "targets": [ + { + "datasource": "Prometheus", + "disableTextWrap": false, + "editorMode": "code", + "exemplar": false, + "expr": "sum by(namespace) (repository_data_pruned_bytes{namespace!=\"kasten-io\"} > 0)", + "format": "time_series", + "fullMetaSearch": false, + "includeNullMetadata": true, + "instant": false, + "interval": "", + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + }, + { + "datasource": "Prometheus", + "editorMode": "code", + "expr": "sum(repository_data_pruned_bytes{namespace=\"kasten-io\"} > 0)", + "hide": false, + "instant": false, + "legendFormat": "metadata", + "range": true, + "refId": "B" + } + ], + "title": "Recently recovered storage per app", + "type": "bargauge" + }, + { + "datasource": "Prometheus", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "text", + "value": null + } + ] + }, + "unit": "bytes" + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 4, + "x": 12, + "y": 57 + }, + "id": 1015, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.1.5", + "targets": [ + { + "datasource": "Prometheus", + "editorMode": "code", + "expr": "sum(repository_data_pruned_bytes_total)", + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A" + } + ], + "title": "Total recovered storage", + "transformations": [ + { + "id": "reduce", + "options": { + "includeTimeField": false, + "mode": "reduceFields", + "reducers": [ + "lastNotNull" + ] + } + } + ], + "type": "stat" + }, + { + "datasource": "Prometheus", + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "bytes" + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 8, + "x": 16, + "y": 57 + }, + "id": 1018, + "options": { + "displayMode": "basic", + "maxVizHeight": 300, + "minVizHeight": 16, + "minVizWidth": 8, + "namePlacement": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showUnfilled": true, + "sizing": "auto", + "valueMode": "text" + }, + "pluginVersion": "11.1.5", + "targets": [ + { + "datasource": "Prometheus", + "editorMode": "code", + "exemplar": false, + "expr": "repository_data_pruned_bytes_total{namespace!=\"kasten-io\"} > 0", + "format": "time_series", + "instant": false, + "interval": "", + "legendFormat": "{{namespace}}", + "range": true, + "refId": "A" + }, + { + "datasource": "Prometheus", + "editorMode": "code", + "expr": "sum(repository_data_pruned_bytes_total{namespace=\"kasten-io\"} > 0)", + "hide": false, + "instant": false, + "legendFormat": "metadata", + "range": true, + "refId": "B" + } + ], + "title": "Total recovered storage per app", + "type": "bargauge" + }, + { + "datasource": "Prometheus", + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "text", + "value": null + } + ] + }, + "unit": "bytes" + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 4, + "x": 0, + "y": 62 + }, + "id": 1017, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.1.5", + "targets": [ + { + "datasource": "Prometheus", + "editorMode": "code", + "expr": "sum(repository_data_scheduled_for_pruning_bytes)", + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A" + } + ], + "title": "Expired data marked for deletion", + "transformations": [ + { + "id": "reduce", + "options": { + "includeTimeField": false, + "mode": "reduceFields", + "reducers": [ + "lastNotNull" + ] + } + } + ], + "type": "stat" + }, + { + "datasource": "Prometheus", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + }, + "unit": "bytes" + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 8, + "x": 4, + "y": 62 + }, + "id": 1019, + "options": { + "displayMode": "basic", + "maxVizHeight": 300, + "minVizHeight": 16, + "minVizWidth": 8, + "namePlacement": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showUnfilled": true, + "sizing": "auto", + "valueMode": "text" + }, + "pluginVersion": "11.1.5", + "targets": [ + { + "datasource": "Prometheus", + "editorMode": "code", + "expr": "repository_data_scheduled_for_pruning_bytes{namespace!=\"kasten-io\"} > 0", + "instant": false, + "legendFormat": "{{namespace}}", + "range": true, + "refId": "A" + }, + { + "datasource": "Prometheus", + "editorMode": "code", + "expr": "sum(repository_data_scheduled_for_pruning_bytes{namespace=\"kasten-io\"} > 0)", + "hide": false, + "instant": false, + "legendFormat": "metadata", + "range": true, + "refId": "B" + } + ], + "title": "Expired data marked for deletion per app", + "type": "bargauge" + } + ], + "title": "Backup Export Maintenance", + "type": "row" + } + ], + "schemaVersion": 39, + "style": "dark", + "tags": [], + "templating": { + "list": [ + { + "hide": 2, + "label": "Cluster", + "name": "cluster", + "query": "", + "skipUrlSync": false, + "type": "constant" + }, + { + "current": { + "selected": false, + "text": "All", + "value": "$__all" + }, + "datasource": "Prometheus", + "definition": "limiter_pending_count", + "description": "", + "hide": 2, + "includeAll": true, + "label": "operation", + "multi": false, + "name": "operation", + "options": [], + "query": { + "query": "limiter_pending_count", + "refId": "StandardVariableQuery" + }, + "refresh": 1, + "regex": "/operation=\\\"([\\w]+)\\\"/", + "skipUrlSync": false, + "sort": 0, + "type": "query" + } + ] + }, + "time": { + "from": "now-24h", + "to": "now" + }, + "timepicker": {}, + "timezone": "", + "title": "K10 Dashboard", + "uid": "8Ebb3xS7k", + "version": 3 +} \ No newline at end of file diff --git a/charts/kasten/k10/7.5.401/license b/charts/kasten/k10/7.5.401/license new file mode 100644 index 0000000000..fb23dbb826 --- /dev/null +++ b/charts/kasten/k10/7.5.401/license @@ -0,0 +1 @@ +Y3VzdG9tZXJOYW1lOiBzdGFydGVyLWxpY2Vuc2UKZGF0ZUVuZDogJzIxMDAtMDEtMDFUMDA6MDA6MDAuMDAwWicKZGF0ZVN0YXJ0OiAnMjAyMC0wMS0wMVQwMDowMDowMC4wMDBaJwpmZWF0dXJlczogbnVsbAppZDogc3RhcnRlci00ZjE4NDJjMC0wNzQ1LTQxYTUtYWFhNy1hMDFkNzQ4YjFjMzAKcHJvZHVjdDogSzEwCnJlc3RyaWN0aW9uczoKICBub2RlczogJzEwJwpzZXJ2aWNlQWNjb3VudEtleTogbnVsbAp2ZXJzaW9uOiB2MS4wLjAKc2lnbmF0dXJlOiBqT1N5NDNQZG5ZMFVCZitValhOdU1oUEFSb1J2ZkpzWElQWnhBWFNCaGpKbUwxNlNodi8vVzgyV2NMeGZJM25NZTA0TThtRU03eThPcnArQks1ekxpeFd3clpncmZSbTBEaWlELyttRjR5U3l1Rko0QW1neHV6NDhQTmdnU1VyWUM3S1FVcFYxSEJZV1ZaNm9udEJDeE1rVWtkaDVqdzZJdWMzN3lDaktIYy92bWZaenBzTVhybmxUdGhha2RjVVk0azNyVHJDa3VDcnFUMkpjM1o1amFGalZSZW1Zd1NBVXpkRldNazdQdkp3eHVFdE5rNitPV0pCVERQbnNYdldKdjdNc3NneDBJTmdtNUlJWDRVeEVhQWI4QXpTNkMyQ21XQzlhWURFTDg1aEFpeWhONXUwU0tQczA3ZXB0R1VHYmc3cWtPUVN0d0NhcDFKUURvbDVDT0E9PQo= diff --git a/charts/kasten/k10/7.5.401/questions.yaml b/charts/kasten/k10/7.5.401/questions.yaml new file mode 100644 index 0000000000..713fcb1162 --- /dev/null +++ b/charts/kasten/k10/7.5.401/questions.yaml @@ -0,0 +1,295 @@ +questions: +# ======================== +# SECRETS And Configuration +# ======================== + +### AWS Configuration + +- variable: secrets.awsAccessKeyId + description: "AWS access key ID (required for AWS deployment)" + type: password + label: AWS Access Key ID + required: false + group: "AWS Configuration" + +- variable: secrets.awsSecretAccessKey + description: "AWS access key secret (required for AWS deployment)" + type: password + label: AWS Secret Access Key + required: false + group: "AWS Configuration" + +- variable: secrets.awsIamRole + description: "ARN of the AWS IAM role assumed by K10 to perform any AWS operation." + type: string + label: ARN of the AWS IAM role + required: false + group: "AWS Configuration" + +- variable: awsConfig.assumeRoleDuration + description: "Duration of a session token generated by AWS for an IAM role" + type: string + label: Role Duration + required: false + default: "" + group: "AWS Configuration" + +- variable: awsConfig.efsBackupVaultName + description: "Specifies the AWS EFS backup vault name" + type: string + label: EFS Backup Vault Name + required: false + default: "k10vault" + group: "AWS Configuration" + +### Google Cloud Configuration + +- variable: secrets.googleApiKey + description: "Required If cluster is deployed on Google Cloud" + type: multiline + label: Non-default base64 encoded GCP Service Account key file + required: false + group: "GoogleApi Configuration" + +### Azure Configuration + +- variable: secrets.azureTenantId + description: "Azure tenant ID (required for Azure deployment)" + type: string + label: Tenant ID + required: false + group: "Azure Configuration" + +- variable: secrets.azureClientId + description: "Azure Service App ID" + type: password + label: Service App ID + required: false + group: "Azure Configuration" + +- variable: secrets.azureClientSecret + description: "Azure Service App secret" + type: password + label: Service App secret + required: false + group: "Azure Configuration" + +- variable: secrets.azureResourceGroup + description: "Resource Group name that was created for the Kubernetes cluster" + type: string + label: Resource Group + required: false + group: "Azure Configuration" + +- variable: secrets.azureSubscriptionID + description: "Subscription ID in your Azure tenant" + type: string + label: Subscription ID + required: false + group: "Azure Configuration" + +- variable: secrets.azureResourceMgrEndpoint + description: "Resource management endpoint for the Azure Stack instance" + type: string + label: Resource management endpoint + required: false + group: "Azure Configuration" + +- variable: secrets.azureADEndpoint + description: "Azure Active Directory login endpoint" + type: string + label: Active Directory login endpoint + required: false + group: "Azure Configuration" + +- variable: secrets.azureADResourceID + description: "Azure Active Directory resource ID to obtain AD tokens" + type: string + label: Active Directory resource ID + required: false + group: "Azure Configuration" + +# ======================== +# Authentication +# ======================== + +- variable: auth.basicAuth.enabled + description: "Configures basic authentication for the K10 dashboard" + type: boolean + label: Enable Basic Authentication + required: false + group: "Authentication" + show_subquestion_if: true + subquestions: + - variable: auth.basicAuth.htpasswd + description: "A username and password pair separated by a colon character" + type: password + label: Authentication Details (htpasswd) + - variable: auth.basicAuth.secretName + description: "Name of an existing Secret that contains a file generated with htpasswd" + type: string + label: Secret Name + +- variable: auth.tokenAuth.enabled + description: "Configures token based authentication for the K10 dashboard" + type: boolean + label: Enable Token Based Authentication + required: false + group: "Authentication" + +- variable: auth.oidcAuth.enabled + description: "Configures Open ID Connect based authentication for the K10 dashboard" + type: boolean + label: Enable OpenID Connect Based Authentication + required: false + group: "Authentication" + show_subquestion_if: true + subquestions: + - variable: auth.oidcAuth.providerURL + description: "URL for the OIDC Provider" + type: string + label: OIDC Provider URL + - variable: auth.oidcAuth.redirectURL + description: "URL for the K10 gateway Provider" + type: string + label: OIDC Redirect URL + - variable: auth.oidcAuth.scopes + description: "Space separated OIDC scopes required for userinfo. Example: `profile email`" + type: string + label: OIDC scopes + - variable: auth.oidcAuth.prompt + description: "The type of prompt to be used during authentication (none, consent, login, or select_account)" + type: enum + options: + - none + - consent + - login + - select_account + default: none + label: The type of prompt to be used during authentication (none, consent, login, or select_account) + - variable: auth.oidcAuth.clientID + description: "Client ID given by the OIDC provider for K10" + type: password + label: OIDC Client ID + - variable: auth.oidcAuth.clientSecret + description: "Client secret given by the OIDC provider for K10" + type: password + label: OIDC Client Secret + - variable: auth.oidcAuth.usernameClaim + description: "The claim to be used as the username" + type: string + label: OIDC UserName Claim + - variable: auth.oidcAuth.usernamePrefix + description: "Prefix that has to be used with the username obtained from the username claim" + type: string + label: OIDC UserName Prefix + - variable: auth.oidcAuth.groupClaim + description: "Name of a custom OpenID Connect claim for specifying user groups" + type: string + label: OIDC group Claim + - variable: auth.oidcAuth.groupPrefix + description: "All groups will be prefixed with this value to prevent conflicts" + type: string + label: OIDC group Prefix + +# ======================== +# External Gateway +# ======================== + +- variable: externalGateway.create + description: "Configures an external gateway for K10 API services" + type: boolean + label: Create External Gateway + required: false + group: "External Gateway" + show_subquestion_if: true + subquestions: + - variable: externalGateway.annotations + description: "Standard annotations for the services" + type: multiline + default: "" + label: Annotation + - variable: externalGateway.fqdn.name + description: "Domain name for the K10 API services" + type: string + label: Domain Name + - variable: externalGateway.fqdn.type + description: "Supported gateway type: `route53-mapper` or `external-dns`" + type: string + label: Gateway Type route53-mapper or external-dns + - variable: externalGateway.awsSSLCertARN + description: "ARN for the AWS ACM SSL certificate used in the K10 API server" + type: multiline + label: ARN for the AWS ACM SSL certificate + +# ======================== +# Storage Management +# ======================== + +- variable: global.persistence.storageClass + label: StorageClass Name + description: "Specifies StorageClass Name to be used for PVCs" + type: string + required: false + default: "" + group: "Storage Management" + +- variable: prometheus.server.persistentVolume.storageClass + type: string + label: StorageClass Name for Prometheus PVC + description: "StorageClassName used to create Prometheus PVC. Setting this option overwrites global StorageClass value" + default: "" + required: false + group: "Storage Management" + +- variable: prometheus.server.persistentVolume.enabled + type: boolean + label: Enable PVC for Prometheus server + description: "If true, K10 Prometheus server will create a Persistent Volume Claim" + default: true + required: false + group: "Storage Management" + +- variable: global.persistence.enabled + type: boolean + label: Storage Enabled + description: "If true, K10 will use Persistent Volume Claim" + default: true + required: false + group: "Storage Management" + +# ======================== +# Service Account +# ======================== + +- variable: serviceAccount.name + description: "Name of a service account in the target namespace that has cluster-admin permissions. This is needed for the K10 to be able to protect cluster resources." + type: string + label: Service Account Name + required: false + group: "Service Account" + +# ======================== +# License +# ======================== + +- variable: license + description: "License string obtained from Kasten" + type: multiline + label: License String + group: "License" +- variable: eula.accept + description: "Whether to enable accept EULA before installation" + type: boolean + label: Enable accept EULA before installation + group: "License" + show_subquestion_if: true + subquestions: + - variable: eula.company + description: "Company name. Required field if EULA is accepted" + type: string + label: Company Name + - variable: eula.email + description: "Contact email. Required field if EULA is accepted" + type: string + label: Contact Email diff --git a/charts/kasten/k10/7.5.401/templates/NOTES.txt b/charts/kasten/k10/7.5.401/templates/NOTES.txt new file mode 100644 index 0000000000..9d36f1f12d --- /dev/null +++ b/charts/kasten/k10/7.5.401/templates/NOTES.txt @@ -0,0 +1,215 @@ +Thank you for installing Kasten’s K10 Data Management Platform {{ .Chart.Version }}! +{{- if .Values.fips.enabled }} + +You are operating in FIPS mode. +{{- end }} + +Documentation can be found at https://docs.kasten.io/. + +How to access the K10 Dashboard: + +{{- if .Values.ingress.create }} + +You are using the system's default ingress controller. Please ask your +administrator for instructions on how to access the cluster. + +WebUI location: https://{{ default "Your ingress endpoint" .Values.ingress.host }}/{{ default .Release.Name .Values.ingress.urlPath }} + +In addition, +{{- end }} + +{{- if .Values.route.enabled }} +WebUI location: https://{{ default "k10-route endpoint" .Values.route.host}}/{{ default .Release.Name .Values.route.path }}/ + +In addition, +{{- end }} + +{{- if .Values.externalGateway.create }} +{{- if .Values.externalGateway.fqdn.name }} + +The K10 Dashboard is accessible via {{ if or .Values.secrets.tlsSecret .Values.externalGateway.awsSSLCertARN }}https{{ else }}http{{ end }}://{{ .Values.externalGateway.fqdn.name }}/{{ .Release.Name }}/#/ + +In addition, +{{- else }} + +The K10 Dashboard is accessible via a LoadBalancer. Find the service's EXTERNAL IP using: + `kubectl get svc gateway-ext --namespace {{ .Release.Namespace }} -o wide` +And use it in following URL + `http://SERVICE_EXTERNAL_IP/{{ .Release.Name }}/#/` + +In addition, +{{- end }} +{{- end }} + +To establish a connection to it use the following `kubectl` command: + +`kubectl --namespace {{ .Release.Namespace }} port-forward service/gateway 8080:{{ .Values.gateway.service.externalPort }}` + +The Kasten dashboard will be available at: `http{{ if or .Values.secrets.tlsSecret .Values.externalGateway.awsSSLCertARN }}s{{ end }}://127.0.0.1:8080/{{ .Release.Name }}/#/` +{{ if and ( .Values.metering.awsManagedLicense ) ( not .Values.metering.licenseConfigSecretName ) }} + +IAM Role created during installation need to have permissions that allow K10 to +perform operations on EBS and, if needed, EFS and S3. Please create a policy +with required permissions, and use the commands below to attach the policy to +the service account. + +`ROLE_NAME=$(kubectl get serviceaccount {{ .Values.serviceAccount.name }} -n {{ .Release.Namespace }} -ojsonpath="{.metadata.annotations['eks\.amazonaws\.com/role-arn']}" | awk -F '/' '{ print $(NF) }')` +`aws iam attach-role-policy --role-name "${ROLE_NAME}" --policy-arn ` + +Refer to `https://docs.kasten.io/latest/install/aws-containers-anywhere/aws-containers-anywhere.html#attaching-permissions-for-eks-installations` +for more information. + +{{ end }} + +{{- if .Values.restore }} +{{- if or (empty .Values.restore.copyImagePullSecrets) (.Values.restore.copyImagePullSecrets) }} +-------------------- +Removal warning: The helm field `restore.copyImagePullSecrets` has been removed in version 6.0.12. K10 no longer copies the `imagePullSecret` to the application namespace. +-------------------- +{{- end }} +{{- end }} + +{{- if or (not (empty .Values.garbagecollector.importRunActions)) (not (empty .Values.garbagecollector.backupRunActions)) (not (empty .Values.garbagecollector.retireActions)) }} +Deprecation warning: The `garbagecollector.importRunActions`, `garbagecollector.backupRunActions`, `garbagecollector.retireActions` +blocks within the helm chart values have been replaced with `garbagecollector.actions`. +{{- end }} + +{{- if .Values.secrets.azureADEndpoint }} +-------------------- +Deprecation warning: The helm field `secret.azureADEndpoint` is deprecated and will be removed in upcoming release, we recommend you to use correct respective field, i.e., `secrets.microsoftEntraIDEndpoint`. +-------------------- +{{- end }} + + +{{- if .Values.secrets.azureADResourceID }} +-------------------- +Deprecation warning: The helm field `secret.azureADResourceID` is deprecated and will be removed in upcoming release, we recommend you to use correct respective field, i.e., `secrets.microsoftEntraIDResourceID` +-------------------- +{{- end }} + +{{- if or .Values.kanisterPodCustomLabels .Values.kanisterPodCustomAnnotations }} +-------------------- +Deprecation warning: The Helm values `kanisterPodCustomLabels` and `kanisterPodCustomAnnotations` are deprecated and will be removed in an upcoming release. Please use `global.podLabels` and `global.podAnnotations` to set labels and annotations to all the Kasten pods globally. +-------------------- +{{- end }} + +{{- if gt (int .Values.executorReplicas) 0 }} +-------------------- +Deprecation warning: The Helm value 'executorReplicas' is deprecated and will be removed in an upcoming release. Please use 'limiter.executorReplicas' instead. +-------------------- +{{- end }} +{{- if .Values.injectKanisterSidecar.enabled }} +-------------------- +Deprecation warning: The Helm value 'injectKanisterSidecar.enabled' is deprecated and will be removed in an upcoming release. Please use 'injectGenericVolumeBackupSidecar.enabled' instead. +-------------------- +{{- end }} +{{- if .Values.kanisterPodMetricSidecar.enabled }} +-------------------- +Deprecation warning: The Helm value 'kanisterPodMetricSidecar.enabled' is deprecated and will be removed in an upcoming release. Please use 'workerPodMetricSidecar.enabled' instead. +-------------------- +{{- end }} +{{- if gt (int .Values.services.executor.workerCount) 0 }} +-------------------- +Deprecation warning: The Helm value 'services.executor.workerCount' is deprecated and will be removed in an upcoming release. Please use 'limiter.executorThreads' instead. +-------------------- +{{- end }} +{{- if gt (int .Values.services.executor.maxConcurrentRestoreCsiSnapshots) 0 }} +-------------------- +Deprecation warning: The Helm value 'services.executor.maxConcurrentRestoreCsiSnapshots' is deprecated and will be removed in an upcoming release. Please use 'limiter.csiSnapshotRestoresPerAction' instead. +-------------------- +{{- end }} +{{- if gt (int .Values.services.executor.maxConcurrentRestoreGenericVolumeSnapshots) 0 }} +-------------------- +Deprecation warning: The Helm value 'services.executor.maxConcurrentRestoreGenericVolumeSnapshots' is deprecated and will be removed in an upcoming release. Please use 'limiter.volumeRestoresPerAction' instead. +-------------------- +{{- end }} +{{- if gt (int .Values.services.executor.maxConcurrentRestoreWorkloads) 0 }} +-------------------- +Deprecation warning: The Helm value 'services.executor.maxConcurrentRestoreWorkloads' is deprecated and will be removed in an upcoming release. Please use 'limiter.workloadRestoresPerAction' instead. +-------------------- +{{- end }} +{{- if gt (int .Values.kanister.backupTimeout) 0 }} +-------------------- +Deprecation warning: The Helm value 'kanister.backupTimeout' is deprecated and will be removed in an upcoming release. Please use 'timeout.blueprintBackup' instead. +-------------------- +{{- end }} +{{- if gt (int .Values.kanister.restoreTimeout) 0 }} +-------------------- +Deprecation warning: The Helm value 'kanister.restoreTimeout' is deprecated and will be removed in an upcoming release. Please use 'timeout.blueprintRestore' instead. +-------------------- +{{- end }} +{{- if gt (int .Values.kanister.deleteTimeout) 0 }} +-------------------- +Deprecation warning: The Helm value 'kanister.deleteTimeout' is deprecated and will be removed in an upcoming release. Please use 'timeout.blueprintDelete' instead. +-------------------- +{{- end }} +{{- if gt (int .Values.kanister.hookTimeout) 0 }} +-------------------- +Deprecation warning: The Helm value 'kanister.hookTimeout' is deprecated and will be removed in an upcoming release. Please use 'timeout.blueprintHooks' instead. +-------------------- +{{- end }} +{{- if gt (int .Values.kanister.checkRepoTimeout) 0 }} +-------------------- +Deprecation warning: The Helm value 'kanister.checkRepoTimeout' is deprecated and will be removed in an upcoming release. Please use 'timeout.checkRepoPodReady' instead. +-------------------- +{{- end }} +{{- if gt (int .Values.kanister.statsTimeout) 0 }} +-------------------- +Deprecation warning: The Helm value 'kanister.statsTimeout' is deprecated and will be removed in an upcoming release. Please use 'timeout.statsPodReady' instead. +-------------------- +{{- end }} +{{- if gt (int .Values.kanister.efsPostRestoreTimeout) 0 }} +-------------------- +Deprecation warning: The Helm value 'kanister.efsPostRestoreTimeout' is deprecated and will be removed in an upcoming release. Please use 'timeout.efsRestorePodReady' instead. +-------------------- +{{- end }} +{{- if gt (int .Values.kanister.podReadyWaitTimeout) 0 }} +-------------------- +Deprecation warning: The Helm value 'kanister.podReadyWaitTimeout' is deprecated and will be removed in an upcoming release. Please use 'timeout.workerPodReady' instead. +-------------------- +{{- end }} +{{- if gt (int .Values.limiter.concurrentSnapConversions) 0 }} +-------------------- +Deprecation warning: The Helm value 'limiter.concurrentSnapConversions' is deprecated and will be removed in an upcoming release. Please use 'limiter.snapshotExportsPerAction' instead. +-------------------- +{{- end }} +{{- if gt (int .Values.limiter.genericVolumeSnapshots) 0 }} +-------------------- +Deprecation warning: The Helm value 'limiter.genericVolumeSnapshots' is deprecated and will be removed in an upcoming release. Please use 'limiter.genericVolumeBackupsPerCluster' instead. +-------------------- +{{- end }} +{{- if gt (int .Values.limiter.genericVolumeCopies) 0 }} +-------------------- +Deprecation warning: The Helm value 'limiter.genericVolumeCopies' is deprecated and will be removed in an upcoming release. Please use 'limiter.snapshotExportsPerCluster' instead. +-------------------- +{{- end }} +{{- if gt (int .Values.limiter.genericVolumeRestores) 0 }} +-------------------- +Deprecation warning: The Helm value 'limiter.genericVolumeRestores' is deprecated and will be removed in an upcoming release. Please use 'limiter.volumeRestoresPerCluster' instead. +-------------------- +{{- end }} +{{- if gt (int .Values.limiter.csiSnapshots) 0 }} +-------------------- +Deprecation warning: The Helm value 'limiter.csiSnapshots' is deprecated and will be removed in an upcoming release. Please use 'limiter.csiSnapshotsPerCluster' instead. +-------------------- +{{- end }} +{{- if gt (int .Values.limiter.providerSnapshots) 0 }} +-------------------- +Deprecation warning: The Helm value 'limiter.providerSnapshots' is deprecated and will be removed in an upcoming release. Please use 'limiter.directSnapshotsPerCluster' instead. +-------------------- +{{- end }} +{{- if gt (int .Values.limiter.imageCopies) 0 }} +-------------------- +Deprecation warning: The Helm value 'limiter.imageCopies' is deprecated and will be removed in an upcoming release. Please use 'limiter.imageCopiesPerCluster' instead. +-------------------- +{{- end }} +{{- if and .Values.maxJobWaitDuration (not (empty .Values.maxJobWaitDuration)) }} +-------------------- +Deprecation warning: The Helm value 'maxJobWaitDuration' is deprecated and will be removed in an upcoming release. Please use 'timeout.jobWait' instead. +-------------------- +{{- end }} +{{- if .Values.forceRootInKanisterHooks }} +-------------------- +Deprecation warning: The Helm value 'forceRootInKanisterHooks' is deprecated and will be removed in an upcoming release. Please use 'forceRootInBlueprintActions' instead. +-------------------- +{{- end }} \ No newline at end of file diff --git a/charts/kasten/k10/7.5.401/templates/_definitions.tpl b/charts/kasten/k10/7.5.401/templates/_definitions.tpl new file mode 100644 index 0000000000..b5d7b79a8f --- /dev/null +++ b/charts/kasten/k10/7.5.401/templates/_definitions.tpl @@ -0,0 +1,259 @@ +{{/* Code generated automatically. DO NOT EDIT. */}} +{{/* K10 services can be disabled by customers via helm value based feature flags. +Therefore, fetching of a list or yaml with service names should be done with the get.enabled* helper functions. +For example, the k10.restServices list can be fetched with get.enabledRestServices */}} +{{- define "k10.additionalServices" -}}frontend kanister{{- end -}} +{{- define "k10.restServices" -}}auth bloblifecyclemanager catalog controllermanager crypto dashboardbff events executor garbagecollector jobs logging metering repositories state vbrintegrationapi{{- end -}} +{{- define "k10.services" -}}aggregatedapis gateway{{- end -}} +{{- define "k10.exposedServices" -}}auth dashboardbff vbrintegrationapi{{- end -}} +{{- define "k10.statelessServices" -}}aggregatedapis auth bloblifecyclemanager controllermanager crypto dashboardbff events executor garbagecollector repositories gateway state vbrintegrationapi{{- end -}} +{{- define "k10.colocatedServices" -}} +bloblifecyclemanager: + port: 8001 + primary: crypto +events: + port: 8001 + primary: state +garbagecollector: + port: 8002 + primary: crypto +repositories: + port: 8003 + primary: crypto +vbrintegrationapi: + port: 8001 + primary: dashboardbff +{{- end -}} +{{- define "k10.colocatedServiceLookup" -}} +crypto: +- bloblifecyclemanager +- garbagecollector +- repositories +dashboardbff: +- vbrintegrationapi +state: +- events +{{- end -}} +{{- define "k10.aggregatedAPIs" -}}actions apps repositories vault dr{{- end -}} +{{- define "k10.configAPIs" -}}config{{- end -}} +{{- define "k10.profiles" -}}profiles{{- end -}} +{{- define "k10.policies" -}}policies{{- end -}} +{{- define "k10.policypresets" -}}policypresets{{- end -}} +{{- define "k10.transformsets" -}}transformsets{{- end -}} +{{- define "k10.blueprintbindings" -}}blueprintbindings{{- end -}} +{{- define "k10.auditconfigs" -}}auditconfigs{{- end -}} +{{- define "k10.storagesecuritycontexts" -}}storagesecuritycontexts{{- end -}} +{{- define "k10.storagesecuritycontextbindings" -}}storagesecuritycontextbindings{{- end -}} +{{- define "k10.reportingAPIs" -}}reporting{{- end -}} +{{- define "k10.distAPIs" -}}dist{{- end -}} +{{- define "k10.actionsAPIs" -}}actions{{- end -}} +{{- define "k10.backupActions" -}}backupactions{{- end -}} +{{- define "k10.backupActionsDetails" -}}backupactions/details{{- end -}} +{{- define "k10.reportActions" -}}reportactions{{- end -}} +{{- define "k10.reportActionsDetails" -}}reportactions/details{{- end -}} +{{- define "k10.storageRepositories" -}}storagerepositories{{- end -}} +{{- define "k10.restoreActions" -}}restoreactions{{- end -}} +{{- define "k10.restoreActionsDetails" -}}restoreactions/details{{- end -}} +{{- define "k10.importActions" -}}importactions{{- end -}} +{{- define "k10.importActionsDetails" -}}importactions/details{{- end -}} +{{- define "k10.exportActions" -}}exportactions{{- end -}} +{{- define "k10.exportActionsDetails" -}}exportactions/details{{- end -}} +{{- define "k10.retireActions" -}}retireactions{{- end -}} +{{- define "k10.runActions" -}}runactions{{- end -}} +{{- define "k10.runActionsDetails" -}}runactions/details{{- end -}} +{{- define "k10.backupClusterActions" -}}backupclusteractions{{- end -}} +{{- define "k10.backupClusterActionsDetails" -}}backupclusteractions/details{{- end -}} +{{- define "k10.restoreClusterActions" -}}restoreclusteractions{{- end -}} +{{- define "k10.restoreClusterActionsDetails" -}}restoreclusteractions/details{{- end -}} +{{- define "k10.cancelActions" -}}cancelactions{{- end -}} +{{- define "k10.upgradeActions" -}}upgradeactions{{- end -}} +{{- define "k10.appsAPIs" -}}apps{{- end -}} +{{- define "k10.restorePoints" -}}restorepoints{{- end -}} +{{- define "k10.restorePointsDetails" -}}restorepoints/details{{- end -}} +{{- define "k10.clusterRestorePoints" -}}clusterrestorepoints{{- end -}} +{{- define "k10.clusterRestorePointsDetails" -}}clusterrestorepoints/details{{- end -}} +{{- define "k10.applications" -}}applications{{- end -}} +{{- define "k10.applicationsDetails" -}}applications/details{{- end -}} +{{- define "k10.vaultAPIs" -}}vault{{- end -}} +{{- define "k10.passkey" -}}passkeys{{- end -}} +{{- define "k10.authAPIs" -}}auth{{- end -}} +{{- define "k10.defaultK10LimiterSnapshotExportsPerAction" -}}3{{- end -}} +{{- define "k10.defaultK10LimiterWorkloadSnapshotsPerAction" -}}5{{- end -}} +{{- define "k10.defaultK10DataStoreParallelUpload" -}}8{{- end -}} +{{- define "k10.defaultK10DataStoreGeneralContentCacheSizeMB" -}}0{{- end -}} +{{- define "k10.defaultK10DataStoreGeneralMetadataCacheSizeMB" -}}500{{- end -}} +{{- define "k10.defaultK10DataStoreTotalCacheSizeLimitMB" -}}1500{{- end -}} +{{- define "k10.defaultK10DataStoreRestoreContentCacheSizeMB" -}}500{{- end -}} +{{- define "k10.defaultK10DataStoreRestoreMetadataCacheSizeMB" -}}500{{- end -}} +{{- define "k10.defaultK10BackupBufferFileHeadroomFactor" -}}1.1{{- end -}} +{{- define "k10.defaultK10LimiterGenericVolumeBackupsPerCluster" -}}10{{- end -}} +{{- define "k10.defaultK10LimiterSnapshotExportsPerCluster" -}}10{{- end -}} +{{- define "k10.defaultK10LimiterVolumeRestoresPerCluster" -}}10{{- end -}} +{{- define "k10.defaultK10LimiterCsiSnapshotsPerCluster" -}}10{{- end -}} +{{- define "k10.defaultK10LimiterImageCopiesPerCluster" -}}10{{- end -}} +{{- define "k10.defaultK10LimiterDirectSnapshotsPerCluster" -}}10{{- end -}} +{{- define "k10.defaultK10GCDaemonPeriod" -}}21600{{- end -}} +{{- define "k10.defaultK10GCKeepMaxActions" -}}1000{{- end -}} +{{- define "k10.defaultK10GCActionsEnabled" -}}false{{- end -}} +{{- define "k10.defaultK10LimiterExecutorThreads" -}}8{{- end -}} +{{- define "k10.defaultK10LimiterCsiSnapshotRestoresPerAction" -}}3{{- end -}} +{{- define "k10.defaultK10LimiterVolumeRestoresPerAction" -}}3{{- end -}} +{{- define "k10.defaultK10LimiterWorkloadRestoresPerAction" -}}3{{- end -}} +{{- define "k10.defaultAssumeRoleDuration" -}}60m{{- end -}} +{{- define "k10.defaultK10TimeoutBlueprintBackup" -}}45{{- end -}} +{{- define "k10.defaultK10TimeoutBlueprintRestore" -}}600{{- end -}} +{{- define "k10.defaultK10TimeoutBlueprintDelete" -}}45{{- end -}} +{{- define "k10.defaultK10TimeoutBlueprintHooks" -}}20{{- end -}} +{{- define "k10.defaultK10TimeoutCheckRepoPodReady" -}}20{{- end -}} +{{- define "k10.defaultK10TimeoutStatsPodReady" -}}20{{- end -}} +{{- define "k10.defaultK10TimeoutEFSRestorePodReady" -}}45{{- end -}} +{{- define "k10.cloudProviders" -}}aws google azure{{- end -}} +{{- define "k10.serviceResources" -}} +aggregatedapis-svc: + aggregatedapis-svc: + requests: + cpu: 90m + memory: 180Mi +auth-svc: + auth-svc: + requests: + cpu: 3m + memory: 120Mi +catalog-svc: + catalog-svc: + requests: + cpu: 6m + memory: 150Mi + kanister-sidecar: + limits: + cpu: 1200m + memory: 800Mi + requests: + cpu: 100m + memory: 800Mi + schema-upgrade-check: + requests: + cpu: 6m + memory: 150Mi + upgrade-init: + requests: + cpu: 5m + memory: 20Mi +controllermanager-svc: + controllermanager-svc: + requests: + cpu: 9m + memory: 160Mi +crypto-svc: + bloblifecyclemanager-svc: + requests: + cpu: 3m + memory: 120Mi + crypto-svc: + requests: + cpu: 3m + memory: 120Mi + garbagecollector-svc: + requests: + cpu: 3m + memory: 130Mi + repositories-svc: + requests: + cpu: 3m + memory: 130Mi +dashboardbff-svc: + dashboardbff-svc: + requests: + cpu: 9m + memory: 170Mi + vbrintegrationapi-svc: + requests: + cpu: 3m + memory: 120Mi +executor-svc: + executor-svc: + requests: + cpu: 3m + memory: 160Mi + tools: + requests: + cpu: 1m + memory: 2Mi +frontend-svc: + frontend-svc: + requests: + cpu: 1m + memory: 40Mi +jobs-svc: + jobs-svc: + requests: + cpu: 3m + memory: 120Mi + upgrade-init: + requests: + cpu: 5m + memory: 20Mi +kanister-svc: + kanister-svc: + requests: + cpu: 3m + memory: 100Mi +logging-svc: + logging-svc: + requests: + cpu: 3m + memory: 120Mi + upgrade-init: + requests: + cpu: 5m + memory: 20Mi +metering-svc: + metering-svc: + requests: + cpu: 12m + memory: 170Mi + upgrade-init: + requests: + cpu: 5m + memory: 20Mi +state-svc: + events-svc: + requests: + cpu: 3m + memory: 120Mi + state-svc: + requests: + cpu: 3m + memory: 130Mi +{{- end -}} +{{- define "k10.multiClusterVersion" -}}2.5{{- end -}} +{{- define "k10.mcExternalPort" -}}18000{{- end -}} +{{- define "k10.defaultKubeVirtVMsUnfreezeTimeout" -}}5m{{- end -}} +{{- define "k10.aggAuditPolicyFile" -}}agg-audit-policy.yaml{{- end -}} +{{- define "k10.siemAuditLogFilePath" -}}-{{- end -}} +{{- define "k10.siemAuditLogFileSize" -}}100{{- end -}} +{{- define "k10.kanisterToolsImageTag" -}}0.113.0{{- end -}} +{{- define "k10.disabledServicesEnvVar" -}}K10_DISABLED_SERVICES{{- end -}} +{{- define "k10.openShiftClientSecretEnvVar" -}}K10_OPENSHIFT_CLIENT_SECRET{{- end -}} +{{- define "k10.defaultK10DefaultPriorityClassName" -}}{{- end -}} +{{- define "k10.dexServiceAccountName" -}}k10-dex-k10-sa{{- end -}} +{{- define "k10.defaultCACertConfigMapName" -}}custom-ca-bundle-store{{- end -}} +{{- define "k10.openShiftConsolePluginName" -}}console-plugin{{- end -}} +{{- define "k10.openShiftConsolePluginCRName" -}}veeam-kasten-console-plugin{{- end -}} +{{- define "k10.openShiftConsolePluginImageName" -}}ocpconsoleplugin{{- end -}} +{{- define "k10.gatewayPrefixVarName" -}}PREFIX_PATH{{- end -}} +{{- define "k10.gatewayGrafanaSvcVarName" -}}GRAFANA_SVC_NAME{{- end -}} +{{- define "k10.gatewayRequestHeadersVarName" -}}EXTAUTH_REQUEST_HEADERS{{- end -}} +{{- define "k10.gatewayAuthHeadersVarName" -}}EXTAUTH_AUTH_HEADERS{{- end -}} +{{- define "k10.gatewayPortVarName" -}}PORT{{- end -}} +{{- define "k10.gatewayEnableDex" -}}ENABLE_DEX{{- end -}} +{{- define "k10.gatewayTLSCertFile" -}}TLS_CRT_FILE{{- end -}} +{{- define "k10.gatewayTLSKeyFile" -}}TLS_KEY_FILE{{- end -}} +{{- define "k10.azureClientIDEnvVar" -}}AZURE_CLIENT_ID{{- end -}} +{{- define "k10.azureTenantIDEnvVar" -}}AZURE_TENANT_ID{{- end -}} +{{- define "k10.azureClientSecretEnvVar" -}}AZURE_CLIENT_SECRET{{- end -}} +{{- define "k10.oidcSecretName" -}}k10-oidc-auth{{- end -}} +{{- define "k10.oidcCustomerSecretName" -}}k10-oidc-auth-creds{{- end -}} +{{- define "k10.secretsDir" -}}/var/run/secrets/kasten.io{{- end -}} +{{- define "k10.sccNameEnvVar" -}}K10_SCC_NAME{{- end -}} +{{- define "k10.fluentbitEndpointEnvVar" -}}FLUENTBIT_ENDPOINT{{- end -}} diff --git a/charts/kasten/k10/7.5.401/templates/_helpers.tpl b/charts/kasten/k10/7.5.401/templates/_helpers.tpl new file mode 100644 index 0000000000..8739b88799 --- /dev/null +++ b/charts/kasten/k10/7.5.401/templates/_helpers.tpl @@ -0,0 +1,1563 @@ +{{/* Returns a string of the disabled K10 services */}} +{{- define "get.disabledServices" -}} + {{/* Append services to this list based on helm values */}} + {{- $disabledServices := list -}} + + {{- if eq .Values.logging.internal false -}} + {{- $disabledServices = append $disabledServices "logging" -}} + {{- end -}} + + {{- $disabledServices | join " " -}} +{{- end -}} + +{{/* Removes disabled service names from the provided string of service names */}} +{{- define "removeDisabledServicesFromList" -}} + {{- $disabledServices := include "get.disabledServices" .main | splitList " " -}} + {{- $services := .list | splitList " " -}} + + {{- range $disabledServices -}} + {{- $services = without $services . -}} + {{- end -}} + + {{- $services | join " " -}} +{{- end -}} + +{{/* Removes keys with disabled service names from the provided YAML string */}} +{{- define "removeDisabledServicesFromYaml" -}} + {{- $disabledServices := include "get.disabledServices" .main | splitList " " -}} + {{- $services := .yaml | fromYaml -}} + + {{- range $disabledServices -}} + {{- $services = unset $services . -}} + {{- end -}} + + {{- if gt (len $services) 0 -}} + {{- $services | toYaml | trim | nindent 0}} + {{- else -}} + {{- print "" -}} + {{- end -}} +{{- end -}} + +{{/* Returns k10.additionalServices string with disabled services removed */}} +{{- define "get.enabledAdditionalServices" -}} + {{- $list := include "k10.additionalServices" . -}} + {{- dict "main" . "list" $list | include "removeDisabledServicesFromList" -}} +{{- end -}} + +{{/* Returns k10.restServices string with disabled services removed */}} +{{- define "get.enabledRestServices" -}} + {{- $list := include "k10.restServices" . -}} + {{- dict "main" . "list" $list | include "removeDisabledServicesFromList" -}} +{{- end -}} + +{{/* Returns k10.services string with disabled services removed */}} +{{- define "get.enabledServices" -}} + {{- $list := include "k10.services" . -}} + {{- dict "main" . "list" $list | include "removeDisabledServicesFromList" -}} +{{- end -}} + +{{/* Returns k10.exposedServices string with disabled services removed */}} +{{- define "get.enabledExposedServices" -}} + {{- $list := include "k10.exposedServices" . -}} + {{- dict "main" . "list" $list | include "removeDisabledServicesFromList" -}} +{{- end -}} + +{{/* Returns k10.statelessServices string with disabled services removed */}} +{{- define "get.enabledStatelessServices" -}} + {{- $list := include "k10.statelessServices" . -}} + {{- dict "main" . "list" $list | include "removeDisabledServicesFromList" -}} +{{- end -}} + +{{/* Returns k10.colocatedServices string with disabled services removed */}} +{{- define "get.enabledColocatedServices" -}} + {{- $yaml := include "k10.colocatedServices" . -}} + {{- dict "main" . "yaml" $yaml | include "removeDisabledServicesFromYaml" -}} +{{- end -}} + +{{/* Returns YAML of primary services mapped to their secondary services */}} +{{/* The content will only have services which are not disabled */}} +{{- define "get.enabledColocatedServiceLookup" -}} + {{- $colocatedServicesLookup := include "k10.colocatedServiceLookup" . | fromYaml -}} + {{- $disabledServices := include "get.disabledServices" . | splitList " " -}} + {{- $filteredLookup := dict -}} + + {{/* construct filtered lookup */}} + {{- range $primaryService, $secondaryServices := $colocatedServicesLookup -}} + {{/* proceed only if primary service is enabled */}} + {{- if not (has $primaryService $disabledServices) -}} + {{/* filter out secondary services */}} + {{- range $disabledServices -}} + {{- $secondaryServices = without $secondaryServices . -}} + {{- end -}} + {{/* add entry for primary service only if secondary services exist */}} + {{- if gt (len $secondaryServices) 0 -}} + {{- $filteredLookup = set $filteredLookup $primaryService $secondaryServices -}} + {{- end -}} + {{- end -}} + {{- end -}} + + {{/* return filtered lookup */}} + {{- if gt (len $filteredLookup) 0 -}} + {{- $filteredLookup | toYaml | trim | nindent 0 -}} + {{- else -}} + {{- print "" -}} + {{- end -}} +{{- end -}} + +{{- define "k10.capabilities" -}} + {{- /* Internal capabilities enabled by other Helm values are added here */ -}} + {{- $internal_capabilities := list "" -}} + + {{- /* Multi-cluster */ -}} + {{- if eq .Values.multicluster.enabled true -}} + {{- $internal_capabilities = append $internal_capabilities "mc" -}} + {{- end -}} + + {{- /* FIPS */ -}} + {{- if .Values.fips.enabled -}} + {{- $internal_capabilities = append $internal_capabilities "fips.strict" -}} + {{- $internal_capabilities = append $internal_capabilities "crypto.k10.v2" -}} + {{- $internal_capabilities = append $internal_capabilities "crypto.storagerepository.v2" -}} + {{- $internal_capabilities = append $internal_capabilities "crypto.vbr.v2" -}} + {{- end -}} + + {{- concat $internal_capabilities (.Values.capabilities | default list) | join " " -}} +{{- end -}} + +{{- define "k10.capabilities_mask" -}} + {{- /* Internal capabilities masked by other Helm values are added here */ -}} + {{- $internal_capabilities_mask := list -}} + + {{- /* Multi-cluster */ -}} + {{- if eq .Values.multicluster.enabled false -}} + {{- $internal_capabilities_mask = append $internal_capabilities_mask "mc" -}} + {{- end -}} + + {{- concat $internal_capabilities_mask (.Values.capabilitiesMask | default list) | join " " -}} +{{- end -}} + +{{/* + k10.capability checks whether a given capability is enabled + + For example: + + include "k10.capability" (. | merge (dict "capability" "SOME.CAPABILITY")) +*/}} +{{- define "k10.capability" -}} + {{- $capabilities := dict -}} + {{- range $capability := include "k10.capabilities" . | splitList " " -}} + {{- $_ := set $capabilities $capability "enabled" -}} + {{- end -}} + {{- range $capability := include "k10.capabilities_mask" . | splitList " " -}} + {{- $_ := unset $capabilities $capability -}} + {{- end -}} + + {{- index $capabilities .capability | default "" -}} +{{- end -}} + +{{/* Check if VAPs and VAP bindings for checking permissions for basic users at +the Kubernetes control plane level need to be created. +Returns "true" if the Kubernetes version in the cluster supports VAP +(assumption is 1.28 and 1.29) and if the K10 capability for allowing the VAP creation +is enabled.*/}} +{{- define "vap.check" -}} + {{- if and (.Capabilities.APIVersions.Has "admissionregistration.k8s.io/v1beta1/ValidatingAdmissionPolicy") (include "k10.capability" (. | merge (dict "capability" "vap.kasten.policy.permissions"))) -}} + {{- print true -}} + {{- else -}} + {{- print false -}} + {{- end -}} +{{- end -}} + +{{/* Check if basic auth is needed */}} +{{- define "basicauth.check" -}} + {{- if .Values.auth.basicAuth.enabled }} + {{- print true }} + {{- end -}} {{/* End of check for auth.basicAuth.enabled */}} +{{- end -}} + +{{/* +Check if trusted root CA certificate related configmap settings +have been configured +*/}} +{{- define "check.cacertconfigmap" -}} +{{- if .Values.cacertconfigmap.name -}} +{{- print true -}} +{{- else -}} +{{- print false -}} +{{- end -}} +{{- end -}} + +{{/* +Check if OCP CA certificates automatic extraction is enabled +*/}} +{{- define "k10.ocpcacertsautoextraction" -}} + {{- if and .Values.auth.openshift.enabled .Values.auth.openshift.caCertsAutoExtraction -}} + {{- true -}} + {{- end -}} +{{- end -}} + +{{/* +Get the name of the CA certificate related configmap +*/}} +{{- define "k10.cacertconfigmapname" -}} + {{- if eq (include "check.cacertconfigmap" .) "true" -}} + {{- .Values.cacertconfigmap.name -}} + {{- else if (include "k10.ocpcacertsautoextraction" .) -}} + {{- include "k10.defaultCACertConfigMapName" . -}} + {{- end -}} +{{- end -}} + +{{- define "k10.sccAnnotations" -}} + {{- if .Values.scc.create -}} + {{- dict "openshift.io/required-scc" (printf "%s-scc" .Release.Name) | toYaml -}} + {{- end -}} +{{- end -}} + +{{/* Merging common pod labels for deployments with specific order of priority +to prevent overwriting of required labels. Certain site-specific required labels +are passed into the context as a dict at the caller site. */}} +{{- define "k10.deploymentPodLabels" -}} + {{- (merge (dict) (.requiredLabels) (include "k10.azMarketPlace.billingIdentifier" . | fromYaml) (include "helm.labels" . | fromYaml) (include "k10.globalPodLabels" . | fromYaml)) | toYaml -}} +{{- end -}} + +{{/* Merging common pod annotations for deployments with specific order of +priority to prevent overwriting of required annotations. Certain site-specific +required annotations are passed into the context as a dict at the caller site. */}} +{{- define "k10.deploymentPodAnnotations" -}} + {{- (merge (dict) (.requiredAnnotations) (dict "checksum/config" (include (print .Template.BasePath "/k10-config.yaml") . | sha256sum)) (dict "checksum/secret" (include (print .Template.BasePath "/secrets.yaml") . | sha256sum)) (include "k10.sccAnnotations" . | fromYaml) (include "k10.globalPodAnnotations" . | fromYaml)) | toYaml -}} +{{- end -}} + +{{/* Custom pod labels applied globally to all pods */}} +{{- define "k10.globalPodLabels" -}} + {{ include "k10.validateGlobalAndKanisterLabelsAnnotations" . }} + {{- with .Values.global.podLabels -}} + {{- toYaml . -}} + {{- end -}} +{{- end -}} + +{{/* Custom pod labels applied globally to all pods in a json format */}} +{{- define "k10.globalPodLabelsJson" -}} + {{- if .Values.global.podLabels -}} + {{- toJson .Values.global.podLabels -}} + {{- end -}} +{{- end -}} + +{{/* Custom pod annotations applied globally to all pods */}} +{{- define "k10.globalPodAnnotations" -}} + {{ include "k10.validateGlobalAndKanisterLabelsAnnotations" . }} + {{- with .Values.global.podAnnotations -}} + {{- toYaml . -}} + {{- end -}} +{{- end -}} + +{{/* Custom pod annotations applied globally to all pods in a json format */}} +{{- define "k10.globalPodAnnotationsJson" -}} + {{- if .Values.global.podAnnotations -}} + {{- toJson .Values.global.podAnnotations -}} + {{- end -}} +{{- end -}} + +{{/* +Validate and fail if the labels/annotations are configured at global level (global.podLabels) +as well as kanister helm field level (kanisterPodCustomLabels) +*/}} +{{- define "k10.validateGlobalAndKanisterLabelsAnnotations" -}} + {{- if and .Values.global.podAnnotations .Values.kanisterPodCustomAnnotations -}} + {{- fail "The `kanisterPodCustomAnnotations` field has been deprecated and cannot be used simultaneously with `global.podAnnotations`. Please use `global.podAnnotations` to set annotations to all the Kasten pods globally." }} + {{- end -}} + {{- if and .Values.global.podLabels .Values.kanisterPodCustomLabels -}} + {{- fail "The `kanisterPodCustomLabels` field has been deprecated and cannot be used simultaneously with `global.podLabels`. Please use `global.podLabels` to set labels to all the Kasten pods globally." }} + {{- end -}} +{{- end -}} + +{{/* +Check if the auth options are implemented using Dex +*/}} +{{- define "check.dexAuth" -}} +{{- if or .Values.auth.openshift.enabled .Values.auth.ldap.enabled -}} +{{- print true -}} +{{- end -}} +{{- end -}} + +{{/* Check the only 1 auth is specified */}} +{{- define "singleAuth.check" -}} +{{- $count := dict "count" (int 0) -}} +{{- $authList := list .Values.auth.basicAuth.enabled .Values.auth.tokenAuth.enabled .Values.auth.oidcAuth.enabled .Values.auth.openshift.enabled .Values.auth.ldap.enabled -}} +{{- range $i, $val := $authList }} +{{ if $val }} +{{ $c := add1 $count.count | set $count "count" }} +{{ if gt $count.count 1 }} +{{- fail "Multiple auth types were selected. Only one type can be enabled." }} +{{ end }} +{{ end }} +{{- end }} +{{- end -}}{{/* Check the only 1 auth is specified */}} + +{{/* Check if Auth is enabled */}} +{{- define "authEnabled.check" -}} +{{- $count := dict "count" (int 0) -}} +{{- $authList := list .Values.auth.basicAuth.enabled .Values.auth.tokenAuth.enabled .Values.auth.oidcAuth.enabled .Values.auth.openshift.enabled .Values.auth.ldap.enabled -}} +{{- range $i, $val := $authList }} +{{ if $val }} +{{ $c := add1 $count.count | set $count "count" }} +{{ end }} +{{- end }} +{{- if eq $count.count 0}} + {{- fail "Auth is required to expose access to K10." }} +{{- end }} +{{- end -}}{{/*end of check */}} + +{{/* Return ingress class name annotation */}} +{{- define "ingressClassAnnotation" -}} +{{- if .Values.ingress.class -}} +kubernetes.io/ingress.class: {{ .Values.ingress.class | quote }} +{{- end -}} +{{- end -}} + +{{/* Return ingress class name in spec */}} +{{- define "specIngressClassName" -}} +{{- if and .Values.ingress.class (semverCompare ">= 1.27-0" .Capabilities.KubeVersion.Version) -}} +ingressClassName: {{ .Values.ingress.class }} +{{- end -}} +{{- end -}} + +{{/* Helm required labels */}} +{{- define "helm.labels" -}} +heritage: {{ .Release.Service }} +helm.sh/chart: {{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }} +app.kubernetes.io/name: {{ .Chart.Name }} +app.kubernetes.io/instance: {{ .Release.Name }} +app.kubernetes.io/managed-by: {{ .Release.Service }} +{{ include "k10.common.matchLabels" . }} +{{- end -}} + +{{- define "k10.common.matchLabels" -}} +app: {{ .Chart.Name }} +release: {{ .Release.Name }} +{{- end -}} + +{{- define "k10.defaultRBACLabels" -}} +k10.kasten.io/default-rbac-object: "true" +{{- end -}} + +{{/* Expand the name of the chart. */}} +{{- define "name" -}} +{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}} +{{- end -}} + +{{/* +Create a default fully qualified app name. +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +*/}} +{{- define "fullname" -}} +{{- $name := default .Chart.Name .Values.nameOverride -}} +{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}} +{{- end -}} + +{{/* +Create the name of the service account to use +*/}} +{{- define "serviceAccountName" -}} +{{- if and .Values.metering.awsMarketplace ( not .Values.serviceAccount.name ) -}} + {{ print "k10-metering" }} +{{- else if .Values.serviceAccount.create -}} + {{ default (include "fullname" .) .Values.serviceAccount.name }} +{{- else -}} + {{ default "default" .Values.serviceAccount.name }} +{{- end -}} +{{- end -}} + +{{/* +Create the name of the metering service account to use +*/}} +{{- define "meteringServiceAccountName" -}} +{{- if and .Values.metering.awsManagedLicense ( not .Values.serviceAccount.name ) ( not .Values.metering.serviceAccount.name ) ( not .Values.metering.licenseConfigSecretName ) -}} + {{ print "k10-metering" }} +{{- else -}} + {{ default (include "serviceAccountName" .) .Values.metering.serviceAccount.name }} +{{- end -}} +{{- end -}} + +{{/* +Prints annotations based on .Values.fqdn.type +*/}} +{{- define "dnsAnnotations" -}} +{{- if .Values.externalGateway.fqdn.name -}} +{{- if eq "route53-mapper" ( default "" .Values.externalGateway.fqdn.type) }} +domainName: {{ .Values.externalGateway.fqdn.name | quote }} +{{- end }} +{{- if eq "external-dns" (default "" .Values.externalGateway.fqdn.type) }} +external-dns.alpha.kubernetes.io/hostname: {{ .Values.externalGateway.fqdn.name | quote }} +{{- end }} +{{- end -}} +{{- end -}} + +{{/* +Prometheus scrape config template for k10 services +*/}} +{{- define "k10.prometheusScrape" -}} +{{- $cluster_domain := "" -}} +{{- with .main.Values.cluster.domainName -}} + {{- $cluster_domain = printf ".%s" . -}} +{{- end -}} +- job_name: {{ .k10service }} + metrics_path: /metrics + {{- if eq "aggregatedapis" .k10service }} + scheme: https + tls_config: + insecure_skip_verify: true + bearer_token_file: /var/run/secrets/kubernetes.io/serviceaccount/token + {{- else }} + scheme: http + {{- end }} + static_configs: + - targets: + {{- if eq "aggregatedapis" .k10service }} + - {{ .k10service }}-svc.{{ .main.Release.Namespace }}.svc{{ $cluster_domain }}:443 + {{- else }} + {{- $service := default .k10service (index (include "get.enabledColocatedServices" . | fromYaml) .k10service).primary }} + {{- $port := default .main.Values.service.externalPort (index (include "get.enabledColocatedServices" . | fromYaml) .k10service).port }} + - {{ $service }}-svc.{{ .main.Release.Namespace }}.svc{{ $cluster_domain }}:{{ $port }} + {{- end }} + labels: + application: {{ .main.Release.Name }} + service: {{ .k10service }} +{{- end -}} + +{{/* +Prometheus scrape config template for k10 services +*/}} +{{- define "k10.prometheusTargetConfig" -}} +{{- $cluster_domain := "" -}} +{{- with .main.Values.cluster.domainName -}} + {{- $cluster_domain = printf ".%s" . -}} +{{- end -}} +- service: {{ .k10service }} + metricsPath: /metrics + {{- if eq "aggregatedapis" .k10service }} + scheme: https + tls_config: + insecure_skip_verify: true + bearer_token_file: /var/run/secrets/kubernetes.io/serviceaccount/token + {{- else }} + scheme: http + {{- end }} + {{- $serviceFqdn := "" }} + {{- $servicePort := "" }} + {{- if eq "aggregatedapis" .k10service -}} + {{- $serviceFqdn = printf "%s-svc.%s.svc%s" .k10service .main.Release.Namespace $cluster_domain -}} + {{- $servicePort = "443" -}} + {{- else -}} + {{- $service := default .k10service (index (include "get.enabledColocatedServices" .main | fromYaml) .k10service).primary -}} + {{- $port := default .main.Values.service.externalPort (index (include "get.enabledColocatedServices" .main | fromYaml) .k10service).port | toString -}} + {{- $serviceFqdn = printf "%s-svc.%s.svc%s" $service .main.Release.Namespace $cluster_domain -}} + {{- $servicePort = $port -}} + {{- end }} + fqdn: {{ $serviceFqdn }} + port: {{ $servicePort }} + application: {{ .main.Release.Name }} +{{- end -}} + +{{/* +Expands the name of the Prometheus chart. It is equivalent to what the +"prometheus.name" template does. It is needed because the referenced values in a +template are relative to where/when the template is called from, and not where +the template is defined at. This means that the value of .Chart.Name and +.Values.nameOverride are different depending on whether the template is called +from within the Prometheus chart or the K10 chart. +*/}} +{{- define "k10.prometheus.name" -}} +{{- default "prometheus" .Values.prometheus.nameOverride | trunc 63 | trimSuffix "-" -}} +{{- end -}} + +{{/* +Expands the name of the Prometheus service created to expose the prometheus server. +*/}} +{{- define "k10.prometheus.service.name" -}} +{{- default (printf "%s-%s-%s" .Release.Name "prometheus" .Values.prometheus.server.name) .Values.prometheus.server.fullnameOverride }} +{{- end -}} + +{{/* +Checks if EULA is accepted via cmd +Enforces eula.company and eula.email as required fields +returns configMap fields +*/}} +{{- define "k10.eula.fields" -}} +{{- if .Values.eula.accept -}} +accepted: "true" +company: {{ required "eula.company is required field if eula is accepted" .Values.eula.company }} +email: {{ required "eula.email is required field if eula is accepted" .Values.eula.email }} +{{- else -}} +accepted: "" +company: "" +email: "" +{{- end }} +{{- end -}} + +{{/* +Helper to determine the API Domain +*/}} +{{- define "apiDomain" -}} +{{- if .Values.useNamespacedAPI -}} +kio.{{- replace "-" "." .Release.Namespace -}} +{{- else -}} +kio.kasten.io +{{- end -}} +{{- end -}} + +{{/* +Get dex image, if user wants to +install certified version of upstream +images or not +*/}} + +{{- define "get.dexImage" }} + {{- (get .Values.global.images (include "dex.dexImageName" .)) | default (include "dex.dexImage" .) }} +{{- end }} + +{{- define "dex.dexImage" -}} + {{- printf "%s:%s" (include "dex.dexImageRepo" .) (include "dex.dexImageTag" .) }} +{{- end -}} + +{{- define "dex.dexImageRepo" -}} + {{- if .Values.global.airgapped.repository }} + {{- printf "%s/%s" .Values.global.airgapped.repository (include "dex.dexImageName" .) }} + {{- else if .Values.global.azMarketPlace }} + {{- printf "%s/%s" .Values.global.azure.images.dex.registry .Values.global.azure.images.dex.image }} + {{- else }} + {{- printf "%s/%s" .Values.global.image.registry (include "dex.dexImageName" .) }} + {{- end }} +{{- end -}} + +{{- define "dex.dexImageName" -}} + {{- printf "dex" }} +{{- end -}} + +{{- define "dex.dexImageTag" -}} + {{- if .Values.global.azMarketPlace }} + {{- print .Values.global.azure.images.dex.tag }} + {{- else }} + {{- .Values.global.image.tag | default .Chart.AppVersion }} + {{- end -}} +{{- end -}} + +{{/* + Get dex frontend directory (in the dex image) +*/}} +{{- define "k10.dexFrontendDir" -}} + {{- $dexImageDict := default $.Values.dexImage dict }} + {{- index $dexImageDict "frontendDir" | default "/srv/dex/web" }} +{{- end -}} + +{{/* +Get the k10tools image. +*/}} +{{- define "k10.k10ToolsImage" -}} + {{- (get .Values.global.images (include "k10.k10ToolsImageName" .)) | default (include "k10.k10ToolsDefaultImage" .) -}} +{{- end -}} + +{{- define "k10.k10ToolsDefaultImage" -}} + {{- printf "%s:%s" (include "k10.k10ToolsImageRepo" .) (include "k10.k10ToolsImageTag" .) -}} +{{- end -}} + +{{- define "k10.k10ToolsImageRepo" -}} + {{- if .Values.global.airgapped.repository -}} + {{- printf "%s/%s" .Values.global.airgapped.repository (include "k10.k10ToolsImageName" .) -}} + {{- else if .Values.global.azMarketPlace -}} + {{- printf "%s/%s" .Values.global.azure.images.k10tools.registry .Values.global.azure.images.k10tools.image -}} + {{- else -}} + {{- printf "%s/%s" .Values.global.image.registry (include "k10.k10ToolsImageName" .) -}} + {{- end -}} +{{- end -}} + +{{- define "k10.k10ToolsImageName" -}} + {{- print "k10tools" -}} +{{- end -}} + +{{- define "k10.k10ToolsImageTag" -}} + {{- if .Values.global.azMarketPlace -}} + {{- print .Values.global.azure.images.k10tools.tag -}} + {{- else -}} + {{- include "get.k10ImageTag" . -}} + {{- end -}} +{{- end -}} + +{{/* +Get the ocpconsoleplugin image. +*/}} +{{- define "k10.ocpConsolePluginImage" -}} + {{- (get .Values.global.images (include "k10.openShiftConsolePluginImageName" .)) | default (include "k10.ocpConsolePluginDefaultImage" .) -}} +{{- end -}} + +{{- define "k10.ocpConsolePluginDefaultImage" -}} + {{- printf "%s:%s" (include "k10.ocpConsolePluginImageRepo" .) (include "get.k10ImageTag" .) -}} +{{- end -}} + +{{- define "k10.ocpConsolePluginImageRepo" -}} + {{- if .Values.global.airgapped.repository -}} + {{- printf "%s/%s" .Values.global.airgapped.repository (include "k10.openShiftConsolePluginImageName" .) -}} + {{- else -}} + {{- printf "%s/%s" .Values.global.image.registry (include "k10.openShiftConsolePluginImageName" .) -}} + {{- end -}} +{{- end -}} + +{{/* +Get the datamover image. +*/}} +{{- define "get.datamoverImage" }} + {{- (get .Values.global.images (include "k10.datamoverImageName" .)) | default (include "k10.datamoverImage" .) }} +{{- end }} + +{{- define "k10.datamoverImage" -}} + {{- printf "%s:%s" (include "k10.datamoverImageRepo" .) (include "k10.datamoverImageTag" .) }} +{{- end -}} + +{{- define "k10.datamoverImageRepo" -}} + {{- if .Values.global.airgapped.repository }} + {{- printf "%s/%s" .Values.global.airgapped.repository (include "k10.datamoverImageName" .) }} + {{- else if .Values.global.azMarketPlace }} + {{- printf "%s/%s" .Values.global.azure.images.datamover.registry .Values.global.azure.images.datamover.image }} + {{- else }} + {{- printf "%s/%s" .Values.global.image.registry (include "k10.datamoverImageName" .) }} + {{- end }} +{{- end -}} + +{{- define "k10.datamoverImageName" -}} + {{- printf "datamover" }} +{{- end -}} + +{{- define "k10.datamoverImageTag" -}} + {{- if .Values.global.azMarketPlace }} + {{- print .Values.global.azure.images.datamover.tag }} + {{- else }} + {{- include "get.k10ImageTag" . }} + {{- end }} +{{- end -}} + +{{/* +Get the metric-sidecar image. +*/}} +{{- define "get.metricSidecarImage" }} + {{- (get .Values.global.images (include "k10.metricSidecarImageName" .)) | default (include "k10.metricSidecarImage" .) }} +{{- end }} + +{{- define "k10.metricSidecarImage" -}} + {{- printf "%s:%s" (include "k10.metricSidecarImageRepo" .) (include "k10.metricSidecarImageTag" .) }} +{{- end -}} + +{{- define "k10.metricSidecarImageRepo" -}} + {{- if .Values.global.airgapped.repository }} + {{- printf "%s/%s" .Values.global.airgapped.repository (include "k10.metricSidecarImageName" .) }} + {{- else if .Values.global.azMarketPlace }} + {{- printf "%s/%s" (.Values.global.azure.images.metricsidecar.registry) (.Values.global.azure.images.metricsidecar.image) }} + {{- else }} + {{- printf "%s/%s" .Values.global.image.registry (include "k10.metricSidecarImageName" .) }} + {{- end }} +{{- end -}} + +{{- define "k10.metricSidecarImageName" -}} + {{- printf "metric-sidecar" }} +{{- end -}} + +{{- define "k10.metricSidecarImageTag" -}} + {{- if .Values.global.azMarketPlace }} + {{- print .Values.global.azure.images.metricsidecar.tag }} + {{- else }} + {{- include "get.k10ImageTag" . }} + {{- end }} +{{- end -}} + +{{/* +Check if AWS creds are specified +*/}} +{{- define "check.awscreds" -}} +{{- if or .Values.secrets.awsAccessKeyId .Values.secrets.awsSecretAccessKey -}} +{{- print true -}} +{{- end -}} +{{- end -}} + +{{- define "check.awsSecretName" -}} +{{- if .Values.secrets.awsClientSecretName -}} +{{- print true -}} +{{- end -}} +{{- end -}} + +{{- define "check.azureFederatedIdentity" -}} +{{- if and .Values.azure.useFederatedIdentity .Values.secrets.azureClientId -}} +{{- print true -}} +{{- end -}} +{{- end -}} + +{{/* +Check if Azure MSI with Default ID is specified +*/}} +{{- define "check.azureMSIWithDefaultID" -}} +{{- if .Values.azure.useDefaultMSI -}} +{{- print true -}} +{{- end -}} +{{- end -}} + +{{/* +Check if Azure MSI with a specific Client ID is specified +*/}} +{{- define "check.azureMSIWithClientID" -}} +{{- if and (not (or .Values.secrets.azureClientSecret .Values.secrets.azureTenantId)) .Values.secrets.azureClientId -}} +{{- print true -}} +{{- end -}} +{{- end -}} + +{{/* +Check if Azure ClientSecret creds are specified +*/}} +{{- define "check.azureClientSecretCreds" -}} +{{- if and (and .Values.secrets.azureTenantId .Values.secrets.azureClientId) .Values.secrets.azureClientSecret -}} +{{- print true -}} +{{- end -}} +{{- end -}} + +{{/* +Checks and enforces only 1 set of azure creds is specified +*/}} +{{- define "enforce.singleazurecreds" -}} +{{- if and (eq (include "check.azureFederatedIdentity" .) "true") (eq (include "check.azureMSIWithDefaultID" .) "true") -}} +{{- fail "useDefaultMSI is set to true, but FederatedIdentity is also set to true. Please choose one." -}} +{{- end -}} +{{- if and (eq (include "check.azureMSIWithClientID" .) "true") (eq (include "check.azureMSIWithDefaultID" .) "true") -}} +{{- fail "useDefaultMSI is set to true, but an additional ClientID is also provided. Please choose one." -}} +{{- end -}} +{{ if and ( or (eq (include "check.azureClientSecretCreds" .) "true") (eq (include "check.azuresecret" .) "true" )) (or (eq (include "check.azureMSIWithClientID" .) "true") (eq (include "check.azureMSIWithDefaultID" .) "true")) }} +{{- fail "Both Azure ClientSecret and Managed Identity creds are available, but only one is allowed. Please choose one." }} +{{- end -}} +{{- end -}} + +{{/* +Get the kanister-tools image. +*/}} +{{- define "get.kanisterToolsImage" -}} + {{- (get .Values.global.images (include "kan.kanisterToolsImageName" .)) | default (include "kan.kanisterToolsImage" .) }} +{{- end }} + +{{- define "kan.kanisterToolsImage" -}} + {{- printf "%s:%s" (include "kan.kanisterToolsImageRepo" .) (include "kan.kanisterToolsImageTag" .) }} +{{- end -}} + +{{- define "kan.kanisterToolsImageRepo" -}} + {{- if .Values.global.airgapped.repository }} + {{- printf "%s/%s" .Values.global.airgapped.repository (include "kan.kanisterToolsImageName" .) }} + {{- else if .Values.global.azMarketPlace }} + {{- printf "%s/%s" .Values.global.azure.images.kanistertools.registry .Values.global.azure.images.kanistertools.image }} + {{- else }} + {{- printf "%s/%s" .Values.global.image.registry (include "kan.kanisterToolsImageName" .) }} + {{- end }} +{{- end -}} + +{{- define "kan.kanisterToolsImageName" -}} + {{- printf "kanister-tools" }} +{{- end -}} + +{{- define "kan.kanisterToolsImageTag" -}} + {{- if .Values.global.azMarketPlace }} + {{- print .Values.global.azure.images.kanistertools.tag }} + {{- else }} + {{- include "get.k10ImageTag" . }} + {{- end }} +{{- end -}} + +{{/* +Check if Google Workload Identity Federation is enabled +*/}} +{{- define "check.gwifenabled" -}} +{{- if .Values.google.workloadIdentityFederation.enabled -}} +{{- print true -}} +{{- end -}} +{{- end -}} + + +{{/* +Check if Google Workload Identity Federation Identity Provider is set +*/}} +{{- define "check.gwifidptype" -}} +{{- if .Values.google.workloadIdentityFederation.idp.type -}} +{{- print true -}} +{{- end -}} +{{- end -}} + +{{/* +Fail if Google Workload Identity Federation is enabled but no Identity Provider is set +*/}} +{{- define "validate.gwif.idp.type" -}} +{{- if and (eq (include "check.gwifenabled" .) "true") (ne (include "check.gwifidptype" .) "true") -}} + {{- fail "Google Workload Federation is enabled but helm flag for idp type is missing. Please set helm value google.workloadIdentityFederation.idp.type" -}} +{{- end -}} +{{- end -}} + +{{/* +Check if K8S Bound Service Account Token (aka Projected Service Account Token) is needed, +which is when GWIF is enabled and the IdP is kubernetes +*/}} +{{- define "check.projectSAToken" -}} +{{- if and (eq (include "check.gwifenabled" .) "true") (eq .Values.google.workloadIdentityFederation.idp.type "kubernetes") -}} +{{- print true -}} +{{- end -}} +{{- end -}} + +{{/* +Check if the audience that the bound service account token is intended for is set +*/}} +{{- define "check.gwifidpaud" -}} +{{- if .Values.google.workloadIdentityFederation.idp.aud -}} +{{- print true -}} +{{- end -}} +{{- end -}} + +{{/* +Fail if Service Account token projection is expected but no indented Audience is set +*/}} +{{- define "validate.gwif.idp.aud" -}} +{{- if and (eq (include "check.projectSAToken" .) "true") (ne (include "check.gwifidpaud" .) "true") -}} + {{- fail "Kubernetes is set as the Identity Provider but an intended Audience is missing. Please set helm value google.workloadIdentityFederation.idp.aud" -}} +{{- end -}} +{{- end -}} + + +{{/* +Check if Google creds are specified +*/}} +{{- define "check.googlecreds" -}} +{{- if .Values.secrets.googleApiKey -}} + {{- if eq (include "check.isBase64" .Values.secrets.googleApiKey) "false" -}} + {{- fail "secrets.googleApiKey must be base64 encoded" -}} + {{- end -}} + {{- print true -}} +{{- end -}} +{{- end -}} + +{{- define "check.googleCredsSecret" -}} +{{- if .Values.secrets.googleClientSecretName -}} + {{- print true -}} +{{- end -}} +{{- end -}} + +{{- define "check.googleCredsOrSecret" -}} +{{- if or (eq (include "check.googlecreds" .) "true") (eq (include "check.googleCredsSecret" .) "true")}} + {{- print true -}} +{{- end -}} +{{- end -}} + +{{/* +Check if Google Project ID is not set without Google API Key +*/}} +{{- define "check.googleproject" -}} +{{- if .Values.secrets.googleProjectId -}} + {{- if not .Values.secrets.googleApiKey -}} + {{- print false -}} + {{- else -}} + {{- print true -}} + {{- end -}} +{{- else -}} + {{- print true -}} +{{- end -}} +{{- end -}} + +{{/* +Check if Azure creds are specified +*/}} +{{- define "check.azurecreds" -}} +{{- if or (eq (include "check.azureClientSecretCreds" .) "true") ( or (eq (include "check.azureMSIWithClientID" .) "true") (eq (include "check.azureMSIWithDefaultID" .) "true")) -}} +{{- print true -}} +{{- end -}} +{{- end -}} + +{{- define "check.azuresecret" -}} +{{- if .Values.secrets.azureClientSecretName }} +{{- print true -}} +{{- end -}} +{{- end -}} + +{{/* +Check if Vsphere creds are specified +*/}} +{{- define "check.vspherecreds" -}} +{{- if or (or .Values.secrets.vsphereEndpoint .Values.secrets.vsphereUsername) .Values.secrets.vspherePassword -}} +{{- print true -}} +{{- end -}} +{{- end -}} + +{{- define "check.vsphereClientSecret" -}} +{{- if .Values.secrets.vsphereClientSecretName -}} +{{- print true -}} +{{- end -}} +{{- end -}} + +{{/* +Check if Vault token secret creds are specified +*/}} +{{- define "check.vaulttokenauth" -}} +{{- if .Values.vault.secretName -}} +{{- print true -}} +{{- end -}} +{{- end -}} + +{{/* +Check if K8s role is specified +*/}} +{{- define "check.vaultk8sauth" -}} +{{- if .Values.vault.role -}} +{{- print true -}} +{{- end -}} +{{- end -}} + +{{/* +Check if Vault creds for token or k8s auth are specified +*/}} +{{- define "check.vaultcreds" -}} +{{- if or (eq (include "check.vaulttokenauth" .) "true") (eq (include "check.vaultk8sauth" .) "true") -}} +{{- print true -}} +{{- end -}} +{{- end -}} + + +{{/* +Checks and enforces only 1 set of cloud creds is specified +*/}} +{{- define "enforce.singlecloudcreds" -}} +{{- $count := dict "count" (int 0) -}} +{{- $main := . -}} +{{- range $ind, $cloud_provider := include "k10.cloudProviders" . | splitList " " }} +{{ if eq (include (printf "check.%screds" $cloud_provider) $main) "true" }} +{{ $c := add1 $count.count | set $count "count" }} +{{ if gt $count.count 1 }} +{{- fail "Credentials for different cloud providers were provided but only one is allowed. Please verify your .secrets.* values." }} +{{ end }} +{{ end }} +{{- end }} +{{- end -}} + +{{/* +Converts .Values.features into k10-features: map[string]: "value" +*/}} +{{- define "k10.features" -}} +{{ range $n, $v := .Values.features }} +{{ $n }}: {{ $v | quote -}} +{{ end }} +{{- end -}} + +{{/* +Checks if string is base64 encoded +*/}} +{{- define "check.isBase64" -}} +{{- not (. | b64dec | contains "illegal base64 data") -}} +{{- end -}} + +{{/* +Returns a license base64 either from file or from values +or prints it for awsmarketplace or awsManagedLicense +*/}} +{{- define "k10.getlicense" -}} +{{- if .Values.metering.awsMarketplace -}} + {{- print "Y3VzdG9tZXJOYW1lOiBhd3MtbWFya2V0cGxhY2UKZGF0ZUVuZDogJzIxMDAtMDEtMDFUMDA6MDA6MDAuMDAwWicKZGF0ZVN0YXJ0OiAnMjAxOC0wOC0wMVQwMDowMDowMC4wMDBaJwpmZWF0dXJlczoKICBjbG91ZE1ldGVyaW5nOiBhd3MKaWQ6IGF3cy1ta3QtNWMxMDlmZDUtYWI0Yy00YTE0LWJiY2QtNTg3MGU2Yzk0MzRiCnByb2R1Y3Q6IEsxMApyZXN0cmljdGlvbnM6IG51bGwKdmVyc2lvbjogdjEuMC4wCnNpZ25hdHVyZTogY3ZEdTNTWHljaTJoSmFpazR3THMwTk9mcTNFekYxQ1pqLzRJMUZVZlBXS0JETHpuZmh2eXFFOGUvMDZxNG9PNkRoVHFSQlY3VFNJMzVkQzJ4alllaGp3cWwxNHNKT3ZyVERKZXNFWVdyMVFxZGVGVjVDd21HczhHR0VzNGNTVk5JQXVseGNTUG9oZ2x2UlRJRm0wVWpUOEtKTzlSTHVyUGxyRjlGMnpnK0RvM2UyTmVnamZ6eTVuMUZtd24xWUNlbUd4anhFaks0djB3L2lqSGlwTGQzWVBVZUh5Vm9mZHRodGV0YmhSUGJBVnVTalkrQllnRklnSW9wUlhpYnpTaEMvbCs0eTFEYzcyTDZXNWM0eUxMWFB1SVFQU3FjUWRiYnlwQ1dYYjFOT3B3aWtKMkpsR0thMldScFE4ZUFJNU9WQktqZXpuZ3FPa0lRUC91RFBtSXFBPT0K" -}} +{{- else if or ( .Values.metering.awsManagedLicense ) ( .Values.metering.licenseConfigSecretName ) -}} + {{- print "Y3VzdG9tZXJOYW1lOiBhd3MtdG90ZW0KZGF0ZUVuZDogJzIxMDAtMDEtMDFUMDA6MDA6MDAuMDAwWicKZGF0ZVN0YXJ0OiAnMjAyMS0wOS0wMVQwMDowMDowMC4wMDBaJwpmZWF0dXJlczoKICBleHRlcm5hbExpY2Vuc2U6IGF3cwogIHByb2R1Y3RTS1U6IGI4YzgyMWQ5LWJmNDAtNDE4ZC1iYTBiLTgxMjBiZjc3ZThmOQogIGtleUZpbmdlcnByaW50OiBhd3M6Mjk0NDA2ODkxMzExOkFXUy9NYXJrZXRwbGFjZTppc3N1ZXItZmluZ2VycHJpbnQKaWQ6IGF3cy1leHQtMWUxMTVlZjMtM2YyMC00MTJlLTgzODItMmE1NWUxMTc1OTFlCnByb2R1Y3Q6IEsxMApyZXN0cmljdGlvbnM6CiAgbm9kZXM6ICczJwp2ZXJzaW9uOiB2MS4wLjAKc2lnbmF0dXJlOiBkeEtLN3pPUXdzZFBOY2I1NExzV2hvUXNWeWZSVDNHVHZ0VkRuR1Vvb2VxSGlwYStTY25HTjZSNmdmdmtWdTRQNHh4RmV1TFZQU3k2VnJYeExOTE1RZmh2NFpBSHVrYmFNd3E5UXhGNkpGSmVXbTdzQmdtTUVpWVJ2SnFZVFcyMlNoakZEU1RWejY5c2JBTXNFMUd0VTdXKytITGk0dnhybjVhYkd6RkRHZW5iRE5tcXJQT3dSa3JIdTlHTFQ1WmZTNDFUL0hBMjNZZnlsTU54MGFlK2t5TGZvZXNuK3FKQzdld2NPWjh4eE94bFRJR3RuWDZ4UU5DTk5iYjhSMm5XbmljNVd0OElEc2VDR3lLMEVVRW9YL09jNFhsWVVra3FGQ0xPdVhuWDMxeFZNZ1NFQnVEWExFd3Y3K2RlSmcvb0pMaW9EVHEvWUNuM0lnem9VR2NTMGc9PQo=" -}} +{{- else -}} + {{- $license := .Values.license -}} + {{- if eq (include "check.isBase64" $license) "false" -}} + {{- $license = $license | b64enc -}} + {{- end -}} + {{- print (default (.Files.Get "license") $license) -}} +{{- end -}} +{{- end -}} + +{{/* +Returns resource usage given a pod name and container name +*/}} +{{- define "k10.resource.request" -}} +{{- $resourceDefaultList := (include "k10.serviceResources" .main | fromYaml) }} +{{- $podName := .k10_service_pod_name }} +{{- $containerName := .k10_service_container_name }} +{{- $resourceValue := "" }} +{{- if (hasKey $resourceDefaultList $podName) }} + {{- $resourceValue = index (index $resourceDefaultList $podName) $containerName }} +{{- end }} +{{- if (hasKey .main.Values.resources $podName) }} + {{- if (hasKey (index .main.Values.resources $podName) $containerName) }} + {{- $resourceValue = index (index .main.Values.resources $podName) $containerName }} + {{- end }} +{{- end }} +{{- /* If no resource usage value was provided, do not include the resources section */}} +{{- /* This allows users to set unlimited resources by providing a service key that is empty (e.g. `--set resources.=`) */}} +{{- if $resourceValue }} +resources: +{{- $resourceValue | toYaml | trim | nindent 2 }} +{{- else if eq .main.Release.Namespace "default" }} +resources: + requests: + cpu: "0.01" +{{- end }} +{{- end -}} + +{{/* +Adds priorityClassName field according to helm values. +*/}} +{{- define "k10.priorityClassName" }} +{{- $deploymentName := .k10_deployment_name }} +{{- $defaultPriorityClassName := default "" .main.Values.defaultPriorityClassName }} +{{- $priorityClassName := $defaultPriorityClassName }} + +{{- if and (hasKey .main.Values "priorityClassName") (hasKey .main.Values.priorityClassName $deploymentName) }} + {{- $priorityClassName = index .main.Values.priorityClassName $deploymentName }} +{{- end -}} + +{{- if $priorityClassName }} +priorityClassName: {{ $priorityClassName }} +{{- end }} + +{{- end }}{{/* define "k10.priorityClassName" */}} + +{{- define "kanisterToolsResources" }} +{{- if .Values.genericVolumeSnapshot.resources.requests.memory }} +KanisterToolsMemoryRequests: {{ .Values.genericVolumeSnapshot.resources.requests.memory | quote }} +{{- end }} +{{- if .Values.genericVolumeSnapshot.resources.requests.cpu }} +KanisterToolsCPURequests: {{ .Values.genericVolumeSnapshot.resources.requests.cpu | quote }} +{{- end }} +{{- if .Values.genericVolumeSnapshot.resources.limits.memory }} +KanisterToolsMemoryLimits: {{ .Values.genericVolumeSnapshot.resources.limits.memory | quote }} +{{- end }} +{{- if .Values.genericVolumeSnapshot.resources.limits.cpu }} +KanisterToolsCPULimits: {{ .Values.genericVolumeSnapshot.resources.limits.cpu | quote }} +{{- end }} +{{- end }} + +{{- define "workerPodMetricSidecarResources" }} +{{- if not (quote .Values.workerPodMetricSidecar.resources.requests.memory | empty) }} +WorkerPodMetricSidecarMemoryRequest: {{ .Values.workerPodMetricSidecar.resources.requests.memory | quote }} +{{- else if not (quote .Values.kanisterPodMetricSidecar.resources.requests.memory | empty) }} +WorkerPodMetricSidecarMemoryRequest: {{ .Values.kanisterPodMetricSidecar.resources.requests.memory | quote }} +{{- end }} +{{- if not (quote .Values.workerPodMetricSidecar.resources.requests.cpu | empty) }} +WorkerPodMetricSidecarCPURequest: {{ .Values.workerPodMetricSidecar.resources.requests.cpu | quote }} +{{- else if not (quote .Values.kanisterPodMetricSidecar.resources.requests.cpu | empty) }} +WorkerPodMetricSidecarCPURequest: {{ .Values.kanisterPodMetricSidecar.resources.requests.cpu | quote }} +{{- end }} +{{- if not (quote .Values.workerPodMetricSidecar.resources.limits.memory | empty) }} +WorkerPodMetricSidecarMemoryLimit: {{ .Values.workerPodMetricSidecar.resources.limits.memory | quote }} +{{- else if not (quote .Values.kanisterPodMetricSidecar.resources.limits.memory | empty) }} +WorkerPodMetricSidecarMemoryLimit: {{ .Values.kanisterPodMetricSidecar.resources.limits.memory | quote }} +{{- end }} +{{- if not (quote .Values.workerPodMetricSidecar.resources.limits.cpu | empty) }} +WorkerPodMetricSidecarCPULimit: {{ .Values.workerPodMetricSidecar.resources.limits.cpu | quote }} +{{- else if not (quote .Values.kanisterPodMetricSidecar.resources.limits.cpu | empty) }} +WorkerPodMetricSidecarCPULimit: {{ .Values.kanisterPodMetricSidecar.resources.limits.cpu | quote }} +{{- end }} +{{- end }} + +{{- define "workerPodResourcesCRD" }} +{{- if .Values.workerPodCRDs.resourcesRequests.maxMemory }} +workerPodMaxMemoryRequest: {{ .Values.workerPodCRDs.resourcesRequests.maxMemory | quote }} +{{- end }} +{{- if .Values.workerPodCRDs.resourcesRequests.maxCPU }} +workerPodMaxCPURequest: {{ .Values.workerPodCRDs.resourcesRequests.maxCPU | quote }} +{{- end }} +{{- if .Values.workerPodCRDs.defaultActionPodSpec }} +workerPodDefaultAPSName: {{ .Values.workerPodCRDs.defaultActionPodSpec | quote }} +{{- end }} +{{- end }} + +{{- define "get.kanisterPodCustomLabels" -}} +{{- if .Values.kanisterPodCustomLabels }} +KanisterPodCustomLabels: {{ .Values.kanisterPodCustomLabels | quote }} +{{- end }} +{{- end }} + +{{- define "get.gvsActivationToken" }} +{{- if .Values.genericStorageBackup.token }} +GVSActivationToken: {{ .Values.genericStorageBackup.token | quote }} +{{- end }} +{{- end }} + +{{- define "get.kanisterPodCustomAnnotations" -}} +{{- if .Values.kanisterPodCustomAnnotations }} +KanisterPodCustomAnnotations: {{ .Values.kanisterPodCustomAnnotations | quote }} +{{- end }} +{{- end }} + +{{/* +Lookup and return only enabled colocated services +*/}} +{{- define "get.enabledColocatedSvcList" -}} +{{- $enabledColocatedSvcList := dict }} +{{- $colocatedList := include "get.enabledColocatedServiceLookup" . | fromYaml }} +{{- range $primary, $secondaryList := $colocatedList }} + {{- $enabledSecondarySvcList := list }} + {{- range $skip, $secondary := $secondaryList }} + {{- if or (not (hasKey $.Values.optionalColocatedServices $secondary)) ((index $.Values.optionalColocatedServices $secondary).enabled) }} + {{- $enabledSecondarySvcList = append $enabledSecondarySvcList $secondary }} + {{- end }} + {{- end }} + {{- if gt (len $enabledSecondarySvcList) 0 }} + {{- $enabledColocatedSvcList = set $enabledColocatedSvcList $primary $enabledSecondarySvcList }} + {{- end }} +{{- end }} +{{- $enabledColocatedSvcList | toYaml | trim | nindent 0}} +{{- end -}} + +{{- define "get.serviceContainersInPod" -}} +{{- $podService := .k10_service_pod }} +{{- $colocatedList := include "get.enabledColocatedServices" .main | fromYaml }} +{{- $colocatedLookupByPod := include "get.enabledColocatedSvcList" .main | fromYaml }} +{{- $containerList := list $podService }} +{{- if hasKey $colocatedLookupByPod $podService }} + {{- $containerList = concat $containerList (index $colocatedLookupByPod $podService)}} +{{- end }} +{{- $containerList | join " " }} +{{- end -}} + +{{- define "get.statefulRestServicesInPod" -}} +{{- $statefulRestSvcsInPod := list }} +{{- $podService := .k10_service_pod }} +{{- $containerList := (dict "main" .main "k10_service_pod" $podService | include "get.serviceContainersInPod" | splitList " ") }} +{{- if .main.Values.global.persistence.enabled }} + {{- range $skip, $containerInPod := $containerList }} + {{- $isRestService := has $containerInPod (include "get.enabledRestServices" $.main | splitList " ") }} + {{- $isStatelessService := has $containerInPod (include "get.enabledStatelessServices" $.main | splitList " ") }} + {{- if and $isRestService (not $isStatelessService) }} + {{- $statefulRestSvcsInPod = append $statefulRestSvcsInPod $containerInPod }} + {{- end }} + {{- end }} +{{- end }} +{{- $statefulRestSvcsInPod | join " " }} +{{- end -}} + +{{- define "k10.prefixPath" -}} + {{- if .Values.route.enabled -}} + /{{ .Values.route.path | default .Release.Name | trimPrefix "/" | trimSuffix "/" }} + {{- else if .Values.ingress.create -}} + /{{ .Values.ingress.urlPath | default .Release.Name | trimPrefix "/" | trimSuffix "/" }} + {{- else -}} + /{{ .Release.Name }} + {{- end -}} +{{- end -}} + +{{/* +Check if encryption keys are specified +*/}} +{{- define "check.primaryKey" -}} +{{- if (or .Values.encryption.primaryKey.awsCmkKeyId .Values.encryption.primaryKey.vaultTransitKeyName) -}} +{{- print true -}} +{{- end -}} +{{- end -}} + +{{- define "check.validateImagePullSecrets" -}} + {{/* Validate image pull secrets if a custom Docker config is provided */}} + {{- if (or .Values.secrets.dockerConfig .Values.secrets.dockerConfigPath ) -}} + {{- if (and .Values.prometheus.server.enabled (not .Values.global.imagePullSecret) (not .Values.prometheus.imagePullSecrets)) -}} + {{ fail "A custom Docker config was provided, but Prometheus is not configured to use it. Please check that global.imagePullSecret is set correctly." }} + {{- end -}} + {{- end -}} +{{- end -}} + +{{- define "k10.imagePullSecrets" }} +{{- $imagePullSecrets := list .Values.global.imagePullSecret }}{{/* May be empty, but the compact below will handle that */}} +{{- if (or .Values.secrets.dockerConfig .Values.secrets.dockerConfigPath) }} + {{- $imagePullSecrets = concat $imagePullSecrets (list "k10-ecr") }} +{{- end }} +{{- $imagePullSecrets = $imagePullSecrets | compact | uniq }} + +{{- if $imagePullSecrets }} +imagePullSecrets: + {{- range $imagePullSecrets }} + {{/* Check if the name is not empty string */}} + - name: {{ . }} + {{- end }} +{{- end }} +{{- end }} + +{{/* +k10.imagePullSecretNames gets us just the secret names that are going be used +as imagePullSecrets in the k10 services. +*/}} +{{- define "k10.imagePullSecretNames" }} +{{- $pullSecretsSpec := (include "k10.imagePullSecrets" . ) | fromYaml }} +{{- if $pullSecretsSpec }} + {{- range $pullSecretsSpec.imagePullSecrets }} + {{- $secretName := . }} + {{- printf "%s " ( $secretName.name) }} + {{- end}} +{{- end}} +{{- end }} + +{{/* +Below helper template functions are referred from chart +https://github.com/prometheus-community/helm-charts/blob/main/charts/prometheus/templates/_helpers.tpl +*/}} + +{{/* +Return kubernetes version +*/}} +{{- define "k10.kubeVersion" -}} + {{- default .Capabilities.KubeVersion.Version (regexFind "v[0-9]+\\.[0-9]+\\.[0-9]+" .Capabilities.KubeVersion.Version) -}} +{{- end -}} + +{{/* +Return the appropriate apiVersion for ingress. +*/}} +{{- define "ingress.apiVersion" -}} + {{- if and (.Capabilities.APIVersions.Has "networking.k8s.io/v1") (semverCompare ">= 1.19.x" (include "k10.kubeVersion" .)) -}} + {{- print "networking.k8s.io/v1" -}} + {{- else if .Capabilities.APIVersions.Has "extensions/v1beta1" -}} + {{- print "extensions/v1beta1" -}} + {{- else -}} + {{- print "networking.k8s.io/v1beta1" -}} + {{- end -}} +{{- end -}} + +{{/* +Is ingress part of stable APIVersion. +*/}} +{{- define "ingress.isStable" -}} + {{- eq (include "ingress.apiVersion" .) "networking.k8s.io/v1" -}} +{{- end -}} + +{{/* +Check if `ingress.defaultBackend` is properly formatted when specified. +*/}} +{{- define "check.ingress.defaultBackend" -}} + {{- if .Values.ingress.defaultBackend -}} + {{- if and .Values.ingress.defaultBackend.service.enabled .Values.ingress.defaultBackend.resource.enabled -}} + {{- fail "Both `service` and `resource` cannot be enabled in the `ingress.defaultBackend`. Provide only one." -}} + {{- end -}} + {{- if .Values.ingress.defaultBackend.service.enabled -}} + {{- if and (not .Values.ingress.defaultBackend.service.port.name) (not .Values.ingress.defaultBackend.service.port.number) -}} + {{- fail "Provide either `name` or `number` in the `ingress.defaultBackend.service.port`." -}} + {{- end -}} + {{- if and .Values.ingress.defaultBackend.service.port.name .Values.ingress.defaultBackend.service.port.number -}} + {{- fail "Both `name` and `number` cannot be specified in the `ingress.defaultBackend.service.port`. Provide only one." -}} + {{- end -}} + {{- end -}} + {{- end -}} +{{- end -}} + +{{- define "check.validatePrometheusConfig" -}} + {{if and ( and .Values.global.prometheus.external.host .Values.global.prometheus.external.port) .Values.prometheus.server.enabled}} + {{ fail "Both internal and external Prometheus configs are not allowed at same time"}} + {{- end -}} +{{- end -}} + +{{/* +Defines unique ID to be assigned to all the K10 ambassador resources. +This will ensure that the K10's ambassador does not conflict with any other ambassador instances +running in the same cluster. +*/}} +{{- define "k10.ambassadorId" -}} +"kasten.io/k10" +{{- end -}} + +{{/* Check that image.values are not set. */}} +{{- define "image.values.check" -}} + {{- if not (empty .main.Values.image) }} + + {{- $registry := .main.Values.image.registry }} + {{- $repository := .main.Values.image.repository }} + {{- if or $registry $repository }} + {{- $registry = coalesce $registry "gcr.io" }} + {{- $repository = coalesce $repository "kasten-images" }} + + {{- $oldCombinedRegistry := "" }} + {{- if hasPrefix $registry $repository }} + {{- $oldCombinedRegistry = $repository }} + {{- else }} + {{- $oldCombinedRegistry = printf "%s/%s" $registry $repository }} + {{- end }} + + {{- if ne $oldCombinedRegistry .main.Values.global.image.registry }} + {{- fail "Setting image.registry and image.repository is no longer supported use global.image.registry instead" }} + {{- end }} + {{- end }} + + {{- $tag := .main.Values.image.tag }} + {{- if $tag }} + {{- if ne $tag .main.Values.global.image.tag }} + {{- fail "Setting image.tag is no longer supported use global.image.tag instead" }} + {{- end }} + {{- end }} + + {{- $pullPolicy := .main.Values.image.pullPolicy }} + {{- if $pullPolicy }} + {{- if ne $pullPolicy .main.Values.global.image.pullPolicy }} + {{- fail "Setting image.pullPolicy is no longer supported use global.image.pullPolicy instead" }} + {{- end }} + {{- end }} + + {{- end }} +{{- end -}} + +{{/* Used to verify if Ironbank is enabled */}} +{{- define "ironbank.enabled" -}} + {{- if (.Values.global.ironbank | default dict).enabled -}} + {{- print true -}} + {{- end -}} +{{- end -}} + +{{/* Get the K10 image tag. Fails if not set correctly */}} +{{- define "get.k10ImageTag" -}} + {{- $imageTag := coalesce .Values.global.image.tag (include "k10.imageTag" .) }} + {{- if not $imageTag }} + {{- fail "global.image.tag must be set because helm chart does not include a default tag." }} + {{- else }} + {{- $imageTag }} + {{- end }} +{{- end -}} + +{{- define "get.initImage" -}} + {{- (get .Values.global.images (include "init.ImageName" .)) | default (include "init.Image" .) }} +{{- end -}} + +{{- define "init.Image" -}} + {{- printf "%s:%s" (include "init.ImageRepo" .) (include "get.k10ImageTag" .) }} +{{- end -}} + +{{- define "init.ImageRepo" -}} + {{- if .Values.global.airgapped.repository }} + {{- printf "%s/%s" .Values.global.airgapped.repository (include "init.ImageName" .) }} + {{- else if .main.Values.global.azMarketPlace }} + {{- printf "%s/%s" .Values.global.azure.images.init.registry .Values.global.azure.images.init.image }} + {{- else }} + {{- printf "%s/%s" .Values.global.image.registry (include "init.ImageName" .) }} + {{- end }} +{{- end -}} + +{{- define "init.ImageName" -}} + {{- printf "init" }} +{{- end -}} + +{{- define "k10.splitImage" -}} + {{- $split_repo_tag_and_hash := .image | splitList "@" -}} + {{- $split_repo_and_tag := $split_repo_tag_and_hash | first | splitList ":" -}} + {{- $repo := $split_repo_and_tag | first -}} + + {{- /* Error if there are extra pieces we don't understand in the image */ -}} + {{- $split_repo_tag_and_hash_len := $split_repo_tag_and_hash | len -}} + {{- $split_repo_and_tag_len := $split_repo_and_tag | len -}} + {{- if or (gt $split_repo_tag_and_hash_len 2) (gt $split_repo_and_tag_len 2) -}} + {{- fail (printf "Unsupported image format: %q (%s)" .image .path) -}} + {{- end -}} + + {{- $digest := $split_repo_tag_and_hash | rest | first -}} + {{- $tag := $split_repo_and_tag | rest | first -}} + + {{- $sha := "" -}} + {{- if $digest -}} + {{- if not ($digest | hasPrefix "sha256:") -}} + {{- fail (printf "Unsupported image ...@hash type: %q (%s)" .image .path) -}} + {{- end -}} + {{- $sha = $digest | trimPrefix "sha256:" }} + {{- end -}} + + {{- /* Split out the registry if the first component of the repo contains a "." */ -}} + {{- $registry := "" }} + {{- $split_repo := $repo | splitList "/" -}} + {{- if first $split_repo | contains "." -}} + {{- $registry = first $split_repo -}} + {{- $split_repo = rest $split_repo -}} + {{- end -}} + {{- $repo = $split_repo | join "/" -}} + + {{- + (dict + "registry" $registry + "repository" $repo + "tag" ($tag | default "") + "digest" ($digest | default "") + "sha" ($sha | default "") + ) | toJson + -}} +{{- end -}} + +{{/* Fail if Ironbank is enabled and images we don't support are turned on */}} +{{- define "k10.fail.ironbankRHMarketplace" -}} + {{- if and (include "ironbank.enabled" .) (.Values.global.rhMarketPlace) -}} + {{- fail "global.ironbank.enabled and global.rhMarketPlace cannot both be enabled at the same time" -}} + {{- end -}} +{{- end -}} + +{{/* Fail if Ironbank is enabled and images we don't support are turned on */}} +{{- define "k10.fail.ironbankGrafana" -}} + {{- if (include "ironbank.enabled" .) -}} + {{- range $key, $value := .Values.grafana.sidecar -}} + {{/* + https://go.dev/doc/go1.18: the "and" used to evaluate all conditions and not terminate early + if a predicate was met, so we must have the below as their own conditional for any customers + used go version < 1.18. + */}} + {{- if kindIs "map" $value -}} + {{- if hasKey $value "enabled" -}} + {{- if $value.enabled -}} + {{- fail (printf "Ironbank deployment does not support grafana sidecar %s" $key) -}} + {{- end -}} + {{- end -}} + {{- end -}} + {{- end -}} + {{- end -}} +{{- end -}} + +{{/* Fail if Ironbank is enabled and images we don't support are turned on */}} +{{- define "k10.fail.ironbankPrometheus" -}} + {{- if (include "ironbank.enabled" .) -}} + {{- $prometheusDict := pick .Values.prometheus "alertmanager" "kube-state-metrics" "prometheus-node-exporter" "prometheus-pushgateway" -}} + {{- range $key, $value := $prometheusDict -}} + {{/* + https://go.dev/doc/go1.18: the "and" used to evaluate all conditions and not terminate early + if a predicate was met, so we must have the below as their own conditional for any customers + used go version < 1.18. + */}} + {{- if kindIs "map" $value -}} + {{- if hasKey $value "enabled" -}} + {{- if $value.enabled -}} + {{- fail (printf "Ironbank deployment does not support prometheus %s" $key) -}} + {{- end -}} + {{- end -}} + {{- end -}} + {{- end -}} + {{- end -}} +{{- end -}} + +{{/* Fail if FIPS is enabled and Prometheus is turned on */}} +{{- define "k10.fail.fipsPrometheus" -}} + {{- if and (.Values.fips.enabled) (.Values.prometheus.server.enabled) -}} + {{- fail "fips.enabled and prometheus.server.enabled cannot both be enabled at the same time" -}} + {{- end -}} +{{- end -}} + +{{/* Check to see whether SIEM logging is enabled */}} +{{- define "k10.siemEnabled" -}} + {{- if or .Values.siem.logging.cluster.enabled .Values.siem.logging.cloud.awsS3.enabled -}} + {{- true -}} + {{- end -}} +{{- end -}} + +{{/* Determine if logging should go to filepath instead of stdout */}} +{{- define "k10.siemLoggingClusterFile" -}} + {{- if .Values.siem.logging.cluster.enabled -}} + {{- if (.Values.siem.logging.cluster.file | default dict).enabled -}} + {{- .Values.siem.logging.cluster.file.path | default "" -}} + {{- end -}} + {{- end -}} +{{- end -}} + +{{/* Determine if a max file size should be used */}} +{{- define "k10.siemLoggingClusterFileSize" -}} + {{- if .Values.siem.logging.cluster.enabled -}} + {{- if (.Values.siem.logging.cluster.file | default dict).enabled -}} + {{- .Values.siem.logging.cluster.file.size | default "" -}} + {{- end -}} + {{- end -}} +{{- end -}} + +{{/* Returns a generated name for the OpenShift Service Account secret */}} +{{- define "get.openshiftServiceAccountSecretName" -}} + {{ printf "%s-k10-secret" (include "get.openshiftServiceAccountName" .) | quote }} +{{- end -}} + +{{/* +Returns a generated name for the OpenShift Service Account if a service account name +is not configuredby the user using the helm value auth.openshift.serviceAccount +*/}} +{{- define "get.openshiftServiceAccountName" -}} + {{ default (include "k10.dexServiceAccountName" .) .Values.auth.openshift.serviceAccount}} +{{- end -}} + +{{/* +Returns the required environment variables to enforce FIPS mode using +the Microsoft Go toolchain and Red Hat's OpenSSL. +*/}} +{{- define "k10.enforceFIPSEnvironmentVariables" }} +- name: GOFIPS + value: "1" +- name: OPENSSL_FORCE_FIPS_MODE + value: "1" +{{- if .Values.fips.disable_ems }} +- name: KASTEN_CRYPTO_POLICY + value: disable_ems +{{- end }} +{{- end }} + +{{/* +Returns a billing identifier label to be added to workloads for azure marketplace offer +*/}} +{{- define "k10.azMarketPlace.billingIdentifier" -}} + {{- if .Values.global.azMarketPlace -}} + azure-extensions-usage-release-identifier: {{.Release.Name}} + {{- end -}} +{{- end -}} + +{{/* +Returns the externally configured grafana URL +*/}} +{{- define "k10.grafanaUrl" -}} + {{- if (.Values.grafana.external.url) }} + {{- .Values.grafana.external.url }} + {{- end }} +{{- end }} + +{{/* Fail if internal logging is enabled and fluentbit endpoint is specified, otherwise return the fluentbit endpoint even if its empty */}} +{{- define "k10.fluentbitEndpoint" -}} + {{- if and (.Values.logging.fluentbit_endpoint) (.Values.logging.internal) -}} + {{- fail "logging.fluentbit_endpoint cannot be set if logging.internal is true" -}} + {{- end -}} + {{ .Values.logging.fluentbit_endpoint }} +{{- end -}} + +{{/* +Returns the name of the K10 OpenShift Console Plugin ConfigMap +*/}} +{{- define "k10.openShiftConsolePluginConfigMapName" -}} + {{- printf "%s-config" (include "k10.openShiftConsolePluginName" .) -}} +{{- end -}} + +{{/* +Returns the name of the K10 OpenShift Console Plugin TLS certificate +*/}} +{{- define "k10.openShiftConsolePluginTLSCertName" -}} + {{- printf "%s-tls-cert" (include "k10.openShiftConsolePluginName" .) -}} +{{- end -}} + +{{/* +Returns the name of the K10 OpenShift Console Plugin Proxy +*/}} +{{- define "k10.openShiftConsolePluginProxyName" -}} + {{- printf "%s-proxy" (include "k10.openShiftConsolePluginName" .) -}} +{{- end -}} + +{{/* +Returns the name of the K10 OpenShift Console Plugin Proxy ConfigMap +*/}} +{{- define "k10.openShiftConsolePluginProxyConfigMapName" -}} + {{- printf "%s-config" (include "k10.openShiftConsolePluginProxyName" .) -}} +{{- end -}} + +{{/* +Return the name of the K10 OpenShift Console Plugin Proxy TLS certificate +*/}} +{{- define "k10.openShiftConsolePluginProxyTLSCertName" -}} + {{- printf "%s-tls-cert" (include "k10.openShiftConsolePluginProxyName" .) -}} +{{- end -}} + +{{/* +Returns true if release is being installed to the OpenShift cluster +*/}} +{{- define "k10.isOpenShift" -}} + {{- $isOpenShift := "false" -}} + + {{- if .Capabilities.APIVersions -}} + {{- if .Capabilities.APIVersions.Has "console.openshift.io/v1" -}} + {{- $isOpenShift = "true" -}} + {{- end -}} + {{- end -}} + + {{/* We consider that K10 is being installed to OpenShift if .Values.scc.create is true */}} + {{- if .Values.scc.create -}} + {{- $isOpenShift = "true" -}} + {{- end -}} + + {{- print $isOpenShift -}} +{{- end -}} diff --git a/charts/kasten/k10/7.5.401/templates/_k10_container.tpl b/charts/kasten/k10/7.5.401/templates/_k10_container.tpl new file mode 100644 index 0000000000..a00e88be81 --- /dev/null +++ b/charts/kasten/k10/7.5.401/templates/_k10_container.tpl @@ -0,0 +1,1161 @@ +{{- define "k10-containers" }} +{{- $pod := .k10_pod }} +{{- with .main }} +{{- $main_context := . }} +{{- $colocatedList := include "get.enabledColocatedServices" . | fromYaml }} +{{- $containerList := (dict "main" $main_context "k10_service_pod" $pod | include "get.serviceContainersInPod" | splitList " ") }} + containers: +{{- range $skip, $container := $containerList }} + {{- $port := default $main_context.Values.service.externalPort (index $colocatedList $container).port }} + {{- $serviceStateful := has $container (dict "main" $main_context "k10_service_pod" $pod | include "get.statefulRestServicesInPod" | splitList " ") }} + {{- dict "main" $main_context "k10_pod" $pod "k10_container" $container "externalPort" $port "stateful" $serviceStateful | include "k10-container" }} +{{- end }} +{{- end }}{{/* with .main */}} +{{- end }}{{/* define "k10-containers" */}} + +{{- define "k10-container" }} +{{- $pod := .k10_pod }} +{{- $service := .k10_container }} +{{- $externalPort := .externalPort }} +{{- with .main }} + - name: {{ $service }}-svc + {{- dict "main" . "k10_service" $service | include "serviceImage" | indent 8 }} + imagePullPolicy: {{ .Values.global.image.pullPolicy }} +{{- if eq $service "aggregatedapis" }} + args: + - "--secure-port={{ .Values.service.aggregatedApiPort }}" + - "--cert-dir=/tmp/apiserver.local.config/certificates/" + {{- if include "k10.siemEnabled" . }} + - "--audit-policy-file=/etc/kubernetes/{{ include "k10.aggAuditPolicyFile" .}}" + {{/* SIEM cloud logging */}} + - "--enable-k10-audit-cloud-aws-s3={{ .Values.siem.logging.cloud.awsS3.enabled }}" + - "--audit-cloud-path={{ .Values.siem.logging.cloud.path }}" + {{/* SIEM cluster logging */}} + - "--enable-k10-audit-cluster={{ .Values.siem.logging.cluster.enabled }}" + - "--audit-log-file={{ include "k10.siemLoggingClusterFile" . | default (include "k10.siemAuditLogFilePath" .) }}" + - "--audit-log-file-size={{ include "k10.siemLoggingClusterFileSize" . | default (include "k10.siemAuditLogFileSize" .) }}" + {{- end }} +{{- if .Values.useNamespacedAPI }} + - "--k10-api-domain={{ template "apiDomain" . }}" +{{- end }}{{/* .Values.useNamespacedAPI */}} +{{/* +We need this explicit conversion because installation using operator hub was failing +stating that types are not same for the equality check +*/}} +{{- else if not (eq (int .Values.service.externalPort) (int $externalPort) ) }} + args: + - "--port={{ $externalPort }}" + - "--host=0.0.0.0" +{{- end }}{{/* eq $service "aggregatedapis" */}} +{{- $podName := (printf "%s-svc" $pod) }} +{{- $containerName := (printf "%s-svc" $service) }} +{{- dict "main" . "k10_service_pod_name" $podName "k10_service_container_name" $containerName | include "k10.resource.request" | indent 8}} + ports: +{{- if eq $service "aggregatedapis" }} + - containerPort: {{ .Values.service.aggregatedApiPort }} +{{- else }} + - containerPort: {{ $externalPort }} + {{- if eq $service "controllermanager" }} + - containerPort: {{ include "k10.mcExternalPort" nil }} + {{- end }} +{{- end }} +{{- if eq $service "logging" }} + - containerPort: 24224 + protocol: TCP + - containerPort: 24225 + protocol: TCP +{{- end }} + securityContext: + allowPrivilegeEscalation: false + readOnlyRootFilesystem: false + capabilities: + drop: ["ALL"] + livenessProbe: +{{- if eq $service "aggregatedapis" }} + tcpSocket: + port: {{ .Values.service.aggregatedApiPort }} + timeoutSeconds: 5 +{{- else }} + httpGet: + path: /v0/healthz + port: {{ $externalPort }} + timeoutSeconds: 1 +{{- end }} + initialDelaySeconds: 300 +{{- if ne $service "aggregatedapis" }} + readinessProbe: + httpGet: + path: /v0/healthz + port: {{ $externalPort }} + initialDelaySeconds: 3 +{{- end }} + env: +{{- if eq $service "dashboardbff" }} + - name: {{ include "k10.disabledServicesEnvVar" . }} + value: {{ include "get.disabledServices" . | quote }} +{{- end -}} +{{- if list "dashboardbff" "executor" "garbagecollector" "controllermanager" "kanister" | has $service}} +{{- if not (eq (include "check.googleproject" . ) "true") -}} + {{- fail "secrets.googleApiKey field is required when using secrets.googleProjectId" -}} +{{- end -}} +{{- $gkeSecret := default "google-secret" .Values.secrets.googleClientSecretName }} +{{- $gkeProjectId := "kasten-gke-project" }} +{{- $gkeApiKey := "/var/run/secrets/kasten.io/kasten-gke-sa.json"}} +{{- if eq (include "check.googleCredsSecret" .) "true" }} + {{- $gkeProjectId = "google-project-id" }} + {{- $gkeApiKey = "/var/run/secrets/kasten.io/google-api-key" }} +{{- end }} +{{- if eq (include "check.googleCredsOrSecret" .) "true" }} + - name: GOOGLE_APPLICATION_CREDENTIALS + value: {{ $gkeApiKey }} +{{- end }} +{{- if eq (include "check.googleCredsOrSecret" .) "true" }} + - name: projectID + valueFrom: + secretKeyRef: + name: {{ $gkeSecret }} + key: {{ $gkeProjectId }} + optional: true +{{- end }} +{{- end }} +{{- if list "dashboardbff" "executor" "garbagecollector" "controllermanager" "kanister" | has $service}} +{{- if or (eq (include "check.azuresecret" .) "true") (eq (include "check.azurecreds" .) "true" ) }} +{{- if eq (include "check.azuresecret" .) "true" }} + - name: {{ include "k10.azureClientIDEnvVar" . }} + valueFrom: + secretKeyRef: + name: {{ .Values.secrets.azureClientSecretName }} + key: azure_client_id + - name: {{ include "k10.azureTenantIDEnvVar" . }} + valueFrom: + secretKeyRef: + name: {{ .Values.secrets.azureClientSecretName }} + key: azure_tenant_id + - name: {{ include "k10.azureClientSecretEnvVar" . }} + valueFrom: + secretKeyRef: + name: {{ .Values.secrets.azureClientSecretName }} + key: azure_client_secret +{{- else }} +{{- if and (or (eq (include "check.azureMSIWithClientID" .) "true") (eq (include "check.azureClientSecretCreds" .) "true")) (not (eq (include "check.azureFederatedIdentity" . ) "true")) }} + - name: {{ include "k10.azureClientIDEnvVar" . }} + valueFrom: + secretKeyRef: + name: azure-creds + key: azure_client_id +{{- end }} +{{- if eq (include "check.azureClientSecretCreds" .) "true" }} + - name: {{ include "k10.azureTenantIDEnvVar" . }} + valueFrom: + secretKeyRef: + name: azure-creds + key: azure_tenant_id + - name: {{ include "k10.azureClientSecretEnvVar" . }} + valueFrom: + secretKeyRef: + name: azure-creds + key: azure_client_secret +{{- end }} +{{- end }} +{{- if .Values.secrets.azureResourceGroup }} + - name: AZURE_RESOURCE_GROUP + valueFrom: + secretKeyRef: + name: azure-creds + key: azure_resource_group +{{- end }} +{{- if .Values.secrets.azureSubscriptionID }} + - name: AZURE_SUBSCRIPTION_ID + valueFrom: + secretKeyRef: + name: azure-creds + key: azure_subscription_id +{{- end }} +{{- if .Values.secrets.azureResourceMgrEndpoint }} + - name: AZURE_RESOURCE_MANAGER_ENDPOINT + valueFrom: + secretKeyRef: + name: azure-creds + key: azure_resource_manager_endpoint +{{- end }} +{{- if or .Values.secrets.azureADEndpoint .Values.secrets.microsoftEntraIDEndpoint }} + - name: AZURE_AD_ENDPOINT + valueFrom: + secretKeyRef: + name: azure-creds + key: entra_id_endpoint +{{- end }} +{{- if or .Values.secrets.azureADResourceID .Values.secrets.microsoftEntraIDResourceID }} + - name: AZURE_AD_RESOURCE + valueFrom: + secretKeyRef: + name: azure-creds + key: entra_id_resource_id +{{- end }} +{{- if .Values.secrets.azureCloudEnvID }} + - name: AZURE_CLOUD_ENV_ID + valueFrom: + secretKeyRef: + name: azure-creds + key: azure_cloud_env_id +{{- end }} +{{- if eq (include "check.azureMSIWithDefaultID" .) "true" }} + - name: USE_AZURE_DEFAULT_MSI + value: "{{ .Values.azure.useDefaultMSI }}" +{{- end }} +{{- if eq (include "check.azureFederatedIdentity" .) "true" }} + - name: USE_AZURE_FEDERATED_IDENTITY + value: "{{ .Values.azure.useFederatedIdentity }}" +{{- end }} +{{- end }} +{{- end }} + +{{- /* +There are 3 valid states of the secret provided by customer: +1. Only role set +2. Both aws_access_key_id and aws_secret_access_key are set +3. All of role, aws_access_key_id and aws_secret_access_key are set. +*/}} +{{- if eq (include "check.awsSecretName" .) "true" }} + {{- $customerSecret := (lookup "v1" "Secret" .Release.Namespace .Values.secrets.awsClientSecretName )}} + {{- if $customerSecret }} + {{- if and (not $customerSecret.data.role) (not $customerSecret.data.aws_access_key_id) (not $customerSecret.data.aws_secret_access_key) }} + {{ fail "Provided secret must contain at least AWS IAM Role or AWS access key ID together with AWS secret access key"}} + {{- end }} + {{- if not (or (and $customerSecret.data.aws_access_key_id $customerSecret.data.aws_secret_access_key) (and (not $customerSecret.data.aws_access_key_id) (not $customerSecret.data.aws_secret_access_key))) }} + {{ fail "Provided secret lacks aws_access_key_id or aws_secret_access_key" }} + {{- end }} + {{- end }} +{{- end }} +{{- if list "dashboardbff" "executor" "garbagecollector" "controllermanager" "metering" "kanister" | has $service}} +{{- $awsSecretName := default "aws-creds" .Values.secrets.awsClientSecretName }} + - name: AWS_ACCESS_KEY_ID + valueFrom: + secretKeyRef: + name: {{ $awsSecretName }} + key: aws_access_key_id + optional: true + - name: AWS_SECRET_ACCESS_KEY + valueFrom: + secretKeyRef: + name: {{ $awsSecretName }} + key: aws_secret_access_key + optional: true + - name: K10_AWS_IAM_ROLE + valueFrom: + secretKeyRef: + name: {{ $awsSecretName }} + key: role + optional: true +{{- end }} +{{- if list "controllermanager" "executor" "catalog" | has $service}} +{{- if eq (include "check.gwifenabled" .) "true"}} + - name: GOOGLE_WORKLOAD_IDENTITY_FEDERATION_ENABLED + value: "true" +{{- if eq (include "check.gwifidptype" .) "true"}} + - name: GOOGLE_WORKLOAD_IDENTITY_FEDERATION_IDP + value: {{ .Values.google.workloadIdentityFederation.idp.type }} +{{- end }} +{{- if eq (include "check.gwifidpaud" .) "true"}} + - name: GOOGLE_WORKLOAD_IDENTITY_FEDERATION_AUD + value: {{ .Values.google.workloadIdentityFederation.idp.aud }} +{{- end }} +{{- end }} {{/* if eq (include "check.gwifenabled" .) "true" */}} +{{- end }} {{/* list "controllermanager" "executor" "catalog" | has $service */}} +{{- if or (eq $service "crypto") (eq $service "executor") (eq $service "dashboardbff") (eq $service "repositories") (eq $service "aggregatedapis") }} +{{- if eq (include "check.vaultcreds" .) "true" }} + - name: VAULT_ADDR + value: {{ .Values.vault.address }} +{{- if eq (include "check.vaultk8sauth" .) "true" }} + - name: VAULT_AUTH_ROLE + value: {{ .Values.vault.role }} + - name: VAULT_K8S_SERVICE_ACCOUNT_TOKEN_PATH + value: {{ .Values.vault.serviceAccountTokenPath }} +{{- end }} +{{- if (eq (include "check.vaulttokenauth" .) "true") }} + - name: VAULT_TOKEN + valueFrom: + secretKeyRef: + name: {{.Values.vault.secretName }} + key: vault_token +{{- end }} +{{- end }} +{{- end }} +{{- if list "dashboardbff" "executor" "garbagecollector" "controllermanager" | has $service}} +{{- if or (eq (include "check.vspherecreds" .) "true") (eq (include "check.vsphereClientSecret" .) "true") }} +{{- $vsphereSecretName := default "vsphere-creds" .Values.secrets.vsphereClientSecretName }} + - name: VSPHERE_ENDPOINT + valueFrom: + secretKeyRef: + name: {{ $vsphereSecretName }} + key: vsphere_endpoint + - name: VSPHERE_USERNAME + valueFrom: + secretKeyRef: + name: {{ $vsphereSecretName }} + key: vsphere_username + - name: VSPHERE_PASSWORD + valueFrom: + secretKeyRef: + name: {{ $vsphereSecretName }} + key: vsphere_password +{{- end }} +{{- end }} + - name: VERSION + valueFrom: + configMapKeyRef: + name: k10-config + key: version + - name: {{ include "k10.fluentbitEndpointEnvVar" . }} + valueFrom: + configMapKeyRef: + name: k10-config + key: fluentbitEndpoint + optional: true +{{- if .Values.clusterName }} + - name: CLUSTER_NAME + valueFrom: + configMapKeyRef: + name: k10-config + key: clustername +{{- end }} +{{- if .Values.fips.enabled }} + {{- include "k10.enforceFIPSEnvironmentVariables" . | indent 10 }} +{{- end }} + {{- with $capabilities := include "k10.capabilities" . }} + - name: K10_CAPABILITIES + value: {{ $capabilities | quote }} + {{- end }} + {{- with $capabilities_mask := include "k10.capabilities_mask" . }} + - name: K10_CAPABILITIES_MASK + value: {{ $capabilities_mask | quote }} + {{- end }} + - name: K10_HOST_SVC + value: {{ $pod }} +{{- if eq $service "auth" }} + - name: PREFIX_PATH + value: {{ include "k10.prefixPath" . }} +{{- end }} +{{- if eq $service "controllermanager" }} + - name: K10_STATEFUL + value: "{{ .Values.global.persistence.enabled }}" +{{- if .Values.workerPodCRDs.resourcesRequests.maxMemory }} + - name: EPHEMERAL_POD_MAX_MEMORY_REQUESTS + valueFrom: + configMapKeyRef: + name: k10-config + key: workerPodMaxMemoryRequest +{{- end }} +{{- if .Values.workerPodCRDs.resourcesRequests.maxCPU }} + - name: EPHEMERAL_POD_MAX_CPU_REQUESTS + valueFrom: + configMapKeyRef: + name: k10-config + key: workerPodMaxCPURequest +{{- end }} +{{- end }} +{{- if eq $service "executor" }} + - name: KUBEVIRT_VM_UNFREEZE_TIMEOUT + valueFrom: + configMapKeyRef: + name: k10-config + key: kubeVirtVMsUnFreezeTimeout +{{- end }} +{{- if eq $service "executor" }} + - name: QUICK_DISASTER_RECOVERY_ENABLED + valueFrom: + configMapKeyRef: + name: k10-config + key: quickDisasterRecoveryEnabled +{{- end }} +{{- if or (eq $service "executor") (eq $service "controllermanager") (eq $service "aggregatedapis") }} +{{- if or .Values.global.imagePullSecret (or .Values.secrets.dockerConfig .Values.secrets.dockerConfigPath) }} + - name: IMAGE_PULL_SECRET_NAMES + value: {{ (trimSuffix " " (include "k10.imagePullSecretNames" .)) | toJson }} +{{- end }} +{{- end }} + - name: MODEL_STORE_DIR +{{- if or (eq $service "state") (not .Values.global.persistence.enabled) }} + value: "/tmp/k10store" +{{- else }} + valueFrom: + configMapKeyRef: + name: k10-config + key: modelstoredirname +{{- end }} +{{- if or (eq $service "kanister") (eq $service "executor")}} + - name: DATA_MOVER_IMAGE + value: {{ include "get.datamoverImage" . }} +{{- end }} +{{- if or (eq $service "kanister") (eq $service "executor")}} + - name: DATA_STORE_LOG_LEVEL + valueFrom: + configMapKeyRef: + name: k10-config + key: DataStoreLogLevel + - name: DATA_STORE_FILE_LOG_LEVEL + valueFrom: + configMapKeyRef: + name: k10-config + key: DataStoreFileLogLevel +{{- end }} + - name: LOG_LEVEL + valueFrom: + configMapKeyRef: + name: k10-config + key: loglevel +{{- if .Values.kanisterPodCustomLabels }} + - name: KANISTER_POD_CUSTOM_LABELS + valueFrom: + configMapKeyRef: + name: k10-config + key: KanisterPodCustomLabels +{{- end }} +{{- if .Values.kanisterPodCustomAnnotations }} + - name: KANISTER_POD_CUSTOM_ANNOTATIONS + valueFrom: + configMapKeyRef: + name: k10-config + key: KanisterPodCustomAnnotations +{{- end }} + - name: POD_NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace + - name: K10_LIMITER_SNAPSHOT_EXPORTS_PER_ACTION + valueFrom: + configMapKeyRef: + name: k10-config + key: K10LimiterSnapshotExportsPerAction + - name: K10_LIMITER_WORKLOAD_SNAPSHOTS_PER_ACTION + valueFrom: + configMapKeyRef: + name: k10-config + key: K10LimiterWorkloadSnapshotsPerAction + - name: K10_DATA_STORE_PARALLEL_UPLOAD + valueFrom: + configMapKeyRef: + name: k10-config + key: k10DataStoreParallelUpload + - name: K10_DATA_STORE_PARALLEL_DOWNLOAD + valueFrom: + configMapKeyRef: + name: k10-config + key: k10DataStoreParallelDownload + - name: K10_DATA_STORE_PARALLEL_BLOCK_UPLOAD + valueFrom: + configMapKeyRef: + name: k10-config + key: k10DataStoreParallelBlockUpload + - name: K10_DATA_STORE_PARALLEL_BLOCK_DOWNLOAD + valueFrom: + configMapKeyRef: + name: k10-config + key: k10DataStoreParallelBlockDownload + - name: K10_DATA_STORE_ESTIMATION_TYPE + valueFrom: + configMapKeyRef: + name: k10-config + key: k10DataStoreEstimationType + - name: K10_DATA_STORE_ADAPTIVE_ESTIMATION_THRESHOLD + valueFrom: + configMapKeyRef: + name: k10-config + key: k10DataStoreAdaptiveEstimationThreshold + - name: K10_DATA_STORE_GENERAL_CONTENT_CACHE_SIZE_MB + valueFrom: + configMapKeyRef: + name: k10-config + key: k10DataStoreGeneralContentCacheSizeMB + - name: K10_DATA_STORE_GENERAL_METADATA_CACHE_SIZE_MB + valueFrom: + configMapKeyRef: + name: k10-config + key: k10DataStoreGeneralMetadataCacheSizeMB + - name: K10_DATA_STORE_TOTAL_CACHE_SIZE_LIMIT_MB + valueFrom: + configMapKeyRef: + name: k10-config + key: k10DataStoreTotalCacheSizeLimitMB + - name: K10_DATA_STORE_RESTORE_CONTENT_CACHE_SIZE_MB + valueFrom: + configMapKeyRef: + name: k10-config + key: k10DataStoreRestoreContentCacheSizeMB + - name: K10_DATA_STORE_RESTORE_METADATA_CACHE_SIZE_MB + valueFrom: + configMapKeyRef: + name: k10-config + key: k10DataStoreRestoreMetadataCacheSizeMB + - name: K10_LIMITER_GENERIC_VOLUME_BACKUPS_PER_CLUSTER + valueFrom: + configMapKeyRef: + name: k10-config + key: K10LimiterGenericVolumeBackupsPerCluster + - name: K10_LIMITER_SNAPSHOT_EXPORTS_PER_CLUSTER + valueFrom: + configMapKeyRef: + name: k10-config + key: K10LimiterSnapshotExportsPerCluster + - name: K10_LIMITER_VOLUME_RESTORES_PER_CLUSTER + valueFrom: + configMapKeyRef: + name: k10-config + key: K10LimiterVolumeRestoresPerCluster + - name: K10_LIMITER_CSI_SNAPSHOTS_PER_CLUSTER + valueFrom: + configMapKeyRef: + name: k10-config + key: K10LimiterCsiSnapshotsPerCluster + - name: K10_LIMITER_DIRECT_SNAPSHOTS_PER_CLUSTER + valueFrom: + configMapKeyRef: + name: k10-config + key: K10LimiterDirectSnapshotsPerCluster + - name: K10_LIMITER_IMAGE_COPIES_PER_CLUSTER + valueFrom: + configMapKeyRef: + name: k10-config + key: K10LimiterImageCopiesPerCluster + - name: K10_EPHEMERAL_PVC_OVERHEAD + valueFrom: + configMapKeyRef: + name: k10-config + key: K10EphemeralPVCOverhead + - name: K10_PERSISTENCE_STORAGE_CLASS + valueFrom: + configMapKeyRef: + name: k10-config + key: K10PersistenceStorageClass + - name: AWS_ASSUME_ROLE_DURATION + valueFrom: + configMapKeyRef: + name: k10-config + key: AWSAssumeRoleDuration +{{- if (list "kanister" "executor" "repositories" | has $service) }} + - name: K10_DATA_STORE_DISABLE_COMPRESSION + valueFrom: + configMapKeyRef: + name: k10-config + key: k10DataStoreDisableCompression + + - name: K10_KANISTER_POD_METRICS_IMAGE + value: {{ include "get.metricSidecarImage" . }} + + - name: TIMEOUT_WORKER_POD_READY + valueFrom: + configMapKeyRef: + name: k10-config + key: K10TimeoutWorkerPodReady + + - name: WORKER_POD_METRIC_SIDECAR_ENABLED + valueFrom: + configMapKeyRef: + name: k10-config + key: WorkerPodMetricSidecarEnabled + - name: WORKER_POD_METRIC_SIDECAR_METRICS_INTERVAL + valueFrom: + configMapKeyRef: + name: k10-config + key: WorkerPodPushgatewayMetricsInterval + {{- if or (not (quote .Values.workerPodMetricSidecar.resources.requests.memory | empty)) (not (quote .Values.kanisterPodMetricSidecar.resources.requests.memory | empty)) }} + - name: K10_WORKER_POD_METRIC_SIDECAR_MEMORY_REQUEST + valueFrom: + configMapKeyRef: + name: k10-config + key: WorkerPodMetricSidecarMemoryRequest + {{- end }} + {{- if or (not (quote .Values.workerPodMetricSidecar.resources.requests.cpu | empty)) (not (quote .Values.kanisterPodMetricSidecar.resources.requests.cpu | empty)) }} + - name: K10_WORKER_POD_METRIC_SIDECAR_CPU_REQUEST + valueFrom: + configMapKeyRef: + name: k10-config + key: WorkerPodMetricSidecarCPURequest + {{- end }} + {{- if or (not (quote .Values.workerPodMetricSidecar.resources.limits.memory | empty)) (not (quote .Values.kanisterPodMetricSidecar.resources.limits.memory | empty)) }} + - name: K10_WORKER_POD_METRIC_SIDECAR_MEMORY_LIMIT + valueFrom: + configMapKeyRef: + name: k10-config + key: WorkerPodMetricSidecarMemoryLimit + {{- end }} + {{- if or (not (quote .Values.workerPodMetricSidecar.resources.limits.cpu | empty)) (not (quote .Values.kanisterPodMetricSidecar.resources.limits.cpu | empty)) }} + - name: K10_WORKER_POD_METRIC_SIDECAR_CPU_LIMIT + valueFrom: + configMapKeyRef: + name: k10-config + key: WorkerPodMetricSidecarCPULimit + {{- end }} + {{- if .Values.scc.create }} + - name: {{ include "k10.sccNameEnvVar" . }} + value: {{ .Release.Name }}-scc + {{- end }} + {{- if .Values.workerPodCRDs.enabled }} + - name: K10_EPHEMERAL_POD_SPECS_CR_ENABLED + valueFrom: + configMapKeyRef: + name: k10-config + key: workerPodResourcesCRDEnabled + {{- if .Values.workerPodCRDs.defaultActionPodSpec }} + - name: K10_DEFAULT_ACTION_POD_SPEC_NAME + valueFrom: + configMapKeyRef: + name: k10-config + key: workerPodDefaultAPSName + {{- end }} + {{- end }} +{{- end }} +{{- if (list "kanister" "executor" "repositories" "crypto" "dashboardbff" "aggregatedapis" | has $service) }} + {{- if .Values.global.podLabels }} + - name: K10_CUSTOM_POD_LABELS + valueFrom: + configMapKeyRef: + name: k10-config + key: K10CustomPodLabels + {{- end }} + {{- if .Values.global.podAnnotations }} + - name: K10_CUSTOM_POD_ANNOTATIONS + valueFrom: + configMapKeyRef: + name: k10-config + key: K10CustomPodAnnotations + {{- end }} +{{- end }} +{{- if (list "dashboardbff" "catalog" "executor" "crypto" | has $service) }} + {{- if .Values.metering.mode }} + - name: K10REPORTMODE + value: {{ .Values.metering.mode }} + {{- end }} +{{- end }} +{{- if eq $service "garbagecollector" }} + - name: K10_GC_DAEMON_PERIOD + valueFrom: + configMapKeyRef: + name: k10-config + key: K10GCDaemonPeriod + - name: K10_GC_KEEP_MAX_ACTIONS + valueFrom: + configMapKeyRef: + name: k10-config + key: K10GCKeepMaxActions + - name: K10_GC_ACTIONS_ENABLED + valueFrom: + configMapKeyRef: + name: k10-config + key: K10GCActionsEnabled +{{- end }} +{{- if (eq $service "executor") }} + - name: K10_LIMITER_EXECUTOR_THREADS + valueFrom: + configMapKeyRef: + name: k10-config + key: K10LimiterExecutorThreads + - name: K10_LIMITER_CSI_SNAPSHOT_RESTORES_PER_ACTION + valueFrom: + configMapKeyRef: + name: k10-config + key: K10LimiterCsiSnapshotRestoresPerAction + - name: K10_LIMITER_VOLUME_RESTORES_PER_ACTION + valueFrom: + configMapKeyRef: + name: k10-config + key: K10LimiterVolumeRestoresPerAction + - name: K10_LIMITER_WORKLOAD_RESTORES_PER_ACTION + valueFrom: + configMapKeyRef: + name: k10-config + key: K10LimiterWorkloadRestoresPerAction + - name: K10_TIMEOUT_BLUEPRINT_BACKUP + valueFrom: + configMapKeyRef: + name: k10-config + key: K10TimeoutBlueprintBackup + - name: K10_TIMEOUT_BLUEPRINT_RESTORE + valueFrom: + configMapKeyRef: + name: k10-config + key: K10TimeoutBlueprintRestore + - name: K10_TIMEOUT_BLUEPRINT_DELETE + valueFrom: + configMapKeyRef: + name: k10-config + key: K10TimeoutBlueprintDelete + - name: K10_TIMEOUT_BLUEPRINT_HOOKS + valueFrom: + configMapKeyRef: + name: k10-config + key: K10TimeoutBlueprintHooks + - name: K10_TIMEOUT_CHECK_REPO_POD_READY + valueFrom: + configMapKeyRef: + name: k10-config + key: K10TimeoutCheckRepoPodReady + - name: K10_TIMEOUT_STATS_POD_READY + valueFrom: + configMapKeyRef: + name: k10-config + key: K10TimeoutStatsPodReady + - name: K10_TIMEOUT_EFS_RESTORE_POD_READY + valueFrom: + configMapKeyRef: + name: k10-config + key: K10TimeoutEFSRestorePodReady + - name: KANISTER_MANAGED_DATA_SERVICES_BLUEPRINTS_ENABLED + valueFrom: + configMapKeyRef: + name: k10-config + key: KanisterManagedDataServicesBlueprintsEnabled +{{- if or .Values.maxJobWaitDuration .Values.timeout.jobWait }} + - name: K10_TIMEOUT_JOB_WAIT + valueFrom: + configMapKeyRef: + name: k10-config + key: K10TimeoutJobWait +{{- end }} + - name: K10_FORCE_ROOT_IN_BLUEPRINT_ACTIONS + valueFrom: + configMapKeyRef: + name: k10-config + key: K10ForceRootInBlueprintActions +{{- end }} +{{- if and (eq $service "executor") (.Values.awsConfig.efsBackupVaultName) }} + - name: EFS_BACKUP_VAULT_NAME + valueFrom: + configMapKeyRef: + name: k10-config + key: efsBackupVaultName +{{- end }} +{{- if and (eq $service "executor") (.Values.genericStorageBackup.token) }} + - name: K10_GVS_ACTIVATION_TOKEN + valueFrom: + configMapKeyRef: + name: k10-config + key: GVSActivationToken +{{- end }} +{{- if and (eq $service "executor") (.Values.genericStorageBackup.overridepubkey) }} + - name: OVERRIDE_GVS_TOKEN_VERIFICATION_KEY + valueFrom: + configMapKeyRef: + name: k10-config + key: overridePublicKeyForGVS +{{- end }} +{{- if and (eq $service "executor") (.Values.vmWare.taskTimeoutMin) }} + - name: VMWARE_GOM_TIMEOUT_MIN + valueFrom: + configMapKeyRef: + name: k10-config + key: vmWareTaskTimeoutMin +{{- end }} +{{- if .Values.useNamespacedAPI }} + - name: K10_API_DOMAIN + valueFrom: + configMapKeyRef: + name: k10-config + key: apiDomain +{{- end }} +{{- if .Values.jaeger.enabled }} + - name: JAEGER_AGENT_HOST + value: {{ .Values.jaeger.agentDNS }} +{{- end }} +{{- if .Values.auth.tokenAuth.enabled }} + - name: TOKEN_AUTH + valueFrom: + secretKeyRef: + name: k10-token-auth + key: auth +{{- end }} + - name: KANISTER_TOOLS + valueFrom: + configMapKeyRef: + name: k10-config + key: KanisterToolsImage +{{- with (include "k10.cacertconfigmapname" .) }} + - name: CACERT_CONFIGMAP_NAME + value: {{ . }} +{{- end }} + - name: K10_RELEASE_NAME + value: {{ .Release.Name }} + - name: KANISTER_FUNCTION_VERSION + valueFrom: + configMapKeyRef: + name: k10-config + key: kanisterFunctionVersion +{{- if and (eq $service "controllermanager") (or .Values.injectGenericVolumeBackupSidecar.enabled .Values.injectKanisterSidecar.enabled) }} + - name: K10_MUTATING_WEBHOOK_ENABLED + value: "true" + - name: K10_MUTATING_WEBHOOK_TLS_CERT_DIR + valueFrom: + configMapKeyRef: + name: k10-config + key: K10MutatingWebhookTLSCertDir + - name: K10_MUTATING_WEBHOOK_PORT + valueFrom: + configMapKeyRef: + name: k10-config + key: K10MutatingWebhookPort +{{- end }} +{{- if (list "controllermanager" "kanister" "executor" "dashboardbff" "repositories" | has $service) }} + - name: K10_DEFAULT_PRIORITY_CLASS_NAME + valueFrom: + configMapKeyRef: + name: k10-config + key: K10DefaultPriorityClassName +{{- if .Values.genericVolumeSnapshot.resources.requests.memory }} + - name: KANISTER_TOOLS_MEMORY_REQUESTS + valueFrom: + configMapKeyRef: + name: k10-config + key: KanisterToolsMemoryRequests +{{- end }} +{{- if .Values.genericVolumeSnapshot.resources.requests.cpu }} + - name: KANISTER_TOOLS_CPU_REQUESTS + valueFrom: + configMapKeyRef: + name: k10-config + key: KanisterToolsCPURequests +{{- end }} +{{- if .Values.genericVolumeSnapshot.resources.limits.memory }} + - name: KANISTER_TOOLS_MEMORY_LIMITS + valueFrom: + configMapKeyRef: + name: k10-config + key: KanisterToolsMemoryLimits +{{- end }} +{{- if .Values.genericVolumeSnapshot.resources.limits.cpu }} + - name: KANISTER_TOOLS_CPU_LIMITS + valueFrom: + configMapKeyRef: + name: k10-config + key: KanisterToolsCPULimits +{{- end }} +{{- end }} +{{- if (list "dashboardbff" "controllermanager" "executor" | has $service) }} + {{- if .Values.prometheus.server.enabled }} + - name: K10_PROMETHEUS_HOST + value: {{ include "k10.prometheus.service.name" . }}-exp + - name: K10_PROMETHEUS_PORT + value: {{ .Values.prometheus.server.service.servicePort | quote }} + - name: K10_PROMETHEUS_BASE_URL + value: {{ .Values.prometheus.server.baseURL }} + {{- else -}} + {{- if and .Values.global.prometheus.external.host .Values.global.prometheus.external.port}} + - name: K10_PROMETHEUS_HOST + value: {{ .Values.global.prometheus.external.host }} + - name: K10_PROMETHEUS_PORT + value: {{ .Values.global.prometheus.external.port | quote }} + - name: K10_PROMETHEUS_BASE_URL + value: {{ .Values.global.prometheus.external.baseURL }} + {{- end -}} + {{- end }} + {{- if (.Values.grafana.external.url) }} + - name: GRAFANA_URL + value: {{ include "k10.grafanaUrl" . }} + {{- end }} +{{- end }} +{{- if eq $service "dashboardbff" }} + {{- if ne .Values.global.persistence.diskSpaceAlertPercent nil }} + - name: K10_DISK_SPACE_ALERT_PERCENT + value: {{ .Values.global.persistence.diskSpaceAlertPercent | quote }} + {{- end -}} +{{- end -}} +{{- if eq $service "controllermanager" }} + {{- if .Values.multicluster.primary.create }} + {{- if not .Values.multicluster.enabled }} + {{- fail "Cannot setup cluster as primary without enabling feature with multicluster.enabled=true" -}} + {{- end }} + {{- if not .Values.multicluster.primary.name }} + {{- fail "Cannot setup cluster as primary without setting cluster name with multicluster.primary.name" -}} + {{- end }} + {{- if not .Values.multicluster.primary.ingressURL }} + {{- fail "Cannot setup cluster as primary without providing an ingress with multicluster.primary.ingressURL" -}} + {{- end }} + - name: K10_MC_CREATE_PRIMARY + value: "true" + - name: K10_MC_PRIMARY_NAME + value: {{ .Values.multicluster.primary.name | quote }} + - name: K10_MC_PRIMARY_INGRESS_URL + value: {{ .Values.multicluster.primary.ingressURL | quote }} + {{- end }} +{{- end -}} +{{- if or $.stateful (or (eq (include "check.googleCredsOrSecret" .) "true") (eq $service "auth" "logging")) }} + volumeMounts: +{{- else if or (or (eq (include "basicauth.check" .) "true") (or .Values.auth.oidcAuth.enabled (eq (include "check.dexAuth" .) "true"))) .Values.features }} + volumeMounts: +{{- else if and (eq $service "controllermanager") (or .Values.injectGenericVolumeBackupSidecar.enabled .Values.injectKanisterSidecar.enabled) }} + volumeMounts: +{{- else if or (eq (include "check.cacertconfigmap" .) "true") (include "k10.ocpcacertsautoextraction" .) }} + volumeMounts: +{{- else if eq $service "frontend" }} + volumeMounts: +{{- else if and (list "controllermanager" "executor" | has $pod) (eq (include "check.projectSAToken" .) "true")}} + volumeMounts: +{{- else if and (eq $service "aggregatedapis") (include "k10.siemEnabled" .) }} + volumeMounts: +{{- end }} +{{- if $.stateful }} + - name: {{ $service }}-persistent-storage + mountPath: {{ .Values.global.persistence.mountPath | quote }} +{{- end }} +{{- if .Values.features }} + - name: k10-features + mountPath: "/mnt/k10-features" +{{- end }} +{{- if eq $service "logging" }} + - name: logging-configmap-storage + mountPath: "/mnt/conf" +{{- end }} +{{- if eq $service "executor" }} + - name: datastore-cache-vol + mountPath: "/tmp" +{{- end }} +{{- if and (eq $service "controllermanager") (or .Values.injectGenericVolumeBackupSidecar.enabled .Values.injectKanisterSidecar.enabled) }} + - name: mutating-webhook-certs + mountPath: /etc/ssl/certs/webhook + readOnly: true +{{- end }} +{{- if list "dashboardbff" "auth" "controllermanager" | has $service}} +{{- if eq (include "basicauth.check" .) "true" }} + - name: k10-basic-auth + mountPath: "/var/run/secrets/kasten.io/k10-basic-auth" + readOnly: true +{{- end }} +{{- if (or .Values.auth.oidcAuth.enabled (eq (include "check.dexAuth" .) "true")) }} + - name: {{ include "k10.oidcSecretName" .}} + mountPath: {{ printf "%s/%s" (include "k10.secretsDir" .) (include "k10.oidcSecretName" .) }} + readOnly: true +{{- if .Values.auth.oidcAuth.clientSecretName }} + - name: {{ include "k10.oidcCustomerSecretName" .}} + mountPath: {{ printf "%s/%s" (include "k10.secretsDir" .) (include "k10.oidcCustomerSecretName" .) }} + readOnly: true +{{- end }} +{{- end }} +{{- end }} +{{- if eq (include "check.googleCredsOrSecret" .) "true"}} + - name: service-account + mountPath: {{ include "k10.secretsDir" .}} +{{- end }} +{{- if and (list "controllermanager" "executor" | has $pod) (eq (include "check.projectSAToken" .) "true")}} + - name: bound-sa-token + mountPath: "/var/run/secrets/kasten.io/serviceaccount/GWIF" + readOnly: true +{{- end }} +{{- with (include "k10.cacertconfigmapname" .) }} + - name: {{ . }} + mountPath: "/etc/ssl/certs/custom-ca-bundle.pem" + subPath: custom-ca-bundle.pem +{{- end }} +{{- if eq $service "frontend" }} + - name: frontend-config + mountPath: /etc/nginx/nginx.conf + subPath: nginx.conf + readOnly: true + - name: frontend-config + mountPath: /etc/nginx/conf.d/frontend.conf + subPath: frontend.conf + readOnly: true +{{- end}} +{{- if and (eq $service "aggregatedapis") (include "k10.siemEnabled" .) }} + - name: aggauditpolicy-config + mountPath: /etc/kubernetes/{{ include "k10.aggAuditPolicyFile" .}} + subPath: {{ include "k10.aggAuditPolicyFile" .}} + readOnly: true +{{- end}} +{{- if and (eq $service "catalog") $.stateful }} + - name: kanister-sidecar + image: {{ include "get.kanisterToolsImage" .}} + imagePullPolicy: {{ .Values.kanisterToolsImage.pullPolicy }} +{{- dict "main" . "k10_service_pod_name" $podName "k10_service_container_name" "kanister-sidecar" | include "k10.resource.request" | indent 8}} + env: + {{- with $capabilities := include "k10.capabilities" . }} + - name: K10_CAPABILITIES + value: {{ $capabilities | quote }} + {{- end }} + {{- with $capabilities_mask := include "k10.capabilities_mask" . }} + - name: K10_CAPABILITIES_MASK + value: {{ $capabilities_mask | quote }} + {{- end }} +{{- if .Values.fips.enabled }} + {{- include "k10.enforceFIPSEnvironmentVariables" . | nindent 10 }} +{{- end }} + volumeMounts: + - name: {{ $service }}-persistent-storage + mountPath: {{ .Values.global.persistence.mountPath | quote }} +{{- with (include "k10.cacertconfigmapname" .) }} + - name: {{ . }} + mountPath: "/etc/ssl/certs/custom-ca-bundle.pem" + subPath: custom-ca-bundle.pem +{{- end }} +{{- if eq (include "check.projectSAToken" .) "true" }} + - name: bound-sa-token + mountPath: "/var/run/secrets/kasten.io/serviceaccount/GWIF" + readOnly: true +{{- end }} +{{- end }} {{/* and (eq $service "catalog") $.stateful */}} +{{- if and ( eq $service "auth" ) ( eq (include "check.dexAuth" .) "true" ) }} + - name: dex + image: {{ include "get.dexImage" . }} +{{- if .Values.auth.ldap.enabled }} + command: ["/usr/local/bin/dex", "serve", "/dex-config/config.yaml"] +{{- if .Values.fips.enabled }} + env: + {{- include "k10.enforceFIPSEnvironmentVariables" . | nindent 10 }} +{{- end }} +{{- else if .Values.auth.openshift.enabled }} + {{- /* + In the case of OpenShift, a template config is used instead of a plain config for Dex. + It requires a different command to be processed correctly. + */}} + command: ["/usr/local/bin/docker-entrypoint", "dex", "serve", "/etc/dex/cfg/config.yaml"] + env: + - name: {{ include "k10.openShiftClientSecretEnvVar" . }} +{{- if and (not .Values.auth.openshift.clientSecretName) (not .Values.auth.openshift.clientSecret) }} + valueFrom: + secretKeyRef: + name: {{ include "get.openshiftServiceAccountSecretName" . }} + key: token +{{- else if .Values.auth.openshift.clientSecretName }} + valueFrom: + secretKeyRef: + name: {{ .Values.auth.openshift.clientSecretName }} + key: token +{{- else }} + value: {{ .Values.auth.openshift.clientSecret }} +{{- end }} +{{- if .Values.fips.enabled }} + {{- include "k10.enforceFIPSEnvironmentVariables" . | indent 10 }} +{{- end }} +{{- end }} + ports: + - name: http + containerPort: 8080 + volumeMounts: +{{- if .Values.auth.ldap.enabled }} + - name: dex-config + mountPath: /dex-config + - name: k10-logos-dex + mountPath: {{ include "k10.dexFrontendDir" . }}/themes/custom/ +{{- else }} + - name: config + mountPath: /etc/dex/cfg +{{- end }} +{{- with (include "k10.cacertconfigmapname" .) }} + - name: {{ . }} + mountPath: "/etc/ssl/certs/custom-ca-bundle.pem" + subPath: custom-ca-bundle.pem +{{- end }} +{{- end }} {{/* end of dex check */}} +{{- end }}{{/* with .main */}} +{{- end }}{{/* define "k10-container" */}} + +{{- define "k10-init-container-header" }} +{{- $pod := .k10_pod }} +{{- with .main }} +{{- $main_context := . }} +{{- $containerList := (dict "main" $main_context "k10_service_pod" $pod | include "get.serviceContainersInPod" | splitList " ") }} +{{- $needsInitContainersHeader := false }} +{{- range $skip, $service := $containerList }} +{{- $serviceStateful := has $service (dict "main" $main_context "k10_service_pod" $pod | include "get.statefulRestServicesInPod" | splitList " ") }} + {{- if and ( eq $service "auth" ) $main_context.Values.auth.ldap.enabled }} + {{- $needsInitContainersHeader = true }} + {{- else if $serviceStateful }} + {{- $needsInitContainersHeader = true }} + {{- end }}{{/* initContainers header needed check */}} +{{- end }}{{/* range $skip, $service := $containerList */}} +{{- if $needsInitContainersHeader }} + initContainers: +{{- end }} +{{- end }}{{/* with .main */}} +{{- end }}{{/* define "k10-init-container-header" */}} + +{{- define "k10-init-container" }} +{{- $pod := .k10_pod }} +{{- $podName := (printf "%s-svc" $pod) }} +{{- with .main }} +{{- $main_context := . }} +{{- $containerList := (dict "main" $main_context "k10_service_pod" $pod | include "get.serviceContainersInPod" | splitList " ") }} +{{- range $skip, $service := $containerList }} +{{- $serviceStateful := has $service (dict "main" $main_context "k10_service_pod" $pod | include "get.statefulRestServicesInPod" | splitList " ") }} +{{- if and ( eq $service "auth" ) $main_context.Values.auth.ldap.enabled }} + - name: dex-init + command: + - /dex/dexconfigmerge + args: + - --config-path=/etc/dex/cfg/config.yaml + - --secret-path=/var/run/secrets/kasten.io/bind-secret/bindPW + - --new-config-path=/dex-config/config.yaml + - --secret-field=bindPW + {{- dict "main" $main_context "k10_service" $service | include "serviceImage" | indent 8 }} + {{- dict "main" $main_context "k10_service_pod_name" $podName "k10_service_container_name" "dex-init" | include "k10.resource.request" | indent 8}} + volumeMounts: + - mountPath: /etc/dex/cfg + name: config + - mountPath: /dex-config + name: dex-config + - name: bind-secret + mountPath: "/var/run/secrets/kasten.io/bind-secret" + readOnly: true +{{- else if $serviceStateful }} + - name: upgrade-init + securityContext: + capabilities: + add: + - FOWNER + - CHOWN + runAsUser: 1000 + allowPrivilegeEscalation: false + {{- dict "main" $main_context "k10_service" "upgrade" | include "serviceImage" | indent 8 }} + imagePullPolicy: {{ $main_context.Values.global.image.pullPolicy }} + {{- dict "main" $main_context "k10_service_pod_name" $podName "k10_service_container_name" "upgrade-init" | include "k10.resource.request" | indent 8}} + env: + - name: MODEL_STORE_DIR + valueFrom: + configMapKeyRef: + name: k10-config + key: modelstoredirname + volumeMounts: + - name: {{ $service }}-persistent-storage + mountPath: {{ $main_context.Values.global.persistence.mountPath | quote }} +{{- if eq $service "catalog" }} + - name: schema-upgrade-check + {{- dict "main" $main_context "k10_service" $service | include "serviceImage" | indent 8 }} + imagePullPolicy: {{ $main_context.Values.global.image.pullPolicy }} + {{- dict "main" $main_context "k10_service_pod_name" $podName "k10_service_container_name" "schema-upgrade-check" | include "k10.resource.request" | indent 8}} + env: +{{- if $main_context.Values.clusterName }} + - name: CLUSTER_NAME + valueFrom: + configMapKeyRef: + name: k10-config + key: clustername +{{- end }} + - name: INIT_CONTAINER + value: "true" + - name: K10_RELEASE_NAME + value: {{ $main_context.Release.Name }} + - name: LOG_LEVEL + valueFrom: + configMapKeyRef: + name: k10-config + key: loglevel + - name: MODEL_STORE_DIR + valueFrom: + configMapKeyRef: + name: k10-config + key: modelstoredirname + - name: POD_NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace + - name: VERSION + valueFrom: + configMapKeyRef: + name: k10-config + key: version + volumeMounts: + - name: {{ $service }}-persistent-storage + mountPath: {{ $main_context.Values.global.persistence.mountPath | quote }} +{{- end }}{{/* eq $service "catalog" */}} +{{- end }}{{/* initContainers definitions */}} +{{- end }}{{/* range $skip, $service := $containerList */}} +{{- end }}{{/* with .main */}} +{{- end }}{{/* define "k10-init-container" */}} diff --git a/charts/kasten/k10/7.5.401/templates/_k10_image_tag.tpl b/charts/kasten/k10/7.5.401/templates/_k10_image_tag.tpl new file mode 100644 index 0000000000..560b678604 --- /dev/null +++ b/charts/kasten/k10/7.5.401/templates/_k10_image_tag.tpl @@ -0,0 +1 @@ +{{- define "k10.imageTag" -}}7.5.4{{- end -}} \ No newline at end of file diff --git a/charts/kasten/k10/7.5.401/templates/_k10_metering.tpl b/charts/kasten/k10/7.5.401/templates/_k10_metering.tpl new file mode 100644 index 0000000000..6dfb6a4165 --- /dev/null +++ b/charts/kasten/k10/7.5.401/templates/_k10_metering.tpl @@ -0,0 +1,352 @@ +{{/* Generate service spec */}} +{{/* because of https://github.com/GoogleCloudPlatform/marketplace-k8s-app-tools/issues/165 +we have to start using .Values.reportingSecret instead +of correct version .Values.metering.reportingSecret */}} +{{- define "k10-metering" }} +{{ $service := .k10_service }} +{{- $podName := (printf "%s-svc" $service) }} +{{ $main := .main }} +{{- with .main }} +{{- $servicePort := .Values.service.externalPort -}} +{{- $optionalServices := .Values.optionalColocatedServices -}} +{{- $rbac := .Values.prometheus.rbac.create -}} +{{- if $.stateful }} +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + namespace: {{ .Release.Namespace }} + name: {{ $service }}-pv-claim + labels: +{{ include "helm.labels" . | indent 4 }} + component: {{ $service }} +spec: + accessModes: + - ReadWriteOnce + resources: + requests: + storage: {{ default .Values.global.persistence.size (index .Values.global.persistence $service "size") }} +{{- if .Values.global.persistence.storageClass }} + {{- if (eq "-" .Values.global.persistence.storageClass) }} + storageClassName: "" + {{- else }} + storageClassName: "{{ .Values.global.persistence.storageClass }}" + {{- end }} +{{- end }} +--- +{{- end }}{{/* if $.stateful */}} +{{ $service_list := include "get.enabledRestServices" . | splitList " " }} +kind: ConfigMap +apiVersion: v1 +metadata: + labels: +{{ include "helm.labels" . | indent 4 }} + namespace: {{ .Release.Namespace }} + name: {{ include "fullname" . }}-metering-config +data: + config: | +{{- if .Values.metering.reportingKey }} + identities: + - name: gcp + gcp: + encodedServiceAccountKey: {{ .Values.metering.reportingKey }} +{{- end }} + metrics: + - name: node_time + type: int + passthrough: {} + endpoints: + - name: on_disk +{{- if .Values.metering.reportingKey }} + - name: servicecontrol +{{- end }} + endpoints: + - name: on_disk + disk: +{{- if .Values.global.persistence.enabled }} + reportDir: /var/reports/ubbagent/reports +{{- else }} + reportDir: /tmp/reports/ubbagent/reports +{{- end }} + expireSeconds: 3600 +{{- if .Values.metering.reportingKey }} + - name: servicecontrol + servicecontrol: + identity: gcp + serviceName: kasten-k10.mp-kasten-public.appspot.com + consumerId: {{ .Values.metering.consumerId }} +{{- end }} + prometheusTargets: | +{{- range $service_list }} +{{- if or (not (hasKey $optionalServices .)) (index $optionalServices .).enabled }} +{{- if not (eq . "executor") }} +{{ $tmpcontx := dict "main" $main "k10service" . -}} +{{ include "k10.prometheusTargetConfig" $tmpcontx | trim | indent 4 -}} +{{- end }} +{{- end }} +{{- end }} +{{- range include "get.enabledServices" . | splitList " " }} +{{- if (or (ne . "aggregatedapis") ($rbac)) }} +{{ $tmpcontx := dict "main" $main "k10service" . -}} +{{ include "k10.prometheusTargetConfig" $tmpcontx | indent 4 -}} +{{- end }} +{{- end }} +{{- range include "get.enabledAdditionalServices" . | splitList " " }} +{{- if not (eq . "frontend") }} +{{ $tmpcontx := dict "main" $main "k10service" . -}} +{{ include "k10.prometheusTargetConfig" $tmpcontx | indent 4 -}} +{{- end }} +{{- end }} + +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + namespace: {{ .Release.Namespace }} + name: {{ $service }}-svc + labels: +{{ include "helm.labels" . | indent 4 }} + component: {{ $service }} +spec: + replicas: {{ $.replicas }} + strategy: + type: Recreate + selector: + matchLabels: +{{ include "k10.common.matchLabels" . | indent 6 }} + component: {{ $service }} + run: {{ $service }}-svc + template: + metadata: + annotations: + {{- include "k10.deploymentPodAnnotations" . | nindent 8 }} + labels: + {{- with merge (dict "requiredLabels" (dict "component" $service "run" (printf "%s-svc" $service) )) . }} + {{- include "k10.deploymentPodLabels" . | nindent 8 }} + {{- end }} + spec: + securityContext: +{{ toYaml .Values.services.securityContext | indent 8 }} + serviceAccountName: {{ template "meteringServiceAccountName" . }} + {{- dict "main" . "k10_deployment_name" $podName | include "k10.priorityClassName" | indent 6}} + {{- include "k10.imagePullSecrets" . | indent 6 }} +{{- if $.stateful }} + initContainers: + - name: upgrade-init + securityContext: + capabilities: + add: + - FOWNER + - CHOWN + runAsUser: 1000 + allowPrivilegeEscalation: false + {{- dict "main" . "k10_service" "upgrade" | include "serviceImage" | indent 8 }} + imagePullPolicy: {{ .Values.global.image.pullPolicy }} + {{- dict "main" . "k10_service_pod_name" $podName "k10_service_container_name" "upgrade-init" | include "k10.resource.request" | indent 8}} + env: + - name: MODEL_STORE_DIR + value: /var/reports/ + volumeMounts: + - name: {{ $service }}-persistent-storage + mountPath: /var/reports/ +{{- end }} + containers: + - name: {{ $service }}-svc + {{- dict "main" . "k10_service" $service | include "serviceImage" | indent 8 }} + imagePullPolicy: {{ .Values.global.image.pullPolicy }} +{{- $containerName := (printf "%s-svc" $service) }} +{{- dict "main" . "k10_service_pod_name" $podName "k10_service_container_name" $containerName | include "k10.resource.request" | indent 8}} + ports: + - containerPort: {{ .Values.service.externalPort }} + livenessProbe: + httpGet: + path: /v0/healthz + port: {{ .Values.service.externalPort }} + initialDelaySeconds: 90 + timeoutSeconds: 1 + env: + - name: VERSION + valueFrom: + configMapKeyRef: + name: k10-config + key: version + - name: {{ include "k10.fluentbitEndpointEnvVar" . }} + valueFrom: + configMapKeyRef: + name: k10-config + key: fluentbitEndpoint + optional: true + - name: KANISTER_TOOLS + valueFrom: + configMapKeyRef: + name: k10-config + key: KanisterToolsImage +{{- if .Values.clusterName }} + - name: CLUSTER_NAME + valueFrom: + configMapKeyRef: + name: k10-config + key: clustername +{{- end }} +{{- if .Values.fips.enabled }} + {{- include "k10.enforceFIPSEnvironmentVariables" . | indent 10 }} +{{- end }} + {{- with $capabilities := include "k10.capabilities" . }} + - name: K10_CAPABILITIES + value: {{ $capabilities | quote }} + {{- end }} + {{- with $capabilities_mask := include "k10.capabilities_mask" . }} + - name: K10_CAPABILITIES_MASK + value: {{ $capabilities_mask | quote }} + {{- end }} + - name: K10_HOST_SVC + value: {{ $service }} + - name: LOG_LEVEL + valueFrom: + configMapKeyRef: + name: k10-config + key: loglevel + - name: POD_NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace +{{- if .Values.useNamespacedAPI }} + - name: K10_API_DOMAIN + valueFrom: + configMapKeyRef: + name: k10-config + key: apiDomain +{{- end }} + - name: AGENT_CONFIG_FILE + value: /var/ubbagent/config.yaml + - name: AGENT_STATE_DIR +{{- if .Values.global.persistence.enabled }} + value: "/var/reports/ubbagent" +{{- else }} + value: "/tmp/reports/ubbagent" + - name: K10SYNCSTATUSDIR + value: "/tmp/reports/k10" + - name: GRACE_PERIOD_STORE + value: /tmp/reports/clustergraceperiod + - name: NODE_USAGE_STORE + value: /tmp/reports/node_usage_history +{{- end }} +{{- if .Values.metering.awsRegion }} + - name: AWS_REGION + value: {{ .Values.metering.awsRegion }} +{{- end }} +{{- if .Values.metering.mode }} + - name: K10REPORTMODE + value: {{ .Values.metering.mode }} +{{- end }} +{{- if .Values.metering.reportCollectionPeriod }} + - name: K10_REPORT_COLLECTION_PERIOD + value: {{ .Values.metering.reportCollectionPeriod | quote }} +{{- end }} +{{- if .Values.metering.reportPushPeriod }} + - name: K10_REPORT_PUSH_PERIOD + value: {{ .Values.metering.reportPushPeriod | quote }} +{{- end }} +{{- if .Values.metering.promoID }} + - name: K10_PROMOTION_ID + value: {{ .Values.metering.promoID }} +{{- end }} + +{{- if .Values.prometheus.server.enabled }} + - name: K10_PROMETHEUS_HOST + value: {{ include "k10.prometheus.service.name" . }}-exp + - name: K10_PROMETHEUS_PORT + value: {{ .Values.prometheus.server.service.servicePort | quote }} + - name: K10_PROMETHEUS_BASE_URL + value: {{ .Values.prometheus.server.baseURL }} +{{- else -}} + {{- if and .Values.global.prometheus.external.host .Values.global.prometheus.external.port}} + - name: K10_PROMETHEUS_HOST + value: {{ .Values.global.prometheus.external.host }} + - name: K10_PROMETHEUS_PORT + value: {{ .Values.global.prometheus.external.port | quote }} + - name: K10_PROMETHEUS_BASE_URL + value: {{ .Values.global.prometheus.external.baseURL }} + {{- end -}} +{{- end }} +{{- if or .Values.workerPodMetricSidecar.enabled .Values.kanisterPodMetricSidecar.enabled }} + - name: WORKER_POD_METRIC_SIDECAR_ENABLED + valueFrom: + configMapKeyRef: + name: k10-config + key: WorkerPodMetricSidecarEnabled + - name: WORKER_POD_METRIC_SIDECAR_METRIC_LIFETIME + valueFrom: + configMapKeyRef: + name: k10-config + key: WorkerPodMetricSidecarMetricLifetime + - name: WORKER_POD_METRIC_SIDECAR_METRICS_INTERVAL + valueFrom: + configMapKeyRef: + name: k10-config + key: WorkerPodPushgatewayMetricsInterval +{{- end }} +{{- if .Values.reportingSecret }} + - name: AGENT_CONSUMER_ID + valueFrom: + secretKeyRef: + name: {{ .Values.reportingSecret }} + key: consumer-id + - name: AGENT_REPORTING_KEY + valueFrom: + secretKeyRef: + name: {{ .Values.reportingSecret }} + key: reporting-key + - name: K10_RELEASE_NAME + value: {{ .Release.Name }} +{{- end }} +{{- if .Values.metering.licenseConfigSecretName }} + - name: AWS_WEB_IDENTITY_REFRESH_TOKEN_FILE + value: "/var/run/secrets/product-license/license_token" + - name: AWS_ROLE_ARN + valueFrom: + secretKeyRef: + name: {{ .Values.metering.licenseConfigSecretName }} + key: iam_role +{{- end }} + volumeMounts: + - name: meter-config + mountPath: /var/ubbagent +{{- if $.stateful }} + - name: {{ $service }}-persistent-storage + mountPath: /var/reports/ +{{- end }} +{{- if .Values.metering.licenseConfigSecretName }} + - name: awsmp-product-license + mountPath: "/var/run/secrets/product-license" +{{- end }} +{{- if .Values.features }} + - name: k10-features + mountPath: "/mnt/k10-features" +{{- end }} + volumes: + - name: meter-config + configMap: + name: {{ include "fullname" . }}-metering-config + items: + - key: config + path: config.yaml + - key: prometheusTargets + path: prometheusTargets.yaml +{{- if .Values.features }} + - name: k10-features + configMap: + name: k10-features +{{- end }} +{{- if $.stateful }} + - name: {{ $service }}-persistent-storage + persistentVolumeClaim: + claimName: {{ $service }}-pv-claim +{{- end }} +{{- if .Values.metering.licenseConfigSecretName }} + - name: awsmp-product-license + secret: + secretName: {{ .Values.metering.licenseConfigSecretName }} +{{- end }} +--- +{{- end }}{{/* with .main */}} +{{- end }}{{/* define "k10-metering" */}} diff --git a/charts/kasten/k10/7.5.401/templates/_k10_serviceimage.tpl b/charts/kasten/k10/7.5.401/templates/_k10_serviceimage.tpl new file mode 100644 index 0000000000..9a333d92ce --- /dev/null +++ b/charts/kasten/k10/7.5.401/templates/_k10_serviceimage.tpl @@ -0,0 +1,50 @@ +{{/* +Helper to get k10 service image +The details on how these image are being generated +is in below issue +https://kasten.atlassian.net/browse/K10-4036 +*/}} +{{- define "serviceImage" -}} +{{/* +we are maintaining the field .Values.global.images to override it when +we install the chart for red hat marketplace. If we dont +have the value specified use earlier flow, if it is, use the +value that is specified. +*/}} +{{- include "image.values.check" . -}} +{{- if not .main.Values.global.rhMarketPlace }} +{{- $serviceImage := "" -}} +{{- $tagFromDefs := "" -}} +{{- if .main.Values.global.airgapped.repository }} +{{- $serviceImage = (include "get.k10ImageTag" .main) | print .main.Values.global.airgapped.repository "/" .k10_service ":" }} +{{- else if .main.Values.global.azMarketPlace }} +{{- $az_image := (get .main.Values.global.azure.images .k10_service) }} +{{- $serviceImage = print $az_image.registry "/" $az_image.image ":" $az_image.tag }} +{{- else }} +{{- $serviceImage = (include "get.k10ImageTag" .main) | print .main.Values.global.image.registry "/" .k10_service ":" }} +{{- end }}{{/* if .main.Values.global.airgapped.repository */}} +{{- $serviceImageKey := print (replace "-" "" .k10_service) "Image" }} +{{- if eq $serviceImageKey "dexImage" }} +{{- $tagFromDefs = (include "dex.dexImageTag" .) }} +{{- end }}{{/* if eq $serviceImageKey "dexImage" */}} +{{- if index .main.Values $serviceImageKey }} +{{- $service_values := index .main.Values $serviceImageKey }} +{{- if .main.Values.global.airgapped.repository }} +{{ $valuesImage := (splitList "/" (index $service_values "image")) }} +{{- if $tagFromDefs }} +image: {{ printf "%s/%s:k10-%s" .main.Values.global.airgapped.repository (index $valuesImage (sub (len $valuesImage) 1) ) $tagFromDefs -}} +{{- end }} +{{- else }}{{/* .main.Values.global.airgapped.repository */}} +{{- if $tagFromDefs }} +image: {{ printf "%s:%s" (index $service_values "image") $tagFromDefs }} +{{- else }} +image: {{ index $service_values "image" }} +{{- end }} +{{- end }}{{/* .main.Values.global.airgapped.repository */}} +{{- else }} +image: {{ $serviceImage }} +{{- end -}}{{/* index .main.Values $serviceImageKey */}} +{{- else }} +image: {{ printf "%s" (get .main.Values.global.images .k10_service) }} +{{- end }}{{/* if not .main.Values.images.executor */}} +{{- end -}}{{/* define "serviceImage" */}} diff --git a/charts/kasten/k10/7.5.401/templates/_k10_template.tpl b/charts/kasten/k10/7.5.401/templates/_k10_template.tpl new file mode 100644 index 0000000000..9d0916054c --- /dev/null +++ b/charts/kasten/k10/7.5.401/templates/_k10_template.tpl @@ -0,0 +1,249 @@ +{{/* Generate service spec */}} +{{- define "k10-default" }} +{{- $service := .k10_service }} +{{- $deploymentName := (printf "%s-svc" $service) }} +{{- with .main }} +{{- $main_context := . }} +{{- range $skip, $statefulContainer := compact (dict "main" $main_context "k10_service_pod" $service | include "get.statefulRestServicesInPod" | splitList " ") }} +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + namespace: {{ $main_context.Release.Namespace }} + name: {{ $statefulContainer }}-pv-claim + labels: +{{ include "helm.labels" $main_context | indent 4 }} + component: {{ $statefulContainer }} +spec: + accessModes: + - ReadWriteOnce + resources: + requests: + storage: {{ index (index $main_context.Values.global.persistence $statefulContainer | default dict) "size" | default $main_context.Values.global.persistence.size }} +{{- if $main_context.Values.global.persistence.storageClass }} + {{- if (eq "-" $main_context.Values.global.persistence.storageClass) }} + storageClassName: "" + {{- else }} + storageClassName: "{{ $main_context.Values.global.persistence.storageClass }}" + {{- end }} +{{- end }} +--- +{{- end }}{{/* if $.stateful */}} +apiVersion: apps/v1 +kind: Deployment +metadata: + namespace: {{ .Release.Namespace }} + name: {{ $deploymentName }} + labels: +{{ include "helm.labels" . | indent 4 }} + component: {{ $service }} +spec: + replicas: {{ $.replicas }} + strategy: + type: Recreate + selector: + matchLabels: +{{ include "k10.common.matchLabels" . | indent 6 }} + component: {{ $service }} + run: {{ $deploymentName }} + template: + metadata: + annotations: +{{- +$requiredAnnotations := (dict + "run" $deploymentName + "checksum/frontend-nginx-config" (include (print .Template.BasePath "/frontend-nginx-configmap.yaml") . | sha256sum) +) +-}} +{{- if .Values.auth.ldap.restartPod -}} + {{- $_ := set $requiredAnnotations "rollme" (randAlphaNum 5) -}} +{{- end -}} + {{- with merge (dict "requiredAnnotations" $requiredAnnotations) . }} + {{- include "k10.deploymentPodAnnotations" . | nindent 8 }} + {{- end }} + labels: +{{- +$requiredLabels := (dict + "component" $service + "run" $deploymentName +) +-}} +{{- if list "executor" "controllermanager" | has $service}} + {{- if eq (include "check.azureFederatedIdentity" .) "true" }} + azure.workload.identity/use: "true" + {{- end }} +{{- end }} + {{- with merge (dict "requiredLabels" $requiredLabels) . }} + {{- include "k10.deploymentPodLabels" . | nindent 8 }} + {{- end }} + spec: +{{- if eq $service "executor" }} +{{- if .Values.services.executor.hostNetwork }} + hostNetwork: true +{{- end }}{{/* .Values.services.executor.hostNetwork */}} +{{- end }}{{/* eq $service "executor" */}} +{{- if eq $service "aggregatedapis" }} +{{- if .Values.services.aggregatedapis.hostNetwork }} + hostNetwork: true +{{- end }}{{/* .Values.services.aggregatedapis.hostNetwork */}} +{{- end }}{{/* eq $service "aggregatedapis" */}} +{{- if eq $service "dashboardbff" }} +{{- if .Values.services.dashboardbff.hostNetwork }} + hostNetwork: true +{{- end }}{{/* .Values.services.dashboardbff.hostNetwork */}} +{{- end }}{{/* eq $service "dashboardbff" */}} + securityContext: +{{ toYaml .Values.services.securityContext | indent 8 }} + serviceAccountName: {{ template "serviceAccountName" . }} + {{- dict "main" . "k10_deployment_name" $deploymentName | include "k10.priorityClassName" | indent 6}} + {{- include "k10.imagePullSecrets" . | indent 6 }} +{{- /* initContainers: */}} +{{- (dict "main" . "k10_pod" $service | include "k10-init-container-header") }} +{{- (dict "main" . "k10_pod" $service | include "k10-init-container") }} +{{- /* containers: */}} +{{- (dict "main" . "k10_pod" $service | include "k10-containers") }} +{{- /* volumes: */}} +{{- (dict "main" . "k10_pod" $service | include "k10-deployment-volumes-header") }} +{{- (dict "main" . "k10_pod" $service | include "k10-deployment-volumes") }} +--- +{{- end }}{{/* with .main */}} +{{- end }}{{/* define "k10-default" */}} + +{{- define "k10-deployment-volumes-header" }} +{{- $pod := .k10_pod }} +{{- with .main }} +{{- $main_context := . }} +{{- $containerList := (dict "main" $main_context "k10_service_pod" $pod | include "get.serviceContainersInPod" | splitList " ") }} +{{- $needsVolumesHeader := false }} +{{- range $skip, $service := $containerList }} + {{- $serviceStateful := has $service (dict "main" $main_context "k10_service_pod" $pod | include "get.statefulRestServicesInPod" | splitList " ") }} + {{- if or $serviceStateful (or (eq (include "check.googlecreds" $main_context) "true") (eq $service "auth" "logging")) }} + {{- $needsVolumesHeader = true }} + {{- else if or (or (eq (include "basicauth.check" $main_context) "true") (or $main_context.Values.auth.oidcAuth.enabled (eq (include "check.dexAuth" $main_context) "true"))) $main_context.Values.features }} + {{- $needsVolumesHeader = true }} + {{- else if and (eq $service "controllermanager") (or $main_context.Values.injectGenericVolumeBackupSidecar.enabled $main_context.Values.injectKanisterSidecar.enabled) }} + {{- $needsVolumesHeader = true }} + {{- else if or (eq (include "check.cacertconfigmap" $main_context) "true") (include "k10.ocpcacertsautoextraction" $main_context) }} + {{- $needsVolumesHeader = true }} + {{- else if and ( eq $service "auth" ) ( eq (include "check.dexAuth" $main_context) "true" ) }} + {{- $needsVolumesHeader = true }} + {{- else if eq $service "frontend" }} + {{- $needsVolumesHeader = true }} + {{- else if and (list "controllermanager" "executor" "catalog" | has $pod) (eq (include "check.projectSAToken" $main_context) "true")}} + {{- $needsVolumesHeader = true }} + {{- else if and (eq $service "aggregatedapis") (include "k10.siemEnabled" $main_context) }} + {{- $needsVolumesHeader = true }} + {{- end }}{{/* volumes header needed check */}} +{{- end }}{{/* range $skip, $service := $containerList */}} +{{- if $needsVolumesHeader }} + volumes: +{{- end }} +{{- end }}{{/* with .main */}} +{{- end }}{{/* define "k10-init-container-header" */}} + +{{- define "k10-deployment-volumes" }} +{{- $pod := .k10_pod }} +{{- with .main }} +{{- if .Values.features }} + - name: k10-features + configMap: + name: k10-features +{{- end }} +{{- if list "executor" | has $pod}} + - name: datastore-cache-vol + emptyDir: {} +{{- end }} +{{- if list "dashboardbff" "auth" "controllermanager" | has $pod}} +{{- if eq (include "basicauth.check" .) "true" }} + - name: k10-basic-auth + secret: + secretName: {{ default "k10-basic-auth" .Values.auth.basicAuth.secretName }} +{{- end }} +{{- if .Values.auth.oidcAuth.enabled }} + - name: {{ include "k10.oidcSecretName" .}} + secret: + secretName: {{ default (include "k10.oidcSecretName" .) .Values.auth.oidcAuth.secretName }} +{{- if .Values.auth.oidcAuth.clientSecretName }} + - name: {{ include "k10.oidcCustomerSecretName" . }} + secret: + secretName: {{ .Values.auth.oidcAuth.clientSecretName }} +{{- end }} +{{- end }} +{{- if .Values.auth.openshift.enabled }} + - name: {{ include "k10.oidcSecretName" .}} + secret: + secretName: {{ default (include "k10.oidcSecretName" .) .Values.auth.openshift.secretName }} +{{- end }} +{{- if .Values.auth.ldap.enabled }} + - name: {{ include "k10.oidcSecretName" .}} + secret: + secretName: {{ default (include "k10.oidcSecretName" .) .Values.auth.ldap.secretName }} + - name: k10-logos-dex + configMap: + name: k10-logos-dex +{{- end }} +{{- end }} +{{- range $skip, $statefulContainer := compact (dict "main" . "k10_service_pod" $pod | include "get.statefulRestServicesInPod" | splitList " ") }} + - name: {{ $statefulContainer }}-persistent-storage + persistentVolumeClaim: + claimName: {{ $statefulContainer }}-pv-claim +{{- end }} +{{- if eq (include "check.googleCredsOrSecret" .) "true" }} +{{- $gkeSecret := default "google-secret" .Values.secrets.googleClientSecretName }} + - name: service-account + secret: + secretName: {{ $gkeSecret }} +{{- end }} +{{- if and (list "controllermanager" "executor" "catalog" | has $pod) (eq (include "check.projectSAToken" .) "true")}} + - name: bound-sa-token + projected: + sources: + - serviceAccountToken: +{{- if eq (include "check.gwifidpaud" .) "true" }} + audience: {{ .Values.google.workloadIdentityFederation.idp.aud }} +{{- end }} + expirationSeconds: 3600 + path: token +{{- end }} +{{- with (include "k10.cacertconfigmapname" .) }} + - name: {{ . }} + configMap: + name: {{ . }} +{{- end }} +{{- if eq $pod "frontend" }} + - name: frontend-config + configMap: + name: frontend-config +{{- end }} +{{- if and (eq $pod "aggregatedapis") (include "k10.siemEnabled" .) }} + - name: aggauditpolicy-config + configMap: + name: aggauditpolicy-config +{{- end }} +{{- $containersInThisPod := (dict "main" . "k10_service_pod" $pod | include "get.serviceContainersInPod" | splitList " ") }} +{{- if has "logging" $containersInThisPod }} + - name: logging-configmap-storage + configMap: + name: fluentbit-configmap +{{- end }} +{{- if and (has "controllermanager" $containersInThisPod) (or .Values.injectGenericVolumeBackupSidecar.enabled .Values.injectKanisterSidecar.enabled) }} + - name: mutating-webhook-certs + secret: + secretName: controllermanager-certs +{{- end }} +{{- if and ( has "auth" $containersInThisPod) ( eq (include "check.dexAuth" .) "true" ) }} + - name: config + configMap: + name: k10-dex + items: + - key: config.yaml + path: config.yaml +{{- if .Values.auth.ldap.enabled }} + - name: dex-config + emptyDir: {} + - name: bind-secret + secret: + secretName: {{ default "k10-dex" .Values.auth.ldap.bindPWSecretName }} +{{- end }} +{{- end }} +{{- end }}{{/* with .main */}} +{{- end }}{{/* define "k10-init-container-header" */}} diff --git a/charts/kasten/k10/7.5.401/templates/_prometheus.tpl b/charts/kasten/k10/7.5.401/templates/_prometheus.tpl new file mode 100644 index 0000000000..a49a8363f6 --- /dev/null +++ b/charts/kasten/k10/7.5.401/templates/_prometheus.tpl @@ -0,0 +1,29 @@ +{{/*** MATCH LABELS *** + NOTE: The match labels here (`app` and `release`) are divergent from + the match labels set by the upstream chart. This is intentional since a + Deployment's `spec.selector` is immutable and K10 has already been shipped + with these values. + + A change to these selector labels will mean that all customers must manually + delete the Prometheus Deployment before upgrading, which is a situation we don't + want for our customers. + + Instead, the `app.kubernetes.io/name` and `app.kubernetes.io/instance` labels + are included in the `prometheus.commonMetaLabels` in: + `helm/k10/templates/{values}/prometheus/charts/{charts}/values/prometheus_values.tpl`. +*/}} +{{- define "prometheus.common.matchLabels" -}} +app: {{ include "prometheus.name" . }} +release: {{ .Release.Name }} +{{- end -}} + +{{- define "prometheus.server.labels" -}} +{{ include "prometheus.server.matchLabels" . }} +{{ include "prometheus.common.metaLabels" . }} +app.kubernetes.io/component: {{ .Values.server.name }} +{{- end -}} + +{{- define "prometheus.server.matchLabels" -}} +component: {{ .Values.server.name | quote }} +{{ include "prometheus.common.matchLabels" . }} +{{- end -}} diff --git a/charts/kasten/k10/7.5.401/templates/aggregatedaudit-policy.yaml b/charts/kasten/k10/7.5.401/templates/aggregatedaudit-policy.yaml new file mode 100644 index 0000000000..ef7f03c6c2 --- /dev/null +++ b/charts/kasten/k10/7.5.401/templates/aggregatedaudit-policy.yaml @@ -0,0 +1,34 @@ +{{- if include "k10.siemEnabled" . -}} +apiVersion: v1 +kind: ConfigMap +metadata: + labels: +{{ include "helm.labels" . | indent 4 }} + name: aggauditpolicy-config + namespace: {{ .Release.Namespace }} +data: + {{ include "k10.aggAuditPolicyFile" .}}: | + apiVersion: audit.k8s.io/v1 + kind: Policy + omitStages: + - "RequestReceived" + rules: + - level: RequestResponse + resources: + - group: "actions.kio.kasten.io" + resources: ["backupactions", "cancelactions", "exportactions", "importactions", "restoreactions", "retireactions", "runactions"] + - group: "apps.kio.kasten.io" + resources: ["applications", "clusterrestorepoints", "restorepoints", "restorepointcontents"] + - group: "repositories.kio.kasten.io" + resources: ["storagerepositories"] + - group: "vault.kio.kasten.io" + resources: ["passkeys"] + verbs: ["create", "update", "patch", "delete", "get"] + - level: None + nonResourceURLs: + - /healthz* + - /version + - /openapi/v2* + - /openapi/v3* + - /timeout* +{{- end -}} diff --git a/charts/kasten/k10/7.5.401/templates/apiservice.yaml b/charts/kasten/k10/7.5.401/templates/apiservice.yaml new file mode 100644 index 0000000000..1811df48a8 --- /dev/null +++ b/charts/kasten/k10/7.5.401/templates/apiservice.yaml @@ -0,0 +1,25 @@ +{{/* Template to generate the aggregated APIService/Service objects */}} +{{- if .Values.apiservices.deployed -}} +{{- $main := . -}} +{{- $container_port := .Values.service.internalPort -}} +{{- $namespace := .Release.Namespace -}} +{{- range include "k10.aggregatedAPIs" . | splitList " " -}} +--- +apiVersion: apiregistration.k8s.io/v1 +kind: APIService +metadata: + name: v1alpha1.{{ . }}.{{ template "apiDomain" $main }} + labels: + apiserver: "true" +{{ include "helm.labels" $ | indent 4 }} +spec: + version: v1alpha1 + group: {{ . }}.{{ template "apiDomain" $main }} + groupPriorityMinimum: 2000 + service: + namespace: {{$namespace}} + name: aggregatedapis-svc + versionPriority: 10 + insecureSkipTLSVerify: true +{{ end }} +{{- end -}} diff --git a/charts/kasten/k10/7.5.401/templates/daemonsets.yaml b/charts/kasten/k10/7.5.401/templates/daemonsets.yaml new file mode 100644 index 0000000000..b8c50b505f --- /dev/null +++ b/charts/kasten/k10/7.5.401/templates/daemonsets.yaml @@ -0,0 +1,26 @@ +{{- if .Values.metering.redhatMarketplacePayg }} +apiVersion: apps/v1 +kind: DaemonSet +metadata: + namespace: {{ .Release.Namespace }} + name: k10-rhmp-paygo + labels: +{{ include "helm.labels" . | indent 4 }} + component: paygo +spec: + selector: + matchLabels: +{{ include "k10.common.matchLabels" . | indent 6 }} + component: paygo + template: + metadata: + labels: +{{ include "helm.labels" . | indent 8 }} + component: paygo + spec: + containers: + - name: paygo + image: {{ .Values.global.images.paygo_daemonset }} + command: [ "sleep" ] + args: [ "36500d" ] +{{- end -}} diff --git a/charts/kasten/k10/7.5.401/templates/deployments.yaml b/charts/kasten/k10/7.5.401/templates/deployments.yaml new file mode 100644 index 0000000000..8159536bb4 --- /dev/null +++ b/charts/kasten/k10/7.5.401/templates/deployments.yaml @@ -0,0 +1,41 @@ +{{/* +Generates deployment specs for K10 services and other services such as +"frontend" and "kanister". +*/}} +{{- include "singleAuth.check" . -}} +{{- $main_context := . -}} +{{- $stateless_services := include "get.enabledStatelessServices" . | splitList " " -}} +{{- $colocated_services := include "get.enabledColocatedServices" . | fromYaml -}} +{{ $service_list := include "get.enabledRestServices" . | splitList " " }} +{{- range $skip, $k10_service := $service_list }} + {{ if not (hasKey $colocated_services $k10_service ) }} + {{/* Set $stateful for stateful services when .Values.global.persistence.enabled is true */}} + {{- $stateful := and $.Values.global.persistence.enabled (not (has $k10_service $stateless_services)) -}} + {{/* Use `limiter.executorReplicas` (with back-compatibility with already deprecated + `executorReplicas`) Helm parameter to configure number of replicas for service. In case of missing + `limiter.{servicename}Replicas` Helm parameter will be set `1`. + See also function `replicasFieldForService` in go/src/kasten.io/k10/kio/tools/restorectl/servicescaler/config.go.*/}} + {{- $replicas := index $.Values (printf "%sReplicas" $k10_service) -}} + {{- if eq $replicas nil -}} + {{- $replicas = index $.Values.limiter (printf "%sReplicas" $k10_service) -}} + {{- if eq $replicas nil -}} + {{- $replicas = 1 -}} + {{- end -}} + {{- end -}} + {{ $tmp_contx := dict "main" $main_context "k10_service" $k10_service "stateful" $stateful "replicas" $replicas }} + {{ if eq $k10_service "metering" }} + {{- include "k10-metering" $tmp_contx -}} + {{ else }} + {{- include "k10-default" $tmp_contx -}} + {{ end }} + {{ end }}{{/* if not (hasKey $colocated_services $k10_service ) */}} +{{- end }} +{{/* +Generate deployment specs for additional services. These are stateless and have +1 replica. +*/}} +{{- range $skip, $k10_service := concat (include "get.enabledServices" . | splitList " ") (include "get.enabledAdditionalServices" . | splitList " ") }} + {{- if eq $k10_service "gateway" -}}{{- continue -}}{{- end -}} + {{ $tmp_contx := dict "main" $main_context "k10_service" $k10_service "stateful" false "replicas" 1 }} + {{- include "k10-default" $tmp_contx -}} +{{- end }} diff --git a/charts/kasten/k10/7.5.401/templates/fluentbit-configmap.yaml b/charts/kasten/k10/7.5.401/templates/fluentbit-configmap.yaml new file mode 100644 index 0000000000..71cecb9668 --- /dev/null +++ b/charts/kasten/k10/7.5.401/templates/fluentbit-configmap.yaml @@ -0,0 +1,34 @@ +kind: ConfigMap +apiVersion: v1 +metadata: + labels: +{{ include "helm.labels" . | indent 4 }} + namespace: {{ .Release.Namespace }} + name: fluentbit-configmap +data: + fluentbit.conf: | + [SERVICE] + HTTP_Server On + HTTP_Listen 0.0.0.0 + HTTP_PORT 24225 + + [INPUT] + Name tcp + Listen 0.0.0.0 + Port 24224 + + [OUTPUT] + Name stdout + Match * + + [OUTPUT] + Name file + Match * + File {{ .Values.global.persistence.mountPath }}/k10.log + logrotate.conf: | + {{ .Values.global.persistence.mountPath }}/k10.log { + create + missingok + rotate 6 + size 1G + } diff --git a/charts/kasten/k10/7.5.401/templates/frontend-nginx-configmap.yaml b/charts/kasten/k10/7.5.401/templates/frontend-nginx-configmap.yaml new file mode 100644 index 0000000000..93d17b3a17 --- /dev/null +++ b/charts/kasten/k10/7.5.401/templates/frontend-nginx-configmap.yaml @@ -0,0 +1,50 @@ +kind: ConfigMap +apiVersion: v1 +metadata: + labels: +{{ include "helm.labels" . | indent 4 }} + namespace: {{ .Release.Namespace }} + name: frontend-config +data: + frontend.conf: | + server { + listen {{ .Values.service.externalPort }} default_server; + {{- if .Values.global.network.enable_ipv6 }} + listen [::]:{{ .Values.service.externalPort }} default_server; + {{- end }} + server_name localhost; + + gzip on; + # serves *.gz files (when present) instead of dynamic compression + gzip_static on; + + root /frontend; + index index.html; + + location / { + try_files $uri $uri/ =404; + } + } + nginx.conf: | + #user nginx; # this directive is ignored if we use a non-root user in Dockerfile + worker_processes 4; + + error_log stderr warn; + pid /var/run/nginx/nginx.pid; + + events { + worker_connections 1024; + } + + http { + include /etc/nginx/mime.types; + default_type application/octet-stream; + access_log /dev/stdout; + sendfile on; + keepalive_timeout 650; + + # turn off nginx version in responses + server_tokens off; + + include /etc/nginx/conf.d/*.conf; + } diff --git a/charts/kasten/k10/7.5.401/templates/gateway-ext.yaml b/charts/kasten/k10/7.5.401/templates/gateway-ext.yaml new file mode 100644 index 0000000000..26ac26544f --- /dev/null +++ b/charts/kasten/k10/7.5.401/templates/gateway-ext.yaml @@ -0,0 +1,36 @@ +{{/* Externally exposed service for gateway endpoint. */}} +{{- $container_port := .Values.service.internalPort -}} +{{- if .Values.externalGateway.create -}} +{{- include "authEnabled.check" . -}} +apiVersion: v1 +kind: Service +metadata: + namespace: {{ $.Release.Namespace }} + name: gateway-ext + labels: + service: gateway + {{- if eq "route53-mapper" (default " " .Values.externalGateway.fqdn.type) }} + dns: route53 + {{- end }} +{{ include "helm.labels" . | indent 4 }} + annotations: + {{- if .Values.externalGateway.annotations }} +{{ toYaml .Values.externalGateway.annotations | indent 4 }} + {{- end }} +{{ include "dnsAnnotations" . | indent 4 }} + {{- if .Values.externalGateway.awsSSLCertARN }} + service.beta.kubernetes.io/aws-load-balancer-ssl-cert: {{ .Values.externalGateway.awsSSLCertARN }} + service.beta.kubernetes.io/aws-load-balancer-ssl-ports: https + {{- if .Values.externalGateway.awsSecurityGroup }} + service.beta.kubernetes.io/aws-load-balancer-extra-security-groups: {{ .Values.externalGateway.awsSecurityGroup }} + {{- end }} + {{- end }} +spec: + type: LoadBalancer + ports: + - name: https + port: {{ if or .Values.secrets.tlsSecret .Values.externalGateway.awsSSLCertARN }}443{{ else }}80{{ end }} + targetPort: {{ $container_port }} + selector: + service: gateway +{{- end -}} diff --git a/charts/kasten/k10/7.5.401/templates/gateway.yaml b/charts/kasten/k10/7.5.401/templates/gateway.yaml new file mode 100644 index 0000000000..aa879bd306 --- /dev/null +++ b/charts/kasten/k10/7.5.401/templates/gateway.yaml @@ -0,0 +1,107 @@ +{{- $container_port := .Values.gateway.service.internalPort | default 8000 -}} +{{- $service_port := .Values.gateway.service.externalPort -}} +--- +apiVersion: v1 +kind: Service +metadata: + namespace: {{ $.Release.Namespace }} + labels: + service: gateway +{{ include "helm.labels" . | indent 4 }} + name: gateway +spec: + ports: + - name: http + port: {{ $service_port }} + targetPort: {{ $container_port }} + selector: + service: gateway +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + namespace: {{ $.Release.Namespace }} + labels: +{{ include "helm.labels" . | indent 4 }} + component: gateway + name: gateway +spec: + replicas: 1 + selector: + matchLabels: + service: gateway + template: + metadata: + annotations: + {{- include "k10.deploymentPodAnnotations" . | nindent 8 }} + labels: + {{- with merge (dict "requiredLabels" (dict "component" "gateway" "service" "gateway")) . }} + {{- include "k10.deploymentPodLabels" . | nindent 8 }} + {{- end }} + spec: + serviceAccountName: {{ template "serviceAccountName" . }} + {{- dict "main" . "k10_deployment_name" "gateway" | include "k10.priorityClassName" | indent 6}} + {{- include "k10.imagePullSecrets" . | indent 6 }} + containers: + - name: gateway + {{- dict "main" . "k10_service" "gateway" | include "serviceImage" | indent 8 }} + {{- if .Values.secrets.tlsSecret }} + volumeMounts: + - name: tls-volume + mountPath: "/etc/tls" + readOnly: true + {{- end }} + resources: + limits: + cpu: {{ .Values.gateway.resources.limits.cpu | quote }} + memory: {{ .Values.gateway.resources.limits.memory | quote }} + requests: + cpu: {{ .Values.gateway.resources.requests.cpu | quote }} + memory: {{ .Values.gateway.resources.requests.memory | quote }} + env: + - name: LOG_LEVEL + valueFrom: + configMapKeyRef: + name: k10-config + key: loglevel + - name: VERSION + valueFrom: + configMapKeyRef: + name: k10-config + key: version +{{- if .Values.fips.enabled }} + {{- include "k10.enforceFIPSEnvironmentVariables" . | indent 10 }} +{{- end }} + {{- with $capabilities := include "k10.capabilities" . }} + - name: K10_CAPABILITIES + value: {{ $capabilities | quote }} + {{- end }} + {{- with $capabilities_mask := include "k10.capabilities_mask" . }} + - name: K10_CAPABILITIES_MASK + value: {{ $capabilities_mask | quote }} + {{- end }} + {{- if eq (include "check.dexAuth" .) "true" }} + - name: {{ include "k10.gatewayEnableDex" . }} + value: "true" + {{- end }} + envFrom: + - configMapRef: + name: k10-gateway + livenessProbe: + httpGet: + path: /healthz + port: {{ $container_port }} + scheme: HTTP{{ if .Values.secrets.tlsSecret }}S{{ end }} + initialDelaySeconds: 5 + readinessProbe: + httpGet: + path: /healthz + port: {{ $container_port }} + scheme: HTTP{{ if .Values.secrets.tlsSecret }}S{{ end }} + restartPolicy: Always + {{- if .Values.secrets.tlsSecret }} + volumes: + - name: tls-volume + secret: + secretName: {{ .Values.secrets.tlsSecret }} + {{- end }} diff --git a/charts/kasten/k10/7.5.401/templates/ingress.yaml b/charts/kasten/k10/7.5.401/templates/ingress.yaml new file mode 100644 index 0000000000..5a98d3bae8 --- /dev/null +++ b/charts/kasten/k10/7.5.401/templates/ingress.yaml @@ -0,0 +1,73 @@ +{{- $ingressApiIsStable := eq (include "ingress.isStable" .) "true" -}} +{{- $service_port := .Values.gateway.service.externalPort -}} +{{ if .Values.ingress.create }} +{{ include "authEnabled.check" . }} +{{ include "check.ingress.defaultBackend" . }} +apiVersion: {{ template "ingress.apiVersion" . }} +kind: Ingress +metadata: + labels: +{{ include "helm.labels" . | indent 4 }} + namespace: {{ .Release.Namespace }} + name: {{ .Values.ingress.name | default (printf "%s-ingress" .Release.Name) }} + annotations: +{{ include "ingressClassAnnotation" . | indent 4 }} + {{- if .Values.secrets.tlsSecret }} + nginx.ingress.kubernetes.io/secure-backends: "true" + nginx.ingress.kubernetes.io/backend-protocol: HTTPS + {{- end }} + {{- if .Values.ingress.annotations }} +{{ toYaml .Values.ingress.annotations | indent 4 }} + {{- end }} +spec: +{{ include "specIngressClassName" . | indent 2 }} +{{ with .Values.ingress.defaultBackend }} + {{- if or .service.enabled .resource.enabled }} + defaultBackend: + {{- with .service }} + {{- if .enabled }} + service: + name: {{ required "`name` is required in the `ingress.defaultBackend.service`." .name }} + port: + {{- if .port.name }} + name: {{ .port.name }} + {{- else if .port.number }} + number: {{ .port.number }} + {{- end }} + {{- end }} + {{- end }} + {{- with .resource }} + {{- if .enabled }} + resource: + apiGroup: {{ .apiGroup }} + name: {{ required "`name` is required in the `ingress.defaultBackend.resource`." .name }} + kind: {{ required "`kind` is required in the `ingress.defaultBackend.resource`." .kind }} + {{- end }} + {{- end }} + {{- end }} +{{- end }} +{{- if .Values.ingress.tls.enabled }} + tls: + - hosts: + - {{ required "ingress.host value is required for TLS configuration" .Values.ingress.host }} + secretName: {{ .Values.ingress.tls.secretName }} +{{- end }} + rules: + - http: + paths: + - path: /{{ default .Release.Name .Values.ingress.urlPath | trimPrefix "/" | trimSuffix "/" }}/ + pathType: {{ default "ImplementationSpecific" .Values.ingress.pathType }} + backend: + {{- if $ingressApiIsStable }} + service: + name: gateway + port: + number: {{ $service_port }} + {{- else }} + serviceName: gateway + servicePort: {{ $service_port }} + {{- end }} + {{- if .Values.ingress.host }} + host: {{ .Values.ingress.host }} + {{- end }} +{{ end }} diff --git a/charts/kasten/k10/7.5.401/templates/k10-config.yaml b/charts/kasten/k10/7.5.401/templates/k10-config.yaml new file mode 100644 index 0000000000..af31659e82 --- /dev/null +++ b/charts/kasten/k10/7.5.401/templates/k10-config.yaml @@ -0,0 +1,365 @@ +kind: ConfigMap +apiVersion: v1 +metadata: + labels: +{{ include "helm.labels" . | indent 4 }} + namespace: {{ .Release.Namespace }} + name: k10-config +data: + DataStoreLogLevel: {{ default "error" | quote }} + DataStoreFileLogLevel: {{ default "" | quote }} + loglevel: {{ .Values.logLevel | quote }} + {{- if .Values.clusterName }} + clustername: {{ quote .Values.clusterName }} + {{- end }} + version: {{ .Chart.AppVersion }} + {{- $capabilities := include "k10.capabilities" . | splitList " " }} + {{- $capabilities_mask := include "k10.capabilities_mask" . | splitList " " }} + {{- if and ( has "mc" $capabilities ) ( not ( has "mc" $capabilities_mask ) ) }} + multiClusterVersion: {{ include "k10.multiClusterVersion" . | quote }} + {{- end }} + modelstoredirname: "//mnt/k10state/kasten-io/" + apiDomain: {{ include "apiDomain" . }} + k10DataStoreDisableCompression: "false" + k10DataStoreParallelUpload: {{ .Values.datastore.parallelUploads | quote }} + k10DataStoreParallelDownload: {{ .Values.datastore.parallelDownloads | quote }} + k10DataStoreParallelBlockUpload: {{ .Values.datastore.parallelBlockUploads | quote }} + k10DataStoreParallelBlockDownload: {{ .Values.datastore.parallelBlockDownloads | quote }} + k10DataStoreEstimationType: {{ .Values.datastore.estimationType | quote }} + k10DataStoreAdaptiveEstimationThreshold: {{ .Values.datastore.adaptiveEstimationThreshold | quote }} + k10DataStoreGeneralContentCacheSizeMB: {{ include "k10.defaultK10DataStoreGeneralContentCacheSizeMB" . | quote }} + k10DataStoreGeneralMetadataCacheSizeMB: {{ include "k10.defaultK10DataStoreGeneralMetadataCacheSizeMB" . | quote }} + k10DataStoreTotalCacheSizeLimitMB : {{ include "k10.defaultK10DataStoreTotalCacheSizeLimitMB" . | quote }} + k10DataStoreRestoreContentCacheSizeMB: {{ include "k10.defaultK10DataStoreRestoreContentCacheSizeMB" . | quote }} + k10DataStoreRestoreMetadataCacheSizeMB: {{ include "k10.defaultK10DataStoreRestoreMetadataCacheSizeMB" . | quote }} + K10BackupBufferFileHeadroomFactor: {{ include "k10.defaultK10BackupBufferFileHeadroomFactor" . | quote }} + AWSAssumeRoleDuration: {{ default (include "k10.defaultAssumeRoleDuration" .) .Values.awsConfig.assumeRoleDuration | quote }} + {{- if gt (int .Values.kanister.backupTimeout) 0 }} + K10TimeoutBlueprintBackup: {{ default (include "k10.defaultK10TimeoutBlueprintBackup" .) .Values.kanister.backupTimeout | quote }} + {{- else }} + K10TimeoutBlueprintBackup: {{ default (include "k10.defaultK10TimeoutBlueprintBackup" .) .Values.timeout.blueprintBackup | quote }} + {{- end }} + {{- if gt (int .Values.kanister.restoreTimeout) 0 }} + K10TimeoutBlueprintRestore: {{ default (include "k10.defaultK10TimeoutBlueprintRestore" .) .Values.kanister.restoreTimeout | quote }} + {{- else }} + K10TimeoutBlueprintRestore: {{ default (include "k10.defaultK10TimeoutBlueprintRestore" .) .Values.timeout.blueprintRestore | quote }} + {{- end }} + {{- if gt (int .Values.kanister.deleteTimeout) 0 }} + K10TimeoutBlueprintDelete: {{ default (include "k10.defaultK10TimeoutBlueprintDelete" .) .Values.kanister.deleteTimeout | quote }} + {{- else }} + K10TimeoutBlueprintDelete: {{ default (include "k10.defaultK10TimeoutBlueprintDelete" .) .Values.timeout.blueprintDelete | quote }} + {{- end }} + {{- if gt (int .Values.kanister.hookTimeout) 0 }} + K10TimeoutBlueprintHooks: {{ default (include "k10.defaultK10TimeoutBlueprintHooks" .) .Values.kanister.hookTimeout | quote }} + {{- else }} + K10TimeoutBlueprintHooks: {{ default (include "k10.defaultK10TimeoutBlueprintHooks" .) .Values.timeout.blueprintHooks | quote }} + {{- end }} + {{- if gt (int .Values.kanister.checkRepoTimeout) 0 }} + K10TimeoutCheckRepoPodReady: {{ default (include "k10.defaultK10TimeoutCheckRepoPodReady" .) .Values.kanister.checkRepoTimeout | quote }} + {{- else }} + K10TimeoutCheckRepoPodReady: {{ default (include "k10.defaultK10TimeoutCheckRepoPodReady" .) .Values.timeout.checkRepoPodReady | quote }} + {{- end }} + {{- if gt (int .Values.kanister.statsTimeout) 0 }} + K10TimeoutStatsPodReady: {{ default (include "k10.defaultK10TimeoutStatsPodReady" .) .Values.kanister.statsTimeout | quote }} + {{- else }} + K10TimeoutStatsPodReady: {{ default (include "k10.defaultK10TimeoutStatsPodReady" .) .Values.timeout.statsPodReady | quote }} + {{- end }} + {{- if gt (int .Values.kanister.efsPostRestoreTimeout) 0 }} + K10TimeoutEFSRestorePodReady: {{ default (include "k10.defaultK10TimeoutEFSRestorePodReady" .) .Values.kanister.efsPostRestoreTimeout | quote }} + {{- else }} + K10TimeoutEFSRestorePodReady: {{ default (include "k10.defaultK10TimeoutEFSRestorePodReady" .) .Values.timeout.efsRestorePodReady | quote }} + {{- end }} + {{- if gt (int .Values.kanister.podReadyWaitTimeout) 0 }} + K10TimeoutWorkerPodReady: {{ .Values.kanister.podReadyWaitTimeout | quote }} + {{- else }} + K10TimeoutWorkerPodReady: {{ .Values.timeout.workerPodReady | quote }} + {{- end }} + + KanisterManagedDataServicesBlueprintsEnabled: {{ .Values.kanister.managedDataServicesBlueprintsEnabled | quote }} + + WorkerPodMetricSidecarEnabled: {{ default .Values.kanisterPodMetricSidecar.enabled .Values.workerPodMetricSidecar.enabled | quote }} + WorkerPodMetricSidecarMetricLifetime: {{ default .Values.kanisterPodMetricSidecar.metricLifetime .Values.workerPodMetricSidecar.metricLifetime | quote }} + WorkerPodPushgatewayMetricsInterval: {{ default .Values.kanisterPodMetricSidecar.pushGatewayInterval .Values.workerPodMetricSidecar.pushGatewayInterval | quote }} +{{- include "workerPodMetricSidecarResources" . | indent 2 }} + KanisterToolsImage: {{ include "get.kanisterToolsImage" . | quote }} + + K10MutatingWebhookTLSCertDir: "/etc/ssl/certs/webhook" + {{- if .Values.injectGenericVolumeBackupSidecar.enabled }} + K10MutatingWebhookPort: {{ default 8080 .Values.injectGenericVolumeBackupSidecar.webhookServer.port | quote }} + {{- else }} + K10MutatingWebhookPort: {{ default 8080 .Values.injectKanisterSidecar.webhookServer.port | quote }} + {{- end }} + + {{- if gt (int .Values.limiter.concurrentSnapConversions) 0 }} + K10LimiterSnapshotExportsPerAction: {{ default (include "k10.defaultK10LimiterSnapshotExportsPerAction" .) .Values.limiter.concurrentSnapConversions | quote }} + {{- else }} + K10LimiterSnapshotExportsPerAction: {{ default (include "k10.defaultK10LimiterSnapshotExportsPerAction" .) .Values.limiter.snapshotExportsPerAction | quote }} + {{- end }} + {{- if gt (int .Values.limiter.genericVolumeSnapshots) 0 }} + K10LimiterGenericVolumeBackupsPerCluster: {{ default (include "k10.defaultK10LimiterGenericVolumeBackupsPerCluster" .) .Values.limiter.genericVolumeSnapshots | quote }} + {{- else }} + K10LimiterGenericVolumeBackupsPerCluster: {{ default (include "k10.defaultK10LimiterGenericVolumeBackupsPerCluster" .) .Values.limiter.genericVolumeBackupsPerCluster | quote }} + {{- end }} + {{- if gt (int .Values.limiter.genericVolumeCopies) 0 }} + K10LimiterSnapshotExportsPerCluster: {{ default (include "k10.defaultK10LimiterSnapshotExportsPerCluster" .) .Values.limiter.genericVolumeCopies | quote }} + {{- else }} + K10LimiterSnapshotExportsPerCluster: {{ default (include "k10.defaultK10LimiterSnapshotExportsPerCluster" .) .Values.limiter.snapshotExportsPerCluster | quote }} + {{- end }} + {{- if gt (int .Values.limiter.genericVolumeRestores) 0 }} + K10LimiterVolumeRestoresPerCluster: {{ default (include "k10.defaultK10LimiterVolumeRestoresPerCluster" .) .Values.limiter.genericVolumeRestores | quote }} + {{- else }} + K10LimiterVolumeRestoresPerCluster: {{ default (include "k10.defaultK10LimiterVolumeRestoresPerCluster" .) .Values.limiter.volumeRestoresPerCluster | quote }} + {{- end }} + {{- if gt (int .Values.limiter.csiSnapshots) 0 }} + K10LimiterCsiSnapshotsPerCluster: {{ default (include "k10.defaultK10LimiterCsiSnapshotsPerCluster" .) .Values.limiter.csiSnapshots | quote }} + {{- else }} + K10LimiterCsiSnapshotsPerCluster: {{ default (include "k10.defaultK10LimiterCsiSnapshotsPerCluster" .) .Values.limiter.csiSnapshotsPerCluster | quote }} + {{- end }} + {{- if gt (int .Values.limiter.providerSnapshots) 0 }} + K10LimiterDirectSnapshotsPerCluster: {{ default (include "k10.defaultK10LimiterDirectSnapshotsPerCluster" .) .Values.limiter.providerSnapshots | quote }} + {{- else }} + K10LimiterDirectSnapshotsPerCluster: {{ default (include "k10.defaultK10LimiterDirectSnapshotsPerCluster" .) .Values.limiter.directSnapshotsPerCluster | quote }} + {{- end }} + {{- if gt (int .Values.limiter.imageCopies) 0 }} + K10LimiterImageCopiesPerCluster: {{ default (include "k10.defaultK10LimiterImageCopiesPerCluster" .) .Values.limiter.imageCopies | quote }} + {{- else }} + K10LimiterImageCopiesPerCluster: {{ default (include "k10.defaultK10LimiterImageCopiesPerCluster" .) .Values.limiter.imageCopiesPerCluster | quote }} + {{- end }} + K10LimiterWorkloadSnapshotsPerAction: {{ default (include "k10.defaultK10LimiterWorkloadSnapshotsPerAction" .) .Values.limiter.workloadSnapshotsPerAction | quote }} + {{- if gt (int .Values.services.executor.workerCount) 0 }} + K10LimiterExecutorThreads: {{ default (include "k10.defaultK10LimiterExecutorThreads" .) .Values.services.executor.workerCount | quote }} + {{- else }} + K10LimiterExecutorThreads: {{ default (include "k10.defaultK10LimiterExecutorThreads" .) .Values.limiter.executorThreads | quote }} + {{- end }} + {{- if gt (int .Values.services.executor.maxConcurrentRestoreCsiSnapshots) 0 }} + K10LimiterCsiSnapshotRestoresPerAction: {{ default (include "k10.defaultK10LimiterCsiSnapshotRestoresPerAction" .) .Values.services.executor.maxConcurrentRestoreCsiSnapshots | quote }} + {{- else }} + K10LimiterCsiSnapshotRestoresPerAction: {{ default (include "k10.defaultK10LimiterCsiSnapshotRestoresPerAction" .) .Values.limiter.csiSnapshotRestoresPerAction | quote }} + {{- end }} + {{- if gt (int .Values.services.executor.maxConcurrentRestoreGenericVolumeSnapshots) 0 }} + K10LimiterVolumeRestoresPerAction: {{ default (include "k10.defaultK10LimiterVolumeRestoresPerAction" .) .Values.services.executor.maxConcurrentRestoreGenericVolumeSnapshots | quote }} + {{- else }} + K10LimiterVolumeRestoresPerAction: {{ default (include "k10.defaultK10LimiterVolumeRestoresPerAction" .) .Values.limiter.volumeRestoresPerAction | quote }} + {{- end }} + {{- if gt (int .Values.services.executor.maxConcurrentRestoreWorkloads) 0 }} + K10LimiterWorkloadRestoresPerAction: {{ default (include "k10.defaultK10LimiterWorkloadRestoresPerAction" .) .Values.services.executor.maxConcurrentRestoreWorkloads | quote }} + {{- else }} + K10LimiterWorkloadRestoresPerAction: {{ default (include "k10.defaultK10LimiterWorkloadRestoresPerAction" .) .Values.limiter.workloadRestoresPerAction | quote }} + {{- end }} + + K10GCDaemonPeriod: {{ default (include "k10.defaultK10GCDaemonPeriod" .) .Values.garbagecollector.daemonPeriod | quote }} + K10GCKeepMaxActions: {{ default (include "k10.defaultK10GCKeepMaxActions" .) .Values.garbagecollector.keepMaxActions | quote }} + K10GCActionsEnabled: {{ default (include "k10.defaultK10GCActionsEnabled" .) .Values.garbagecollector.actions.enabled | quote }} + + K10EphemeralPVCOverhead: {{ .Values.ephemeralPVCOverhead | quote }} + + K10PersistenceStorageClass: {{ .Values.global.persistence.storageClass | quote }} + + K10DefaultPriorityClassName: {{ default (include "k10.defaultK10DefaultPriorityClassName" .) .Values.defaultPriorityClassName | quote }} + {{- if .Values.global.podLabels }} + K10CustomPodLabels: {{ include "k10.globalPodLabelsJson" . | quote }} + {{- end }} + {{- if .Values.global.podAnnotations }} + K10CustomPodAnnotations: {{ include "k10.globalPodAnnotationsJson" . | quote }} + {{- end }} + + kubeVirtVMsUnFreezeTimeout: {{ default (include "k10.defaultKubeVirtVMsUnfreezeTimeout" .) .Values.kubeVirtVMs.snapshot.unfreezeTimeout | quote }} + + {{- if and .Values.maxJobWaitDuration (not (empty .Values.maxJobWaitDuration)) }} + K10TimeoutJobWait: {{ .Values.maxJobWaitDuration | quote }} + {{- else }} + K10TimeoutJobWait: {{ .Values.timeout.jobWait | quote }} + {{- end }} + + quickDisasterRecoveryEnabled: {{ .Values.kastenDisasterRecovery.quickMode.enabled | quote }} + + {{- if .Values.forceRootInKanisterHooks }} + K10ForceRootInBlueprintActions: {{ .Values.forceRootInKanisterHooks| quote }} + {{- else }} + K10ForceRootInBlueprintActions: {{ .Values.forceRootInBlueprintActions | quote }} + {{- end }} + + workerPodResourcesCRDEnabled: {{ .Values.workerPodCRDs.enabled | quote }} +{{- include "workerPodResourcesCRD" . | indent 2 }} + + {{- if .Values.awsConfig.efsBackupVaultName }} + efsBackupVaultName: {{ quote .Values.awsConfig.efsBackupVaultName }} + {{- end }} + + {{- if .Values.excludedApps }} + excludedApps: '{{ join "," .Values.excludedApps }}' + {{- end }} + + {{- if .Values.vmWare.taskTimeoutMin }} + vmWareTaskTimeoutMin: {{ quote .Values.vmWare.taskTimeoutMin }} + {{- end }} + +{{- include "get.kanisterPodCustomLabels" . | indent 2}} +{{- include "get.kanisterPodCustomAnnotations" . | indent 2}} + + {{- if .Values.kanisterFunctionVersion }} + kanisterFunctionVersion: {{ .Values.kanisterFunctionVersion | quote }} + {{- else }} + kanisterFunctionVersion: {{ quote "v1.0.0-alpha" }} + {{- end }} +{{- include "kanisterToolsResources" . | indent 2 }} +{{- include "get.gvsActivationToken" . | indent 2 }} + + {{- if .Values.genericStorageBackup.overridepubkey }} + overridePublicKeyForGVS: {{ .Values.genericStorageBackup.overridepubkey | quote }} + {{- end }} + + {{- with (include "k10.fluentbitEndpoint" .) }} + fluentbitEndpoint: {{ . | quote }} + {{- end }} + +{{ if .Values.features }} +--- +kind: ConfigMap +apiVersion: v1 +metadata: + labels: +{{ include "helm.labels" . | indent 4 }} + namespace: {{ .Release.Namespace }} + name: k10-features +data: +{{ include "k10.features" . | indent 2}} +{{ end }} +{{ if .Values.auth.openshift.enabled }} +--- +kind: ConfigMap +apiVersion: v1 +metadata: + labels: +{{ include "helm.labels" . | indent 4 }} + name: k10-dex + namespace: {{ .Release.Namespace }} +data: + config.yaml: | + issuer: {{ printf "%s/dex" (trimSuffix "/" .Values.auth.openshift.dashboardURL) }} + storage: + type: memory + web: + http: 0.0.0.0:8080 + logger: + level: info + format: text + connectors: + - type: openshift + id: openshift + name: OpenShift + config: + issuer: {{ .Values.auth.openshift.openshiftURL }} + clientID: {{ printf "system:serviceaccount:%s:%s" .Release.Namespace (include "get.openshiftServiceAccountName" .) }} + clientSecret: {{ printf "{{ getenv \"%s\" }}" (include "k10.openShiftClientSecretEnvVar" . ) }} + redirectURI: {{ printf "%s/dex/callback" (trimSuffix "/" .Values.auth.openshift.dashboardURL) }} + insecureCA: {{ .Values.auth.openshift.insecureCA }} +{{- if and (eq (include "check.cacertconfigmap" .) "false") .Values.auth.openshift.useServiceAccountCA }} + rootCA: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt +{{- end }} + oauth2: + skipApprovalScreen: true + staticClients: + - name: 'K10' + id: kasten + secret: kastensecret + redirectURIs: + - {{ printf "%s/auth-svc/v0/oidc/redirect" (trimSuffix "/" .Values.auth.openshift.dashboardURL) }} +{{ end }} +{{ if .Values.auth.ldap.enabled }} +--- +kind: ConfigMap +apiVersion: v1 +metadata: + labels: +{{ include "helm.labels" . | indent 4 }} + name: k10-dex + namespace: {{ .Release.Namespace }} +data: + config.yaml: | + issuer: {{ printf "%s/dex" (trimSuffix "/" .Values.auth.ldap.dashboardURL) }} + storage: + type: memory + web: + http: 0.0.0.0:8080 + frontend: + dir: {{ include "k10.dexFrontendDir" . }} + theme: custom + logoURL: theme/kasten-logo.svg + logger: + level: info + format: text + connectors: + - type: ldap + id: ldap + name: LDAP + config: + host: {{ .Values.auth.ldap.host }} + insecureNoSSL: {{ .Values.auth.ldap.insecureNoSSL }} + insecureSkipVerify: {{ .Values.auth.ldap.insecureSkipVerifySSL }} + startTLS: {{ .Values.auth.ldap.startTLS }} + bindDN: {{ .Values.auth.ldap.bindDN }} + bindPW: BIND_PASSWORD_PLACEHOLDER + userSearch: + baseDN: {{ .Values.auth.ldap.userSearch.baseDN }} + filter: {{ .Values.auth.ldap.userSearch.filter }} + username: {{ .Values.auth.ldap.userSearch.username }} + idAttr: {{ .Values.auth.ldap.userSearch.idAttr }} + emailAttr: {{ .Values.auth.ldap.userSearch.emailAttr }} + nameAttr: {{ .Values.auth.ldap.userSearch.nameAttr }} + preferredUsernameAttr: {{ .Values.auth.ldap.userSearch.preferredUsernameAttr }} + groupSearch: + baseDN: {{ .Values.auth.ldap.groupSearch.baseDN }} + filter: {{ .Values.auth.ldap.groupSearch.filter }} + nameAttr: {{ .Values.auth.ldap.groupSearch.nameAttr }} +{{- with .Values.auth.ldap.groupSearch.userMatchers }} + userMatchers: +{{ toYaml . | indent 10 }} +{{- end }} + oauth2: + skipApprovalScreen: true + staticClients: + - name: 'K10' + id: kasten + secret: kastensecret + redirectURIs: + - {{ printf "%s/auth-svc/v0/oidc/redirect" (trimSuffix "/" .Values.auth.ldap.dashboardURL) }} +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: k10-logos-dex + namespace: {{ .Release.Namespace }} +binaryData: + {{- $files := .Files }} + {{- range tuple "files/favicon.png" "files/kasten-logo.svg" "files/styles.css" }} + {{ trimPrefix "files/" . }}: |- + {{ $files.Get . | b64enc }} + {{- end }} +{{ end }} +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: k10-gateway + namespace: {{ .Release.Namespace }} +data: + {{ include "k10.gatewayPrefixVarName" . }}: {{ include "k10.prefixPath" . }} + {{ include "k10.gatewayGrafanaSvcVarName" . }}: {{ printf "%s-grafana" .Release.Name }} + + {{- if .Values.gateway.requestHeaders }} + {{ include "k10.gatewayRequestHeadersVarName" .}}: {{ (.Values.gateway.requestHeaders | default list) | join " " }} + {{- end }} + + {{- if .Values.gateway.authHeaders }} + {{ include "k10.gatewayAuthHeadersVarName" .}}: {{ (.Values.gateway.authHeaders | default list) | join " " }} + {{- end }} + + {{- if .Values.gateway.service.internalPort }} + {{ include "k10.gatewayPortVarName" .}}: {{ .Values.gateway.service.internalPort | quote }} + {{- end }} + + {{- if .Values.secrets.tlsSecret }} + {{ include "k10.gatewayTLSCertFile" . }}: /etc/tls/tls.crt + {{ include "k10.gatewayTLSKeyFile" . }}: /etc/tls/tls.key + {{- end }} diff --git a/charts/kasten/k10/7.5.401/templates/k10-eula.yaml b/charts/kasten/k10/7.5.401/templates/k10-eula.yaml new file mode 100644 index 0000000000..21e251d6c8 --- /dev/null +++ b/charts/kasten/k10/7.5.401/templates/k10-eula.yaml @@ -0,0 +1,21 @@ +kind: ConfigMap +apiVersion: v1 +metadata: + labels: +{{ include "helm.labels" . | indent 4 }} + namespace: {{ .Release.Namespace }} + name: k10-eula +data: + text: {{ .Files.Get "eula.txt" | quote }} +--- +{{ if .Values.eula.accept }} +kind: ConfigMap +apiVersion: v1 +metadata: + labels: +{{ include "helm.labels" . | indent 4 }} + namespace: {{ .Release.Namespace }} + name: k10-eula-info +data: +{{ include "k10.eula.fields" . | indent 2 }} +{{ end }} diff --git a/charts/kasten/k10/7.5.401/templates/k10-scc.yaml b/charts/kasten/k10/7.5.401/templates/k10-scc.yaml new file mode 100644 index 0000000000..12a449f6fa --- /dev/null +++ b/charts/kasten/k10/7.5.401/templates/k10-scc.yaml @@ -0,0 +1,46 @@ +{{- if .Values.scc.create }} +kind: SecurityContextConstraints +apiVersion: security.openshift.io/v1 +metadata: + name: {{ .Release.Name }}-scc + labels: +{{ include "helm.labels" . | indent 4 }} +allowHostDirVolumePlugin: false +allowHostIPC: false +allowHostNetwork: false +allowHostPID: false +allowHostPorts: false +allowPrivilegeEscalation: false +allowPrivilegedContainer: false +allowedCapabilities: + - CHOWN + - FOWNER + - DAC_OVERRIDE +defaultAddCapabilities: + - CHOWN + - FOWNER + - DAC_OVERRIDE +fsGroup: + type: RunAsAny +priority: {{ .Values.scc.priority }} +readOnlyRootFilesystem: false +requiredDropCapabilities: + - ALL +runAsUser: + type: RunAsAny +seLinuxContext: + type: RunAsAny +supplementalGroups: + type: RunAsAny +seccompProfiles: + - runtime/default +users: + - system:serviceaccount:{{.Release.Namespace}}:{{ template "serviceAccountName" . }} +volumes: + - configMap + - downwardAPI + - emptyDir + - persistentVolumeClaim + - projected + - secret +{{- end }} diff --git a/charts/kasten/k10/7.5.401/templates/kopia-tls-certs.yaml b/charts/kasten/k10/7.5.401/templates/kopia-tls-certs.yaml new file mode 100644 index 0000000000..ac0635f51c --- /dev/null +++ b/charts/kasten/k10/7.5.401/templates/kopia-tls-certs.yaml @@ -0,0 +1,33 @@ +# alternate names of the services. This renders to: [ component-svc.namespace, component-svc.namespace.svc ] +{{- $altNamesKopia := list ( printf "%s-svc.%s" "data-mover" .Release.Namespace ) ( printf "%s-svc.%s.svc" "data-mover" .Release.Namespace ) }} +# generate ca cert with 365 days of validity +{{- $caKopia := genCA ( printf "%s-svc-ca" "data-mover" ) 365 }} +# generate cert with CN="component-svc", SAN=$altNames and with 365 days of validity +{{- $certKopia := genSignedCert ( printf "%s-svc" "data-mover" ) nil $altNamesKopia 365 $caKopia }} +apiVersion: v1 +kind: Secret +type: Opaque +metadata: + name: kopia-tls-cert + labels: +{{ include "helm.labels" . | indent 4 }} +{{- if .Values.global.rhMarketPlace }} + annotations: + "helm.sh/hook": "pre-install" +{{- end }} +data: + tls.crt: {{ $certKopia.Cert | b64enc }} +--- +apiVersion: v1 +kind: Secret +type: Opaque +metadata: + name: kopia-tls-key + labels: +{{ include "helm.labels" . | indent 4 }} +{{- if .Values.global.rhMarketPlace }} + annotations: + "helm.sh/hook": "pre-install" +{{- end }} +data: + tls.key: {{ $certKopia.Key | b64enc }} diff --git a/charts/kasten/k10/7.5.401/templates/license.yaml b/charts/kasten/k10/7.5.401/templates/license.yaml new file mode 100644 index 0000000000..f409fb7e51 --- /dev/null +++ b/charts/kasten/k10/7.5.401/templates/license.yaml @@ -0,0 +1,25 @@ +{{- if not ( or ( .Values.license ) ( .Values.metering.awsMarketplace ) ( .Values.metering.awsManagedLicense ) ( .Values.metering.licenseConfigSecretName ) ) }} +{{- if .Files.Get "triallicense" }} +apiVersion: v1 +kind: Secret +metadata: + labels: +{{ include "helm.labels" . | indent 4 }} + namespace: {{ .Release.Namespace }} + name: k10-trial-license +type: Opaque +data: + license: {{ print (.Files.Get "triallicense") }} +{{- end }} +{{- end }} +--- +apiVersion: v1 +kind: Secret +metadata: + labels: +{{ include "helm.labels" . | indent 4 }} + namespace: {{ .Release.Namespace }} + name: k10-license +type: Opaque +data: + license: {{ include "k10.getlicense" . }} diff --git a/charts/kasten/k10/7.5.401/templates/mc.yaml b/charts/kasten/k10/7.5.401/templates/mc.yaml new file mode 100644 index 0000000000..2c23f94ae5 --- /dev/null +++ b/charts/kasten/k10/7.5.401/templates/mc.yaml @@ -0,0 +1,6 @@ +{{- if not .Values.multicluster.enabled -}} + {{- $clusterInfo := lookup "v1" "Secret" .Release.Namespace "mc-cluster-info" -}} + {{- if $clusterInfo -}} + {{- fail "WARNING: Multi-cluster features must remain enabled as long as this cluster is connected to a multi-cluster system.\nEither disconnect this cluster from the multi-cluster system or use multicluster.enabled=true to enable multi-cluster features." -}} + {{- end -}} +{{- end -}} diff --git a/charts/kasten/k10/7.5.401/templates/mutatingwebhook.yaml b/charts/kasten/k10/7.5.401/templates/mutatingwebhook.yaml new file mode 100644 index 0000000000..5f86b25920 --- /dev/null +++ b/charts/kasten/k10/7.5.401/templates/mutatingwebhook.yaml @@ -0,0 +1,78 @@ +{{- if or .Values.injectGenericVolumeBackupSidecar.enabled .Values.injectKanisterSidecar.enabled -}} +# alternate names of the services. This renders to: [ component-svc.namespace, component-svc.namespace.svc ] +{{- $altNames := list ( printf "%s-svc.%s" "controllermanager" .Release.Namespace ) ( printf "%s-svc.%s.svc" "controllermanager" .Release.Namespace ) }} +# generate ca cert with 365 days of validity +{{- $ca := genCA ( printf "%s-svc-ca" "controllermanager" ) 365 }} +# generate cert with CN="component-svc", SAN=$altNames and with 365 days of validity +{{- $cert := genSignedCert ( printf "%s-svc" "controllermanager" ) nil $altNames 365 $ca }} +apiVersion: v1 +kind: Secret +type: kubernetes.io/tls +metadata: + name: controllermanager-certs + labels: +{{ include "helm.labels" . | indent 4 }} +{{/* +This resource uses the Helm-generated CA/Cert. In case of installation to OpenShift, +it should be moved to Helm hook to avoid an upgrade loop in the OpenShift operator. +*/}} +{{- if .Values.scc.create }} + annotations: + "helm.sh/hook": "pre-install,pre-upgrade" +{{- end }} +data: + tls.crt: {{ $cert.Cert | b64enc }} + tls.key: {{ $cert.Key | b64enc }} +--- +apiVersion: admissionregistration.k8s.io/v1 +kind: MutatingWebhookConfiguration +metadata: + labels: +{{ include "helm.labels" . | indent 4 }} +{{/* +This resource uses the Helm-generated CA/Cert. In case of installation to OpenShift, +it should be moved to Helm hook to avoid an upgrade loop in the OpenShift operator. +*/}} +{{- if .Values.scc.create }} + annotations: + "helm.sh/hook": "pre-install,pre-upgrade" +{{- end }} + namespace: {{ .Release.Namespace }} + name: k10-sidecar-injector +webhooks: +- name: k10-sidecar-injector.kasten.io + admissionReviewVersions: ["v1", "v1beta1"] + failurePolicy: Ignore + sideEffects: None + clientConfig: + service: + name: controllermanager-svc + namespace: {{ .Release.Namespace }} + path: "/k10/mutate" + port: 443 + caBundle: {{ b64enc $ca.Cert }} + rules: + - operations: ["CREATE", "UPDATE"] + apiGroups: ["*"] + apiVersions: ["v1"] + resources: ["deployments", "statefulsets", "deploymentconfigs"] +{{- if .Values.injectGenericVolumeBackupSidecar.enabled }} + {{- if .Values.injectGenericVolumeBackupSidecar.namespaceSelector }} + namespaceSelector: + {{- toYaml .Values.injectGenericVolumeBackupSidecar.namespaceSelector | nindent 4 }} + {{- end }} + {{- if .Values.injectGenericVolumeBackupSidecar.objectSelector }} + objectSelector: + {{- toYaml .Values.injectGenericVolumeBackupSidecar.objectSelector | nindent 4 }} + {{- end }} +{{- else if .Values.injectKanisterSidecar.enabled }} + {{- if .Values.injectKanisterSidecar.namespaceSelector }} + namespaceSelector: + {{- toYaml .Values.injectKanisterSidecar.namespaceSelector | nindent 4 }} + {{- end }} + {{- if .Values.injectKanisterSidecar.objectSelector }} + objectSelector: + {{- toYaml .Values.injectKanisterSidecar.objectSelector | nindent 4 }} + {{- end }} +{{- end }} +{{- end }} diff --git a/charts/kasten/k10/7.5.401/templates/networkpolicy.yaml b/charts/kasten/k10/7.5.401/templates/networkpolicy.yaml new file mode 100644 index 0000000000..98ac8d7dff --- /dev/null +++ b/charts/kasten/k10/7.5.401/templates/networkpolicy.yaml @@ -0,0 +1,273 @@ +{{- $mutating_webhook_port := default 8080 (default .Values.injectKanisterSidecar.webhookServer.port .Values.injectGenericVolumeBackupSidecar.webhookServer.port) -}} +{{- if .Values.networkPolicy.create }} +apiVersion: networking.k8s.io/v1 +kind: NetworkPolicy +metadata: + name: default-deny + namespace: {{ .Release.Namespace }} + labels: +{{ include "helm.labels" . | indent 4 }} +spec: + podSelector: {} + policyTypes: + - Ingress +{{- if eq (include "k10.isOpenShift" .) "true" }} +--- +kind: NetworkPolicy +apiVersion: networking.k8s.io/v1 +metadata: + name: allow-openshift-console-access + namespace: {{ .Release.Namespace }} +spec: + podSelector: + matchLabels: + release: {{ .Release.Name }} + ingress: + - from: + - namespaceSelector: + matchLabels: + kubernetes.io/metadata.name: openshift-console +{{- end }} +--- +kind: NetworkPolicy +apiVersion: networking.k8s.io/v1 +metadata: + name: access-k10-services + namespace: {{ .Release.Namespace }} + labels: +{{ include "helm.labels" . | indent 4 }} +spec: + podSelector: + matchLabels: + release: {{ .Release.Name }} + ingress: + - from: + - podSelector: + matchLabels: + access-k10-services: allowed + ports: + - protocol: TCP + port: {{ .Values.service.internalPort }} +--- +kind: NetworkPolicy +apiVersion: networking.k8s.io/v1 +metadata: + name: cross-services-allow + namespace: {{ .Release.Namespace }} + labels: +{{ include "helm.labels" . | indent 4 }} +spec: + podSelector: + matchLabels: + release: {{ .Release.Name }} + ingress: + - from: + - podSelector: + matchLabels: + release: {{ .Release.Name }} + ports: + - protocol: TCP + port: {{ .Values.service.internalPort }} +--- +{{/* TODO: Consider a flag to turn this off. */}} +kind: NetworkPolicy +apiVersion: networking.k8s.io/v1 +metadata: + name: allow-gateway-to-mc-external + namespace: {{ .Release.Namespace }} + labels: +{{ include "helm.labels" . | indent 4 }} +spec: + podSelector: + matchLabels: + component: controllermanager + release: {{ .Release.Name }} + ingress: + - from: + - podSelector: + matchLabels: + service: gateway + release: {{ .Release.Name }} + ports: + - protocol: TCP + port: {{ include "k10.mcExternalPort" nil }} +{{- if .Values.logging.internal }} +--- +kind: NetworkPolicy +apiVersion: networking.k8s.io/v1 +metadata: + name: logging-allow-internal + namespace: {{ .Release.Namespace }} + labels: +{{ include "helm.labels" . | indent 4 }} +spec: + podSelector: + matchLabels: + release: {{ .Release.Name }} + run: logging-svc + ingress: + - from: + - podSelector: + matchLabels: + release: {{ .Release.Name }} + ports: + # Logging input port + - protocol: TCP + port: 24224 + - protocol: TCP + port: 24225 +{{- end }} +--- +kind: NetworkPolicy +apiVersion: networking.k8s.io/v1 +metadata: + name: allow-external + namespace: {{ .Release.Namespace }} + labels: +{{ include "helm.labels" . | indent 4 }} +spec: + podSelector: + matchLabels: + service: gateway + release: {{ .Release.Name }} + ingress: + - from: + ports: + - protocol: TCP + port: {{ .Values.gateway.service.internalPort | default 8000 }} +--- +kind: NetworkPolicy +apiVersion: networking.k8s.io/v1 +metadata: + name: allow-all-api + namespace: {{ .Release.Namespace }} + labels: +{{ include "helm.labels" . | indent 4 }} +spec: + podSelector: + matchLabels: + run: aggregatedapis-svc + release: {{ .Release.Name }} + ingress: + - from: + ports: + - protocol: TCP + port: {{ .Values.service.aggregatedApiPort }} +{{- if or .Values.workerPodMetricSidecar.enabled .Values.kanisterPodMetricSidecar.enabled }} +--- +kind: NetworkPolicy +apiVersion: networking.k8s.io/v1 +metadata: + name: allow-metrics-kanister-pods + namespace: {{ .Release.Namespace }} + labels: +{{ include "helm.labels" . | indent 4 }} +spec: + podSelector: + matchLabels: + release: {{ .Release.Name }} + run: metering-svc + ingress: + - from: + - podSelector: + matchLabels: + createdBy: kanister + ports: + - protocol: TCP + port: {{ .Values.service.internalPort }} +{{- end -}} +{{- if or .Values.injectGenericVolumeBackupSidecar.enabled .Values.injectKanisterSidecar.enabled }} +--- +kind: NetworkPolicy +apiVersion: networking.k8s.io/v1 +metadata: + name: allow-mutating-webhook + namespace: {{ .Release.Namespace }} + labels: +{{ include "helm.labels" . | indent 4 }} +spec: + podSelector: + matchLabels: + release: {{ .Release.Name }} + run: controllermanager-svc + ingress: + - from: + ports: + - protocol: TCP + port: {{ $mutating_webhook_port }} +{{- end -}} +{{- if eq (include "check.dexAuth" .) "true" }} +--- +kind: NetworkPolicy +apiVersion: networking.k8s.io/v1 +metadata: + name: gateway-dex-allow + namespace: {{ .Release.Namespace }} + labels: +{{ include "helm.labels" . | indent 4 }} +spec: + podSelector: + matchLabels: + release: {{ .Release.Name }} + run: auth-svc + ingress: + - from: + - podSelector: + matchLabels: + service: gateway + release: {{ .Release.Name }} + ports: + - protocol: TCP + port: 8080 +--- +kind: NetworkPolicy +apiVersion: networking.k8s.io/v1 +metadata: + name: auth-dex-allow + namespace: {{ .Release.Namespace }} + labels: +{{ include "helm.labels" . | indent 4 }} +spec: + podSelector: + matchLabels: + release: {{ .Release.Name }} + run: auth-svc + ingress: + - from: + - podSelector: + matchLabels: + run: auth-svc + release: {{ .Release.Name }} + ports: + - protocol: TCP + port: 8080 +{{- end -}} +{{- $mainCtx := . }} +{{- $colocatedList := include "get.enabledColocatedSvcList" . | fromYaml }} +{{- range $primary, $secondaryList := $colocatedList }} +--- +kind: NetworkPolicy +apiVersion: networking.k8s.io/v1 +metadata: + name: {{ $primary }}-svc-allow-secondary-services + namespace: {{ $mainCtx.Release.Namespace }} + labels: +{{ include "helm.labels" $mainCtx | indent 4 }} +spec: + podSelector: + matchLabels: + release: {{ $mainCtx.Release.Name }} + run: {{ $primary }}-svc + ingress: + - from: + - podSelector: + matchLabels: + release: {{ $mainCtx.Release.Name }} + ports: + {{- range $skip, $secondary := $secondaryList }} + {{- $colocConfig := index (include "get.enabledColocatedServices" $mainCtx | fromYaml) $secondary }} + - protocol: TCP + port: {{ $colocConfig.port }} + {{- end -}} +{{- end -}} +{{- end -}} diff --git a/charts/kasten/k10/7.5.401/templates/ocp-ca-cert-extract-hook.yaml b/charts/kasten/k10/7.5.401/templates/ocp-ca-cert-extract-hook.yaml new file mode 100644 index 0000000000..73f39bb9c1 --- /dev/null +++ b/charts/kasten/k10/7.5.401/templates/ocp-ca-cert-extract-hook.yaml @@ -0,0 +1,218 @@ +{{- if (include "k10.ocpcacertsautoextraction" .) -}} +{{- if or .Values.secrets.dockerConfig .Values.secrets.dockerConfigPath }} +--- +apiVersion: v1 +kind: Secret +metadata: + annotations: + "helm.sh/hook": pre-install,pre-upgrade + "helm.sh/hook-weight": "1" + "helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded,hook-failed + name: {{ .Release.Name }}-extract-ocp-ca-cert-dockerconfig + namespace: {{ .Release.Namespace }} +type: kubernetes.io/dockerconfigjson +data: + .dockerconfigjson: {{ or .Values.secrets.dockerConfig ( .Values.secrets.dockerConfigPath | b64enc ) }} +{{- end }} +--- +apiVersion: v1 +kind: ServiceAccount +metadata: + annotations: + "helm.sh/hook": pre-install,pre-upgrade + "helm.sh/hook-weight": "1" + "helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded,hook-failed + name: {{ .Release.Name }}-ocp-ca-cert-extractor + namespace: {{ .Release.Namespace }} +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + annotations: + "helm.sh/hook": pre-install,pre-upgrade + "helm.sh/hook-weight": "1" + "helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded,hook-failed + name: openshift-cluster-config-reader +rules: + - apiGroups: ["config.openshift.io"] + resources: ["proxies", "apiservers"] + verbs: ["get", "list"] +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + annotations: + "helm.sh/hook": pre-install,pre-upgrade + "helm.sh/hook-weight": "1" + "helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded,hook-failed + name: openshift-config-reader + namespace: openshift-config +rules: + - apiGroups: [""] + resources: ["configmaps", "secrets"] + verbs: ["get", "list"] +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + annotations: + "helm.sh/hook": pre-install,pre-upgrade + "helm.sh/hook-weight": "1" + "helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded,hook-failed + name: openshift-ingress-operator-reader + namespace: openshift-ingress-operator +rules: + - apiGroups: [""] + resources: ["secrets"] + verbs: ["get", "list"] +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + annotations: + "helm.sh/hook": pre-install,pre-upgrade + "helm.sh/hook-weight": "1" + "helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded,hook-failed + name: openshift-kube-apiserver-reader + namespace: openshift-kube-apiserver +rules: + - apiGroups: [""] + resources: ["secrets"] + verbs: ["get", "list"] +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + annotations: + "helm.sh/hook": pre-install,pre-upgrade + "helm.sh/hook-weight": "1" + "helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded,hook-failed + name: {{ .Release.Namespace }}-configmaps-editor + namespace: {{ .Release.Namespace }} +rules: + - apiGroups: [""] + resources: ["configmaps"] + verbs: ["create", "get", "list", "watch", "patch", "update"] +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + annotations: + "helm.sh/hook": pre-install,pre-upgrade + "helm.sh/hook-weight": "2" + "helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded,hook-failed + name: read-openshift-cluster-config +subjects: + - kind: ServiceAccount + name: {{ .Release.Name }}-ocp-ca-cert-extractor + namespace: {{ .Release.Namespace }} +roleRef: + kind: ClusterRole + name: openshift-cluster-config-reader + apiGroup: rbac.authorization.k8s.io +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + annotations: + "helm.sh/hook": pre-install,pre-upgrade + "helm.sh/hook-weight": "2" + "helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded,hook-failed + name: read-openshift-config + namespace: openshift-config +subjects: + - kind: ServiceAccount + name: {{ .Release.Name }}-ocp-ca-cert-extractor + namespace: {{ .Release.Namespace }} +roleRef: + kind: Role + name: openshift-config-reader + apiGroup: rbac.authorization.k8s.io +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + annotations: + "helm.sh/hook": pre-install,pre-upgrade + "helm.sh/hook-weight": "2" + "helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded,hook-failed + name: read-openshift-ingress-operator + namespace: openshift-ingress-operator +subjects: + - kind: ServiceAccount + name: {{ .Release.Name }}-ocp-ca-cert-extractor + namespace: {{ .Release.Namespace }} +roleRef: + kind: Role + name: openshift-ingress-operator-reader + apiGroup: rbac.authorization.k8s.io +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + annotations: + "helm.sh/hook": pre-install,pre-upgrade + "helm.sh/hook-weight": "2" + "helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded,hook-failed + name: read-openshift-kube-apiserver + namespace: openshift-kube-apiserver +subjects: + - kind: ServiceAccount + name: {{ .Release.Name }}-ocp-ca-cert-extractor + namespace: {{ .Release.Namespace }} +roleRef: + kind: Role + name: openshift-kube-apiserver-reader + apiGroup: rbac.authorization.k8s.io +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + annotations: + "helm.sh/hook": pre-install,pre-upgrade + "helm.sh/hook-weight": "2" + "helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded,hook-failed + name: edit-{{ .Release.Namespace }}-configmaps + namespace: {{ .Release.Namespace }} +subjects: + - kind: ServiceAccount + name: {{ .Release.Name }}-ocp-ca-cert-extractor + namespace: {{ .Release.Namespace }} +roleRef: + kind: Role + name: {{ .Release.Namespace }}-configmaps-editor + apiGroup: rbac.authorization.k8s.io +--- +apiVersion: batch/v1 +kind: Job +metadata: + name: {{ .Release.Name }}-extract-ocp-ca-cert-job + labels: +{{ include "helm.labels" . | indent 4 }} + annotations: + "helm.sh/hook": pre-install,pre-upgrade + "helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded,hook-failed + "helm.sh/hook-weight": "3" +spec: + template: + metadata: + name: {{ .Release.Name }}-extract-ocp-ca-cert-job + labels: +{{ include "helm.labels" . | indent 8 }} + spec: + restartPolicy: Never + serviceAccountName: {{ .Release.Name }}-ocp-ca-cert-extractor + containers: + - name: {{ .Release.Name }}-extract-ocp-ca-cert-job + image: {{ include "k10.k10ToolsImage" . }} + command: ["./k10tools", "openshift", "extract-certificates"] + args: ["-n", "{{ .Release.Namespace }}", "--release-name", "{{ .Release.Name }}", "--ca-cert-configmap-name", "{{ .Values.cacertconfigmap.name }}"] + {{- if or .Values.secrets.dockerConfig .Values.secrets.dockerConfigPath }} + imagePullSecrets: + - name: {{ .Release.Name }}-extract-ocp-ca-cert-dockerconfig + {{- else if .Values.global.imagePullSecret }} + imagePullSecrets: + - name: {{ .Values.global.imagePullSecret }} + {{- end }} + backoffLimit: 0 +{{ end }} diff --git a/charts/kasten/k10/7.5.401/templates/ocp-consoleplugin.yaml b/charts/kasten/k10/7.5.401/templates/ocp-consoleplugin.yaml new file mode 100644 index 0000000000..d14f537870 --- /dev/null +++ b/charts/kasten/k10/7.5.401/templates/ocp-consoleplugin.yaml @@ -0,0 +1,27 @@ +{{- if eq (include "k10.isOpenShift" .) "true" -}} +apiVersion: console.openshift.io/v1 +kind: ConsolePlugin +metadata: + name: {{ template "k10.openShiftConsolePluginCRName" . }} + labels: +{{ include "helm.labels" . | indent 4 }} + component: {{ template "k10.openShiftConsolePluginName" . }} +spec: + displayName: Veeam Kasten Plugin + backend: + type: Service + service: + name: {{ template "k10.openShiftConsolePluginName" . }} + namespace: {{ .Release.Namespace }} + port: 9443 + basePath: / + proxy: + - alias: dashboardbff + authorization: UserToken + endpoint: + service: + name: {{ template "k10.openShiftConsolePluginProxyName" . }} + namespace: {{ .Release.Namespace }} + port: 443 + type: Service +{{- end -}} diff --git a/charts/kasten/k10/7.5.401/templates/ocp-plugin-configmap.yaml b/charts/kasten/k10/7.5.401/templates/ocp-plugin-configmap.yaml new file mode 100644 index 0000000000..1fc0582710 --- /dev/null +++ b/charts/kasten/k10/7.5.401/templates/ocp-plugin-configmap.yaml @@ -0,0 +1,28 @@ +{{- if eq (include "k10.isOpenShift" .) "true" -}} +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ include "k10.openShiftConsolePluginConfigMapName" . }} + namespace: {{ .Release.Namespace }} + labels: +{{ include "helm.labels" . | indent 4 }} + component: {{ template "k10.openShiftConsolePluginName" . }} +data: + nginx.conf: | + pid /var/run/nginx/nginx.pid; + error_log /dev/stdout info; + events {} + http { + access_log /dev/stdout; + include /etc/nginx/mime.types; + default_type application/octet-stream; + keepalive_timeout 65; + server { + listen 9443 ssl; + listen [::]:9443 ssl; + ssl_certificate /var/cert/tls.crt; + ssl_certificate_key /var/cert/tls.key; + root /ocpconsoleplugin; + } + } +{{- end -}} diff --git a/charts/kasten/k10/7.5.401/templates/ocp-plugin-deployment.yaml b/charts/kasten/k10/7.5.401/templates/ocp-plugin-deployment.yaml new file mode 100644 index 0000000000..c33c263804 --- /dev/null +++ b/charts/kasten/k10/7.5.401/templates/ocp-plugin-deployment.yaml @@ -0,0 +1,74 @@ +{{- if eq (include "k10.isOpenShift" .) "true" -}} +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ template "k10.openShiftConsolePluginName" . }} + namespace: {{ .Release.Namespace }} + labels: +{{ include "helm.labels" . | indent 4 }} + component: {{ template "k10.openShiftConsolePluginName" . }} + app.openshift.io/runtime-namespace: {{ .Release.Namespace }} +spec: + replicas: 2 + selector: + matchLabels: +{{ include "k10.common.matchLabels" . | indent 6 }} + component: {{ template "k10.openShiftConsolePluginName" . }} + template: + metadata: + {{- if .Values.scc.create }} + annotations: + openshift.io/required-scc: {{ .Release.Name }}-scc + {{- end }} + labels: +{{ include "k10.common.matchLabels" . | indent 8 }} + component: {{ template "k10.openShiftConsolePluginName" . }} + spec: + containers: + - name: {{ template "k10.openShiftConsolePluginName" . }} + image: {{ include "k10.ocpConsolePluginImage" . }} + ports: + - containerPort: 9443 + protocol: TCP + imagePullPolicy: Always + securityContext: + allowPrivilegeEscalation: false + readOnlyRootFilesystem: false + capabilities: + drop: ["ALL"] + resources: + requests: + cpu: 10m + memory: 50Mi + volumeMounts: + - name: {{ template "k10.openShiftConsolePluginTLSCertName" . }} + readOnly: true + mountPath: /var/cert + - name: {{ template "k10.openShiftConsolePluginConfigMapName" . }} + readOnly: true + mountPath: /etc/nginx/nginx.conf + subPath: nginx.conf + volumes: + - name: {{ template "k10.openShiftConsolePluginTLSCertName" . }} + secret: + secretName: {{ template "k10.openShiftConsolePluginTLSCertName" . }} + defaultMode: 420 + - name: {{ template "k10.openShiftConsolePluginConfigMapName" . }} + configMap: + name: {{ template "k10.openShiftConsolePluginConfigMapName" . }} + defaultMode: 420 + restartPolicy: Always + dnsPolicy: ClusterFirst + securityContext: + fsGroup: 1000 + runAsNonRoot: true + runAsUser: 1000 + seccompProfile: + type: RuntimeDefault + serviceAccountName: {{ template "serviceAccountName" . }} + strategy: + type: RollingUpdate + rollingUpdate: + maxUnavailable: 25% + maxSurge: 25% +{{- end -}} diff --git a/charts/kasten/k10/7.5.401/templates/ocp-plugin-proxy-configmap.yaml b/charts/kasten/k10/7.5.401/templates/ocp-plugin-proxy-configmap.yaml new file mode 100644 index 0000000000..3276a32654 --- /dev/null +++ b/charts/kasten/k10/7.5.401/templates/ocp-plugin-proxy-configmap.yaml @@ -0,0 +1,36 @@ +{{- if eq (include "k10.isOpenShift" .) "true" -}} +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ template "k10.openShiftConsolePluginProxyConfigMapName" . }} + namespace: {{ .Release.Namespace }} + labels: +{{ include "helm.labels" . | indent 4 }} + component: {{ template "k10.openShiftConsolePluginProxyName" . }} +data: + nginx.conf: | + pid /var/run/nginx/nginx.pid; + error_log /dev/stdout info; + events { + worker_connections 1024; + } + http { + access_log /dev/stdout; + server { + listen 8080; + server_name {{ template "k10.openShiftConsolePluginProxyName" . }}.dashboardbff; + location / { + proxy_pass http://dashboardbff-svc.{{ .Release.Namespace }}:8000; + } + } + server { + listen 9443 ssl; + server_name {{ template "k10.openShiftConsolePluginProxyName" . }}.dashboardbff; + ssl_certificate /etc/nginx/ssl/tls.crt; + ssl_certificate_key /etc/nginx/ssl/tls.key; + location / { + proxy_pass http://dashboardbff-svc.{{ .Release.Namespace }}:8000; + } + } + } +{{- end -}} diff --git a/charts/kasten/k10/7.5.401/templates/ocp-plugin-proxy-deployment.yaml b/charts/kasten/k10/7.5.401/templates/ocp-plugin-proxy-deployment.yaml new file mode 100644 index 0000000000..931b6cc694 --- /dev/null +++ b/charts/kasten/k10/7.5.401/templates/ocp-plugin-proxy-deployment.yaml @@ -0,0 +1,67 @@ +{{- if eq (include "k10.isOpenShift" .) "true" -}} +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ template "k10.openShiftConsolePluginProxyName" . }} + namespace: {{ .Release.Namespace }} + labels: +{{ include "helm.labels" . | indent 4 }} + component: {{ template "k10.openShiftConsolePluginProxyName" . }} +spec: + replicas: 1 + selector: + matchLabels: +{{ include "k10.common.matchLabels" . | indent 6 }} + component: {{ template "k10.openShiftConsolePluginProxyName" . }} + template: + metadata: + {{- if .Values.scc.create }} + annotations: + openshift.io/required-scc: {{ .Release.Name }}-scc + {{- end }} + labels: +{{ include "k10.common.matchLabels" . | indent 8 }} + component: {{ template "k10.openShiftConsolePluginProxyName" . }} + spec: + containers: + - image: {{ include "k10.ocpConsolePluginImage" . }} + imagePullPolicy: Always + securityContext: + allowPrivilegeEscalation: false + readOnlyRootFilesystem: false + capabilities: + drop: ["ALL"] + name: nginx + ports: + - containerPort: 8080 + name: http + protocol: TCP + - containerPort: 9443 + name: https + protocol: TCP + resources: + requests: + cpu: 10m + memory: 50Mi + volumeMounts: + - mountPath: /etc/nginx + name: {{ template "k10.openShiftConsolePluginProxyConfigMapName" . }} + - mountPath: /etc/nginx/ssl + name: {{ template "k10.openShiftConsolePluginProxyTLSCertName" . }} + volumes: + - name: {{ template "k10.openShiftConsolePluginProxyConfigMapName" . }} + configMap: + defaultMode: 420 + name: {{ template "k10.openShiftConsolePluginProxyConfigMapName" . }} + - name: {{ template "k10.openShiftConsolePluginProxyTLSCertName" . }} + secret: + defaultMode: 420 + secretName: {{ template "k10.openShiftConsolePluginProxyTLSCertName" . }} + securityContext: + fsGroup: 1000 + runAsNonRoot: true + runAsUser: 1000 + seccompProfile: + type: RuntimeDefault + serviceAccountName: {{ template "serviceAccountName" . }} +{{- end -}} diff --git a/charts/kasten/k10/7.5.401/templates/ocp-plugin-proxy-service.yaml b/charts/kasten/k10/7.5.401/templates/ocp-plugin-proxy-service.yaml new file mode 100644 index 0000000000..c608e65137 --- /dev/null +++ b/charts/kasten/k10/7.5.401/templates/ocp-plugin-proxy-service.yaml @@ -0,0 +1,25 @@ +{{- if eq (include "k10.isOpenShift" .) "true" -}} +apiVersion: v1 +kind: Service +metadata: + annotations: + service.beta.openshift.io/serving-cert-secret-name: {{ template "k10.openShiftConsolePluginProxyTLSCertName" . }} + name: {{ template "k10.openShiftConsolePluginProxyName" . }} + namespace: {{ .Release.Namespace }} + labels: +{{ include "helm.labels" . | indent 4 }} + component: {{ template "k10.openShiftConsolePluginProxyName" . }} +spec: + selector: +{{ include "k10.common.matchLabels" . | indent 4 }} + component: {{ template "k10.openShiftConsolePluginProxyName" . }} + ports: + - name: http + port: 80 + protocol: TCP + targetPort: http + - name: https + port: 443 + protocol: TCP + targetPort: https +{{- end -}} diff --git a/charts/kasten/k10/7.5.401/templates/ocp-plugin-service.yaml b/charts/kasten/k10/7.5.401/templates/ocp-plugin-service.yaml new file mode 100644 index 0000000000..460c6b4ec9 --- /dev/null +++ b/charts/kasten/k10/7.5.401/templates/ocp-plugin-service.yaml @@ -0,0 +1,23 @@ +{{- if eq (include "k10.isOpenShift" .) "true" -}} +apiVersion: v1 +kind: Service +metadata: + annotations: + service.alpha.openshift.io/serving-cert-secret-name: {{ template "k10.openShiftConsolePluginTLSCertName" . }} + name: {{ template "k10.openShiftConsolePluginName" . }} + namespace: {{ .Release.Namespace }} + labels: +{{ include "helm.labels" . | indent 4 }} + component: {{ template "k10.openShiftConsolePluginName" . }} +spec: + ports: + - name: tcp + protocol: TCP + port: 9443 + targetPort: 9443 + selector: +{{ include "k10.common.matchLabels" . | indent 4 }} + component: {{ template "k10.openShiftConsolePluginName" . }} + type: ClusterIP + sessionAffinity: None +{{- end -}} diff --git a/charts/kasten/k10/7.5.401/templates/prometheus-configmap.yaml b/charts/kasten/k10/7.5.401/templates/prometheus-configmap.yaml new file mode 100644 index 0000000000..0c07738a57 --- /dev/null +++ b/charts/kasten/k10/7.5.401/templates/prometheus-configmap.yaml @@ -0,0 +1,75 @@ +{{ include "check.validatePrometheusConfig" .}} +{{- if .Values.prometheus.server.enabled -}} +{{- $cluster_domain := "" -}} +{{- with .Values.cluster.domainName -}} + {{- $cluster_domain = printf ".%s" . -}} +{{- end -}} +{{- $rbac := .Values.prometheus.rbac.create -}} +kind: ConfigMap +apiVersion: v1 +metadata: + labels: +{{ include "helm.labels" . | indent 4 }} + namespace: {{ .Release.Namespace }} + name: {{ .Release.Name }}-{{ .Values.prometheus.server.configMapOverrideName }} +data: + prometheus.yml: | + global: + scrape_interval: 1m + scrape_timeout: 10s + evaluation_interval: 1m + scrape_configs: + - job_name: httpServiceDiscovery + http_sd_configs: + - url: {{ printf "http://metering-svc.%s.svc%s:8000/v0/listScrapeTargets" .Release.Namespace $cluster_domain }} +{{- if or .Values.workerPodMetricSidecar.enabled .Values.kanisterPodMetricSidecar.enabled }} + - job_name: pushAggregator + honor_timestamps: true + metrics_path: /v0/push-metric-agg/metrics + static_configs: + - targets: + - {{ printf "metering-svc.%s.svc%s:8000" .Release.Namespace $cluster_domain }} +{{- end -}} +{{- if .Values.prometheus.scrapeCAdvisor }} + - job_name: 'kubernetes-cadvisor' + scheme: https + tls_config: + ca_file: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt + bearer_token_file: /var/run/secrets/kubernetes.io/serviceaccount/token + kubernetes_sd_configs: + - role: node + relabel_configs: + - action: labelmap + regex: __meta_kubernetes_node_label_(.+) + - target_label: __address__ + replacement: kubernetes.default.svc:443 + - source_labels: [__meta_kubernetes_node_name] + regex: (.+) + target_label: __metrics_path__ + replacement: /api/v1/nodes/${1}/proxy/metrics/cadvisor +{{- end}} + - job_name: prometheus + metrics_path: {{ .Values.prometheus.server.baseURL }}metrics + static_configs: + - targets: + - "localhost:9090" + labels: + app: prometheus + component: server + - job_name: k10-pods + scheme: http + metrics_path: /metrics + kubernetes_sd_configs: + - role: pod + namespaces: + own_namespace: true + selectors: + - role: pod + label: "component=executor" + relabel_configs: + - action: labelmap + regex: __meta_kubernetes_pod_label_(.+) + - source_labels: [__meta_kubernetes_pod_container_port_number] + action: keep + regex: 8\d{3} +{{- end -}} diff --git a/charts/kasten/k10/7.5.401/templates/prometheus-scc.yaml b/charts/kasten/k10/7.5.401/templates/prometheus-scc.yaml new file mode 100644 index 0000000000..4d039ef00e --- /dev/null +++ b/charts/kasten/k10/7.5.401/templates/prometheus-scc.yaml @@ -0,0 +1,41 @@ +{{- if .Values.scc.create }} +kind: SecurityContextConstraints +apiVersion: security.openshift.io/v1 +metadata: + labels: +{{ include "helm.labels" . | indent 4 }} + name: {{ .Release.Name }}-prometheus-server +allowPrivilegedContainer: false +allowHostNetwork: false +allowHostDirVolumePlugin: true +allowHostPorts: true +allowHostPID: false +allowHostIPC: false +readOnlyRootFilesystem: false +requiredDropCapabilities: +- CHOWN +- KILL +- MKNOD +- SETUID +- SETGID +defaultAddCapabilities: [] +allowedCapabilities: [] +priority: 0 +runAsUser: + type: MustRunAsNonRoot +seLinuxContext: + type: RunAsAny +fsGroup: + type: RunAsAny +supplementalGroups: + type: RunAsAny +volumes: +- configMap +- downwardAPI +- emptyDir +- persistentVolumeClaim +- projected +- secret +users: + - system:serviceaccount:{{.Release.Namespace}}:prometheus-server +{{- end }} diff --git a/charts/kasten/k10/7.5.401/templates/prometheus-service.yaml b/charts/kasten/k10/7.5.401/templates/prometheus-service.yaml new file mode 100644 index 0000000000..03cea0cc48 --- /dev/null +++ b/charts/kasten/k10/7.5.401/templates/prometheus-service.yaml @@ -0,0 +1,25 @@ +{{/* Template to generate service spec for v0 rest services */}} +{{- if .Values.prometheus.server.enabled -}} +{{- $postfix := default .Release.Name .Values.ingress.urlPath -}} +{{- $os_postfix := default .Release.Name .Values.route.path -}} +{{- $service_port := .Values.prometheus.server.service.servicePort -}} +apiVersion: v1 +kind: Service +metadata: + namespace: {{ .Release.Namespace }} + name: {{ include "k10.prometheus.service.name" . }}-exp + labels: +{{ include "helm.labels" $ | indent 4 }} + component: {{ include "k10.prometheus.service.name" . }} + run: {{ include "k10.prometheus.service.name" . }} +spec: + ports: + - name: http + protocol: TCP + port: {{ $service_port }} + targetPort: 9090 + selector: + app: {{ include "k10.prometheus.name" . }} + component: {{ .Values.prometheus.server.name }} + release: {{ .Release.Name }} +{{- end -}} diff --git a/charts/kasten/k10/7.5.401/templates/rbac.yaml b/charts/kasten/k10/7.5.401/templates/rbac.yaml new file mode 100644 index 0000000000..2c2c12b5b7 --- /dev/null +++ b/charts/kasten/k10/7.5.401/templates/rbac.yaml @@ -0,0 +1,386 @@ +{{- $main := . -}} +{{- $apiDomain := include "apiDomain" . -}} + +{{- $actionsAPIs := splitList " " (include "k10.actionsAPIs" .) -}} +{{- $aggregatedAPIs := splitList " " (include "k10.aggregatedAPIs" .) -}} +{{- $appsAPIs := splitList " " (include "k10.appsAPIs" .) -}} +{{- $authAPIs := splitList " " (include "k10.authAPIs" .) -}} +{{- $configAPIs := splitList " " (include "k10.configAPIs" .) -}} +{{- $distAPIs := splitList " " (include "k10.distAPIs" .) -}} +{{- $reportingAPIs := splitList " " (include "k10.reportingAPIs" .) -}} +{{- $isVapApplicable := include "vap.check" . -}} + +{{- if .Values.rbac.create }} +kind: ClusterRoleBinding +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + labels: +{{ include "helm.labels" . | indent 4 }} + name: {{ .Release.Namespace }}-{{ template "serviceAccountName" . }}-cluster-admin +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: cluster-admin +subjects: +- kind: ServiceAccount + name: {{ template "serviceAccountName" . }} + namespace: {{ .Release.Namespace }} +{{- if not ( eq (include "meteringServiceAccountName" .) (include "serviceAccountName" .) )}} +- kind: ServiceAccount + name: {{ template "meteringServiceAccountName" . }} + namespace: {{ .Release.Namespace }} +{{- end }} +--- +kind: ClusterRole +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + labels: +{{ include "helm.labels" . | indent 4 }} +{{ include "k10.defaultRBACLabels" . | indent 4 }} + name: {{ .Release.Name }}-admin +rules: +- apiGroups: +{{- range sortAlpha (concat $aggregatedAPIs $configAPIs $reportingAPIs) }} + - {{ . }}.{{ $apiDomain }} +{{- end }} + resources: + - "*" + verbs: + - "*" +- apiGroups: + - cr.kanister.io + resources: + - '*' + verbs: + - '*' +- apiGroups: + - "" + resources: + - namespaces + verbs: + - create + - get + - list +--- +kind: Role +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + labels: +{{ include "helm.labels" . | indent 4 }} +{{ include "k10.defaultRBACLabels" . | indent 4 }} + name: {{ .Release.Name }}-ns-admin + namespace: {{ .Release.Namespace }} +rules: +- apiGroups: + - "apps" + resources: + - deployments + verbs: + - get + - update + - watch + - list +- apiGroups: + - "" + resources: + - pods + verbs: + - get + - create + - delete + - list +- apiGroups: + - "apik10.kasten.io" + resources: + - k10s + verbs: + - list + - patch +- apiGroups: + - "" + resources: + - persistentvolumeclaims + verbs: + - get +- apiGroups: + - "" + resources: + - secrets + verbs: + - create + - delete + - get + - list + - update +- apiGroups: + - "" + resources: + - configmaps + verbs: + - create + - delete + - get + - list + - update +- apiGroups: + - "batch" + resources: + - jobs + verbs: + - get +- apiGroups: + - "" + resources: + - services + verbs: + - create + - get + - delete +- apiGroups: + - "networking.k8s.io" + resources: + - networkpolicies + verbs: + - get + - create + - list + - delete +- apiGroups: + - "" + resources: + - endpoints + verbs: + - list + - get +--- +kind: ClusterRole +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + labels: +{{ include "helm.labels" . | indent 4 }} +{{ include "k10.defaultRBACLabels" . | indent 4 }} + name: {{ .Release.Name }}-mc-admin +rules: +- apiGroups: +{{- range sortAlpha (concat $authAPIs $configAPIs $distAPIs) }} + - {{ . }}.{{ $apiDomain }} +{{- end }} + resources: + - "*" + verbs: + - "*" +- apiGroups: + - "" + resources: + - secrets + verbs: + - "*" +--- +kind: ClusterRole +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + labels: +{{ include "helm.labels" . | indent 4 }} +{{ include "k10.defaultRBACLabels" . | indent 4 }} + name: {{ .Release.Name }}-basic +rules: +- apiGroups: +{{- range sortAlpha $actionsAPIs }} + - {{ . }}.{{ $apiDomain }} +{{- end }} + resources: + - {{ include "k10.backupActions" $main}} + - {{ include "k10.backupActionsDetails" $main}} + - {{ include "k10.restoreActions" $main}} + - {{ include "k10.restoreActionsDetails" $main}} + - {{ include "k10.exportActions" $main}} + - {{ include "k10.exportActionsDetails" $main}} + - {{ include "k10.cancelActions" $main}} + - {{ include "k10.runActions" $main}} + - {{ include "k10.runActionsDetails" $main}} +{{- if eq $isVapApplicable "true" }} + - {{ include "k10.importActions" $main}} + - {{ include "k10.importActionsDetails" $main}} +{{- end }} + verbs: + - "*" +- apiGroups: +{{- range sortAlpha $appsAPIs }} + - {{ . }}.{{ $apiDomain }} +{{- end }} + resources: + - {{ include "k10.restorePoints" $main}} + - {{ include "k10.restorePointsDetails" $main}} + - {{ include "k10.applications" $main}} + - {{ include "k10.applicationsDetails" $main}} + verbs: + - "*" +- apiGroups: + - "" + resources: + - namespaces + verbs: + - get +- apiGroups: +{{- range sortAlpha $configAPIs }} + - {{ . }}.{{ $apiDomain }} +{{- end }} + resources: + - {{ include "k10.policies" $main}} + verbs: + - "*" +--- +kind: ClusterRole +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + labels: +{{ include "helm.labels" . | indent 4 }} +{{ include "k10.defaultRBACLabels" . | indent 4 }} + name: {{ .Release.Name }}-config-view +rules: +- apiGroups: +{{- range sortAlpha $configAPIs }} + - {{ . }}.{{ $apiDomain }} +{{- end }} + resources: + - {{ include "k10.auditconfigs" $main}} + - {{ include "k10.profiles" $main}} + - {{ include "k10.policies" $main}} + - {{ include "k10.policypresets" $main}} + - {{ include "k10.transformsets" $main}} + - {{ include "k10.blueprintbindings" $main}} + - {{ include "k10.storagesecuritycontexts" $main}} + - {{ include "k10.storagesecuritycontextbindings" $main}} + verbs: + - get + - list +--- +kind: ClusterRoleBinding +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + labels: +{{ include "helm.labels" . | indent 4 }} + name: {{ .Release.Namespace }}-{{ template "serviceAccountName" . }}-admin +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: {{ .Release.Name }}-admin +subjects: +- apiGroup: rbac.authorization.k8s.io + kind: Group + name: k10:admins +{{- range .Values.auth.k10AdminUsers }} +- apiGroup: rbac.authorization.k8s.io + kind: User + name: {{ . }} +{{- end }} +{{- range default .Values.auth.groupAllowList .Values.auth.k10AdminGroups }} +- apiGroup: rbac.authorization.k8s.io + kind: Group + name: {{ . }} +{{- end }} +--- +kind: RoleBinding +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + labels: +{{ include "helm.labels" . | indent 4 }} + name: {{ .Release.Namespace }}-{{ template "serviceAccountName" . }}-ns-admin + namespace: {{ .Release.Namespace }} +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: {{ .Release.Name }}-ns-admin +subjects: +- apiGroup: rbac.authorization.k8s.io + kind: Group + name: k10:admins +{{- range .Values.auth.k10AdminUsers }} +- apiGroup: rbac.authorization.k8s.io + kind: User + name: {{ . }} +{{- end }} +{{- range default .Values.auth.groupAllowList .Values.auth.k10AdminGroups }} +- apiGroup: rbac.authorization.k8s.io + kind: Group + name: {{ . }} +{{- end }} +--- +kind: ClusterRoleBinding +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + labels: +{{ include "helm.labels" . | indent 4 }} + name: {{ .Release.Namespace }}-{{ template "serviceAccountName" . }}-mc-admin +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: {{ .Release.Name }}-mc-admin +subjects: + - apiGroup: rbac.authorization.k8s.io + kind: Group + name: k10:admins +{{- range .Values.auth.k10AdminUsers }} + - apiGroup: rbac.authorization.k8s.io + kind: User + name: {{ . }} +{{- end }} +{{- range default .Values.auth.groupAllowList .Values.auth.k10AdminGroups }} + - apiGroup: rbac.authorization.k8s.io + kind: Group + name: {{ . }} +{{- end }} +{{- end }} +{{- if and .Values.rbac.create (not .Values.prometheus.rbac.create) }} +--- +kind: Role +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + labels: +{{ include "helm.labels" . | indent 4 }} +{{ include "k10.defaultRBACLabels" . | indent 4 }} + name: {{ .Release.Name }}-prometheus-server + namespace: {{ .Release.Namespace }} +rules: +- apiGroups: + - "" + resources: + - nodes + - nodes/proxy + - nodes/metrics + - services + - endpoints + - pods + - ingresses + - configmaps + verbs: + - get + - list + - watch +- apiGroups: + - extensions + - networking.k8s.io + resources: + - ingresses/status + - ingresses + verbs: + - get + - list + - watch +--- +kind: RoleBinding +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + labels: +{{ include "helm.labels" . | indent 4 }} + name: {{ .Release.Namespace }}-{{ template "serviceAccountName" . }}-prometheus-server + namespace: {{ .Release.Namespace }} +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: {{ .Release.Name }}-prometheus-server +subjects: + - kind: ServiceAccount + name: prometheus-server + namespace: {{ .Release.Namespace }} +{{- end }} diff --git a/charts/kasten/k10/7.5.401/templates/route.yaml b/charts/kasten/k10/7.5.401/templates/route.yaml new file mode 100644 index 0000000000..1ecd244bed --- /dev/null +++ b/charts/kasten/k10/7.5.401/templates/route.yaml @@ -0,0 +1,36 @@ +{{- $route := .Values.route -}} +{{- if $route.enabled -}} +{{ include "authEnabled.check" . }} +apiVersion: route.openshift.io/v1 +kind: Route +metadata: + name: {{ .Release.Name }}-route + {{- with $route.annotations }} + namespace: {{ .Release.Namespace }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} + labels: +{{ include "helm.labels" . | indent 4 }} + {{- with $route.labels }} + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + host: {{ $route.host }} + path: /{{ default .Release.Name $route.path | trimPrefix "/" | trimSuffix "/" }}/ + port: + targetPort: http + to: + kind: Service + name: gateway + weight: 100 + {{- if $route.tls.enabled }} + tls: + {{- if $route.tls.insecureEdgeTerminationPolicy }} + insecureEdgeTerminationPolicy: {{ $route.tls.insecureEdgeTerminationPolicy }} + {{- end }} + {{- if $route.tls.termination }} + termination: {{ $route.tls.termination }} + {{- end }} + {{- end }} +{{- end -}} diff --git a/charts/kasten/k10/7.5.401/templates/secrets.yaml b/charts/kasten/k10/7.5.401/templates/secrets.yaml new file mode 100644 index 0000000000..0a040e2c0b --- /dev/null +++ b/charts/kasten/k10/7.5.401/templates/secrets.yaml @@ -0,0 +1,257 @@ +{{- include "enforce.singlecloudcreds" . -}} +{{- include "enforce.singleazurecreds" . -}} +{{- include "check.validateImagePullSecrets" . -}} +{{- if and (eq (include "check.awscreds" . ) "true") (not (eq (include "check.awsSecretName" . ) "true")) }} +apiVersion: v1 +kind: Secret +metadata: + labels: +{{ include "helm.labels" . | indent 4 }} + namespace: {{ .Release.Namespace }} + name: aws-creds +type: Opaque +data: + aws_access_key_id: {{ required "secrets.awsAccessKeyId field is required!" .Values.secrets.awsAccessKeyId | b64enc | quote }} + aws_secret_access_key: {{ required "secrets.awsSecretAccessKey field is required!" .Values.secrets.awsSecretAccessKey | b64enc | quote }} +{{- if .Values.secrets.awsIamRole }} + role: {{ .Values.secrets.awsIamRole | trim | b64enc | quote }} +{{- end }} +{{- end }} +{{- if or .Values.secrets.dockerConfig .Values.secrets.dockerConfigPath }} +--- +apiVersion: v1 +kind: Secret +metadata: + labels: +{{ include "helm.labels" . | indent 4 }} + namespace: {{ .Release.Namespace }} + name: k10-ecr +type: kubernetes.io/dockerconfigjson +data: + .dockerconfigjson: {{ or .Values.secrets.dockerConfig ( .Values.secrets.dockerConfigPath | b64enc ) }} +{{- end }} +{{- if and (eq (include "check.googlecreds" .) "true") ( not (eq (include "check.googleCredsSecret" .) "true")) }} +--- +apiVersion: v1 +kind: Secret +metadata: + labels: +{{ include "helm.labels" . | indent 4 }} + namespace: {{ .Release.Namespace }} + name: google-secret +type: Opaque +data: + kasten-gke-sa.json: {{ .Values.secrets.googleApiKey }} +{{- if eq (include "check.googleproject" .) "true" }} + kasten-gke-project: {{ .Values.secrets.googleProjectId | b64enc }} +{{- end }} +{{- end }} +{{- if eq (include "check.azurecreds" .) "true" }} +--- +apiVersion: v1 +kind: Secret +metadata: + labels: +{{ include "helm.labels" . | indent 4 }} + namespace: {{ .Release.Namespace }} + name: azure-creds +type: Opaque +data: + {{- if not (eq (include "check.azuresecret" .) "true" ) }} + {{- if or (eq (include "check.azureMSIWithClientID" .) "true") (eq (include "check.azureClientSecretCreds" .) "true") }} + azure_client_id: {{ required "secrets.azureClientId field is required!" .Values.secrets.azureClientId | b64enc | quote }} + {{- end }} + {{- if eq (include "check.azureClientSecretCreds" .) "true" }} + azure_tenant_id: {{ required "secrets.azureTenantId field is required!" .Values.secrets.azureTenantId | b64enc | quote }} + azure_client_secret: {{ required "secrets.azureClientSecret field is required!" .Values.secrets.azureClientSecret | b64enc | quote }} + {{- end }} + {{- end }} + azure_resource_group: {{ default "" .Values.secrets.azureResourceGroup | b64enc | quote }} + azure_subscription_id: {{ default "" .Values.secrets.azureSubscriptionID | b64enc | quote }} + azure_resource_manager_endpoint: {{ default "" .Values.secrets.azureResourceMgrEndpoint | b64enc | quote }} + entra_id_endpoint: {{ default "" (default .Values.secrets.azureADEndpoint .Values.secrets.microsoftEntraIDEndpoint) | b64enc | quote }} + entra_id_resource_id: {{ default "" (default .Values.secrets.azureADResourceID .Values.secrets.microsoftEntraIDResourceID) | b64enc | quote }} + azure_cloud_env_id: {{ default "" .Values.secrets.azureCloudEnvID | b64enc | quote }} +{{- end }} +{{- if and (eq (include "check.vspherecreds" .) "true") (not (eq (include "check.vsphereClientSecret" . ) "true")) }} +--- +apiVersion: v1 +kind: Secret +metadata: + labels: +{{ include "helm.labels" . | indent 4 }} + namespace: {{ .Release.Namespace }} + name: vsphere-creds +type: Opaque +data: + vsphere_endpoint: {{ required "secrets.vsphereEndpoint field is required!" .Values.secrets.vsphereEndpoint | b64enc | quote }} + vsphere_username: {{ required "secrets.vsphereUsername field is required!" .Values.secrets.vsphereUsername | b64enc | quote }} + vsphere_password: {{ required "secrets.vspherePassword field is required!" .Values.secrets.vspherePassword | b64enc | quote }} +{{- end }} +{{- if and (eq (include "basicauth.check" .) "true") (not .Values.auth.basicAuth.secretName) }} +--- +apiVersion: v1 +kind: Secret +metadata: + labels: +{{ include "helm.labels" . | indent 4 }} + name: k10-basic-auth + namespace: {{ .Release.Namespace }} +data: + auth: {{ required "auth.basicAuth.htpasswd field is required!" .Values.auth.basicAuth.htpasswd | b64enc | quote}} +type: Opaque +{{- end }} +{{- if .Values.auth.tokenAuth.enabled }} +--- +apiVersion: v1 +kind: Secret +metadata: + labels: +{{ include "helm.labels" . | indent 4 }} + name: k10-token-auth + namespace: {{ .Release.Namespace }} +data: + auth: {{ "true" | b64enc | quote}} +type: Opaque +{{- end }} +{{- if and .Values.auth.oidcAuth.enabled (not .Values.auth.oidcAuth.secretName) }} +--- +apiVersion: v1 +kind: Secret +metadata: + labels: +{{ include "helm.labels" . | indent 4 }} + name: {{ include "k10.oidcSecretName" .}} + namespace: {{ .Release.Namespace }} +data: + provider-url: {{ required "auth.oidcAuth.providerURL field is required!" .Values.auth.oidcAuth.providerURL | b64enc | quote }} + redirect-url: {{ required "auth.oidcAuth.redirectURL field is required!" .Values.auth.oidcAuth.redirectURL | b64enc | quote }} +{{- if not .Values.auth.oidcAuth.clientSecretName }} + client-id: {{ required "auth.oidcAuth.clientID field is required!" .Values.auth.oidcAuth.clientID | b64enc | quote }} + client-secret: {{ required "auth.oidcAuth.clientSecret field is required!" .Values.auth.oidcAuth.clientSecret | b64enc | quote }} +{{- end }} + scopes: {{ required "auth.oidcAuth.scopes field is required!" .Values.auth.oidcAuth.scopes | b64enc | quote }} + prompt: {{ default "select_account" .Values.auth.oidcAuth.prompt | b64enc | quote }} + usernameClaim: {{ default "sub" .Values.auth.oidcAuth.usernameClaim | b64enc | quote }} + usernamePrefix: {{ default "" .Values.auth.oidcAuth.usernamePrefix | b64enc | quote }} + groupClaim: {{ default "" .Values.auth.oidcAuth.groupClaim | b64enc | quote }} + groupPrefix: {{ default "" .Values.auth.oidcAuth.groupPrefix | b64enc | quote }} + sessionDuration: {{ default "1h" .Values.auth.oidcAuth.sessionDuration | b64enc | quote }} +{{- if .Values.auth.oidcAuth.refreshTokenSupport }} + refreshTokenSupport: {{ "true" | b64enc | quote }} +{{- else }} + refreshTokenSupport: {{ "false" | b64enc | quote }} +{{ end }} +stringData: + groupAllowList: |- +{{- range $.Values.auth.groupAllowList }} + {{ . -}} +{{ end }} + logout-url: {{ default "" .Values.auth.oidcAuth.logoutURL | b64enc | quote }} +type: Opaque +{{- end }} +{{- if and (.Values.auth.openshift.enabled) (and (not .Values.auth.openshift.clientSecretName) (not .Values.auth.openshift.clientSecret)) }} +--- +apiVersion: v1 +kind: Secret +type: kubernetes.io/service-account-token +metadata: + name: {{ include "get.openshiftServiceAccountSecretName" . }} + annotations: + kubernetes.io/service-account.name: {{ include "get.openshiftServiceAccountName" . | quote }} +{{- end }} +{{- if and (.Values.auth.openshift.enabled) (not .Values.auth.openshift.secretName) }} +{{ $dashboardURL := required "auth.openshift.dashboardURL field is required!" .Values.auth.openshift.dashboardURL }} +{{ $redirectURL := trimSuffix "/" (trimSuffix (default .Release.Name .Values.ingress.urlPath) (trimSuffix "/" $dashboardURL)) | b64enc | quote }} +{{- if .Values.route.enabled }} +{{ $redirectURL := trimSuffix "/" (trimSuffix (default .Release.Name .Values.route.path) (trimSuffix "/" $dashboardURL)) | b64enc | quote }} +{{- end }} +--- +apiVersion: v1 +kind: Secret +metadata: + labels: +{{ include "helm.labels" . | indent 4 }} + name: {{ include "k10.oidcSecretName" .}} + namespace: {{ .Release.Namespace }} +data: + provider-url: {{ printf "%s/dex" (trimSuffix "/" $dashboardURL) | b64enc | quote }} + redirect-url: {{ $redirectURL }} + client-id: {{ (printf "kasten") | b64enc | quote }} + client-secret: {{ (printf "kastensecret") | b64enc | quote }} + scopes: {{ (printf "groups profile email") | b64enc | quote }} + prompt: {{ (printf "select_account") | b64enc | quote }} + usernameClaim: {{ default "email" .Values.auth.openshift.usernameClaim | b64enc | quote }} + usernamePrefix: {{ default "" .Values.auth.openshift.usernamePrefix | b64enc | quote }} + groupClaim: {{ default "groups" .Values.auth.openshift.groupClaim | b64enc | quote }} + groupPrefix: {{ default "" .Values.auth.openshift.groupPrefix | b64enc | quote }} +stringData: + groupAllowList: |- +{{- range $.Values.auth.groupAllowList }} + {{ . -}} +{{ end }} +type: Opaque +{{- end }} +{{- if and .Values.auth.ldap.enabled (not .Values.auth.ldap.secretName) }} +--- +apiVersion: v1 +kind: Secret +metadata: + labels: +{{ include "helm.labels" . | indent 4 }} + name: {{ include "k10.oidcSecretName" .}} + namespace: {{ .Release.Namespace }} +data: + provider-url: {{ required "auth.ldap.dashboardURL field is required!" (printf "%s/dex" (trimSuffix "/" .Values.auth.ldap.dashboardURL)) | b64enc | quote }} + {{- if .Values.route.enabled }} + redirect-url: {{ required "auth.ldap.dashboardURL field is required!" (trimSuffix "/" (trimSuffix (default .Release.Name .Values.route.path) (trimSuffix "/" .Values.auth.ldap.dashboardURL))) | b64enc | quote }} + {{- else }} + redirect-url: {{ required "auth.ldap.dashboardURL field is required!" (trimSuffix "/" (trimSuffix (default .Release.Name .Values.ingress.urlPath) (trimSuffix "/" .Values.auth.ldap.dashboardURL))) | b64enc | quote }} + {{- end }} + client-id: {{ (printf "kasten") | b64enc | quote }} + client-secret: {{ (printf "kastensecret") | b64enc | quote }} + scopes: {{ (printf "groups profile email") | b64enc | quote }} + prompt: {{ (printf "select_account") | b64enc | quote }} + usernameClaim: {{ default "email" .Values.auth.ldap.usernameClaim | b64enc | quote }} + usernamePrefix: {{ default "" .Values.auth.ldap.usernamePrefix | b64enc | quote }} + groupClaim: {{ default "groups" .Values.auth.ldap.groupClaim | b64enc | quote }} + groupPrefix: {{ default "" .Values.auth.ldap.groupPrefix | b64enc | quote }} +stringData: + groupAllowList: |- +{{- range $.Values.auth.groupAllowList }} + {{ . -}} +{{ end }} +type: Opaque +{{- end }} +{{- if and .Values.auth.ldap.enabled (not .Values.auth.ldap.bindPWSecretName) }} +--- +apiVersion: v1 +kind: Secret +metadata: + labels: +{{ include "helm.labels" . | indent 4 }} + name: k10-dex + namespace: {{ .Release.Namespace }} +data: + bindPW: {{ required "auth.ldap.bindPW field is required!" .Values.auth.ldap.bindPW | b64enc | quote }} +type: Opaque +{{- end }} +{{- if eq (include "check.primaryKey" . ) "true" }} +--- +apiVersion: v1 +kind: Secret +metadata: + labels: +{{ include "helm.labels" . | indent 4 }} + name: k10-encryption-primary-key + namespace: {{ .Release.Namespace }} +data: + {{- if .Values.encryption.primaryKey.awsCmkKeyId }} + awscmkkeyid: {{ default "" .Values.encryption.primaryKey.awsCmkKeyId | trim | b64enc | quote }} + {{- end }} + {{- if .Values.encryption.primaryKey.vaultTransitKeyName }} + vaulttransitkeyname: {{ default "" .Values.encryption.primaryKey.vaultTransitKeyName | trim | b64enc | quote }} + vaulttransitpath: {{ default "transit" .Values.encryption.primaryKey.vaultTransitPath | trim | b64enc | quote }} + {{- end }} +type: Opaque +{{- end }} diff --git a/charts/kasten/k10/7.5.401/templates/secure_deployment.tpl b/charts/kasten/k10/7.5.401/templates/secure_deployment.tpl new file mode 100644 index 0000000000..a3126d8d93 --- /dev/null +++ b/charts/kasten/k10/7.5.401/templates/secure_deployment.tpl @@ -0,0 +1,16 @@ +{{/* +This file is used to fail the helm deployment if certain values are set which are +not compatible with a secure deployment. + +A secure deployment is defined as one of the following: +- Iron Bank +- FIPS +*/}} + +{{/* Iron Bank */}} +{{- include "k10.fail.ironbankGrafana" . -}} +{{- include "k10.fail.ironbankPrometheus" . -}} +{{- include "k10.fail.ironbankRHMarketplace" . -}} + +{{/* FIPS */}} +{{- include "k10.fail.fipsPrometheus" . -}} diff --git a/charts/kasten/k10/7.5.401/templates/serviceaccount.yaml b/charts/kasten/k10/7.5.401/templates/serviceaccount.yaml new file mode 100644 index 0000000000..d24d91e16a --- /dev/null +++ b/charts/kasten/k10/7.5.401/templates/serviceaccount.yaml @@ -0,0 +1,48 @@ +{{- if and .Values.serviceAccount.create ( not .Values.metering.awsMarketplace ) }} +kind: ServiceAccount +apiVersion: v1 +metadata: +{{- if .Values.secrets.awsIamRole }} + annotations: + eks.amazonaws.com/role-arn: {{ .Values.secrets.awsIamRole }} +{{- end }} +{{- if eq (include "check.azureFederatedIdentity" .) "true" }} + annotations: + azure.workload.identity/client-id: {{ .Values.secrets.azureClientId | quote }} +{{- end }} + labels: +{{ include "helm.labels" . | indent 4 }} + name: {{ template "serviceAccountName" . }} + namespace: {{ .Release.Namespace }} +{{- end }} +{{- if and (not ( eq (include "meteringServiceAccountName" .) (include "serviceAccountName" .))) ( not .Values.metering.awsManagedLicense ) .Values.metering.serviceAccount.create }} +--- +kind: ServiceAccount +apiVersion: v1 +metadata: +{{- if .Values.metering.awsMarketPlaceIamRole }} + annotations: + eks.amazonaws.com/role-arn: {{ .Values.metering.awsMarketPlaceIamRole }} +{{- end }} + labels: +{{ include "helm.labels" . | indent 4 }} + name: {{ template "meteringServiceAccountName" . }} + namespace: {{ .Release.Namespace }} +{{- end }} +{{- if and (.Values.auth.openshift.enabled) (not .Values.auth.openshift.serviceAccount) }} +{{- if or (.Values.auth.openshift.clientSecret) (.Values.auth.openshift.clientSecretName) }} + {{ fail "auth.openshift.serviceAccount is required when auth.openshift.clientSecret or auth.openshift.clientSecretName is used "}} +{{- end }} +--- +kind: ServiceAccount +apiVersion: v1 +metadata: + name: {{ include "k10.dexServiceAccountName" . }} + namespace: {{ .Release.Namespace }} + annotations: + {{- $dashboardURL := (trimSuffix "/" (required "auth.openshift.dashboardURL field is required" .Values.auth.openshift.dashboardURL)) -}} + {{- if (not (hasSuffix .Release.Name $dashboardURL)) }} + {{ fail "auth.openshift.dashboardURL should end with the K10's release name" }} + {{- end }} + serviceaccounts.openshift.io/oauth-redirecturi.dex: {{ printf "%s/dex/callback" $dashboardURL }} +{{- end }} diff --git a/charts/kasten/k10/7.5.401/templates/v0services.yaml b/charts/kasten/k10/7.5.401/templates/v0services.yaml new file mode 100644 index 0000000000..3b882c1630 --- /dev/null +++ b/charts/kasten/k10/7.5.401/templates/v0services.yaml @@ -0,0 +1,120 @@ +{{/* Template to generate service spec for v0 rest services */}} +{{- $container_port := .Values.service.internalPort -}} +{{- $service_port := .Values.service.externalPort -}} +{{- $aggregated_api_port := .Values.service.aggregatedApiPort -}} +{{- $postfix := default .Release.Name .Values.ingress.urlPath -}} +{{- $colocated_services := include "get.enabledColocatedServices" . | fromYaml -}} +{{- $exposed_services := include "get.enabledExposedServices" . | splitList " " -}} +{{- $os_postfix := default .Release.Name .Values.route.path -}} +{{- $main_context := . -}} +{{ $service_list := append (include "get.enabledRestServices" . | splitList " ") "frontend" }} +{{- range $service_list }} + {{- $exposed_service := (has . $exposed_services) }} + {{- $mc_exposed_service := (eq . "controllermanager") }} + {{ if not (hasKey $colocated_services . ) }} +apiVersion: v1 +kind: Service +metadata: + namespace: {{ $.Release.Namespace }} + name: {{ . }}-svc + labels: +{{ include "helm.labels" $ | indent 4 }} + component: {{ . }} + run: {{ . }}-svc +spec: + ports: + - name: http + protocol: TCP + port: {{ $service_port }} + targetPort: {{ $container_port }} + {{- if and (eq . "controllermanager") (or $.Values.injectGenericVolumeBackupSidecar.enabled $.Values.injectKanisterSidecar.enabled) }} + - name: https + protocol: TCP + port: 443 + targetPort: {{ default 8080 (default $.Values.injectKanisterSidecar.webhookServer.port $.Values.injectGenericVolumeBackupSidecar.webhookServer.port) }} + {{- end }} +{{- $colocatedList := include "get.enabledColocatedSvcList" $main_context | fromYaml }} +{{- range $skip, $secondary := index $colocatedList . }} + {{- $colocConfig := index (include "get.enabledColocatedServices" $main_context | fromYaml) $secondary }} + - name: {{ $secondary }} + protocol: TCP + port: {{ $colocConfig.port }} + targetPort: {{ $colocConfig.port }} +{{- end }} +{{- if eq . "logging" }} + - name: logging + protocol: TCP + port: 24224 + targetPort: 24224 + - name: logging-metrics + protocol: TCP + port: 24225 + targetPort: 24225 +{{- end }} +{{- if eq . "controllermanager" }} + - name: mc-http + protocol: TCP + port: {{ include "k10.mcExternalPort" nil }} + targetPort: {{ include "k10.mcExternalPort" nil }} +{{- end }} + selector: + run: {{ . }}-svc +--- + {{ end }}{{/* if not (hasKey $colocated_services $k10_service ) */}} +{{ end -}}{{/* range append (include "get.enabledRestServices" . | splitList " ") "frontend" */}} +{{- range append (include "get.enabledServices" . | splitList " ") "kanister" }} +{{- if eq . "gateway" -}}{{- continue -}}{{- end -}} +apiVersion: v1 +kind: Service +metadata: + namespace: {{ $.Release.Namespace }} + name: {{ . }}-svc + labels: +{{ include "helm.labels" $ | indent 4 }} + component: {{ . }} + run: {{ . }}-svc +spec: + ports: + {{- if eq . "aggregatedapis" }} + - name: http + port: 443 + protocol: TCP + targetPort: {{ $aggregated_api_port }} + {{- else }} + - name: http + protocol: TCP + port: {{ $service_port }} + targetPort: {{ $container_port }} + {{- end }} +{{- $colocatedList := include "get.enabledColocatedSvcList" $main_context | fromYaml }} +{{- range $skip, $secondary := index $colocatedList . }} + {{- $colocConfig := index (include "get.enabledColocatedServices" . | fromYaml) $secondary }} + - name: {{ $secondary }} + protocol: TCP + port: {{ $colocConfig.port }} + targetPort: {{ $colocConfig.port }} +{{- end }} + selector: + run: {{ . }}-svc +--- +{{ end -}} +{{- if eq (include "check.dexAuth" .) "true" }} +apiVersion: v1 +kind: Service +metadata: + name: dex + namespace: {{ $.Release.Namespace }} + labels: +{{ include "helm.labels" $ | indent 4 }} + component: dex + run: auth-svc +spec: + ports: + - name: http + port: {{ $service_port }} + protocol: TCP + targetPort: 8080 + selector: + run: auth-svc + type: ClusterIP +{{ end -}} diff --git a/charts/kasten/k10/7.5.401/templates/vap.yaml b/charts/kasten/k10/7.5.401/templates/vap.yaml new file mode 100644 index 0000000000..f9d0b08252 --- /dev/null +++ b/charts/kasten/k10/7.5.401/templates/vap.yaml @@ -0,0 +1,151 @@ +{{- $isVapApplicable := include "vap.check" . -}} +--- +{{- if eq $isVapApplicable "true" }} +apiVersion: admissionregistration.k8s.io/v1beta1 +kind: ValidatingAdmissionPolicy +metadata: + name: "kasten.policy.permissions" +spec: + failurePolicy: Fail + matchConstraints: + resourceRules: + - apiGroups: ["config.kio.kasten.io"] + apiVersions: ["v1alpha1"] + operations: ["CREATE", "UPDATE"] + resources: ["policies"] + validations: + {{/* The CEL expressions below are specific checks for restore parameters + allowed to be defined for a basic user, specifically the following: + 1) Target namespace - It should be defined compulsorily for a basic user and + should be equal to the policy namespace + 2) RestoreClusterResources - It should be set to false if defined.*/}} + - expression: "object.spec.actions.filter(x, x.action == 'restore').all(r, has(r.restoreParameters.targetNamespace) && r.restoreParameters.targetNamespace == object.metadata.namespace)" + message: "target namespace is a required field in the restore action and must be the same as the policy namespace" + - expression: "object.spec.actions.filter(x, x.action == 'restore').all(r, !has(r.restoreParameters.restoreClusterResources) || r.restoreParameters.restoreClusterResources == false)" + message: "user is not allowed to restore cluster resources. RestoreClusterResources under restore action should be set to false" + {{/* For export, import, backup and restore actions, the CEL expression checks + if the user has permissions to 'get' the 'profile' resource, which is + referenced under 'actionParameters' in the Policy spec. Since profile + is optional, expression first checks if profile is defined. For backup, + since backupParameters are optional, expression first checks that they exist.*/}} + - expression: "object.spec.actions.filter(x, x.action == 'export').all(e, !has(e.exportParameters.profile) || authorizer.group('config.kio.kasten.io').resource('profiles').namespace(e.exportParameters.profile.namespace).name(e.exportParameters.profile.name).check('get').allowed())" + message: "user does not have permission to access profile specified in export action" + - expression: "object.spec.actions.filter(x, x.action == 'import').all(i, !has(i.importParameters.profile) || authorizer.group('config.kio.kasten.io').resource('profiles').namespace(i.importParameters.profile.namespace).name(i.importParameters.profile.name).check('get').allowed())" + message: "user does not have permission to access profile specified in import action" + - expression: "object.spec.actions.filter(x, x.action == 'backup').all(b, !has(b.backupParameters) || !has(b.backupParameters.profile) || authorizer.group('config.kio.kasten.io').resource('profiles').namespace(b.backupParameters.profile.namespace).name(b.backupParameters.profile.name).check('get').allowed())" + message: "user does not have permission to access profile specified in backup action" + - expression: "object.spec.actions.filter(x, x.action == 'restore').all(r, !has(r.restoreParameters.profile) || authorizer.group('config.kio.kasten.io').resource('profiles').namespace(r.restoreParameters.profile.namespace).name(r.restoreParameters.profile.name).check('get').allowed())" + message: "user does not have permission to access profile specified in restore action" + {{/* If it is an export action, the CEL expression checks if the user has + permissions to 'get' the 'blockModeProfile' resource, which is referenced + under 'exportParameters' in the Policy spec. Since blockModeProfile is optional, + expression first checks if blockModeProfile is defined.*/}} + - expression: "object.spec.actions.filter(x, x.action == 'export').all(e, !has(e.exportParameters.blockModeProfile) || authorizer.group('config.kio.kasten.io').resource('profiles').namespace(e.exportParameters.blockModeProfile.namespace).name(e.exportParameters.blockModeProfile.name).check('get').allowed())" + message: "user does not have permission to access block mode profile specified in export action" + {{/* If it is an restore action, the CEL expression checks if the user has + permissions to 'get' the 'artifactOverrideProfile' resource, which is referenced + under 'restoreParameters' in the Policy spec. Since artifactOverrideProfile is optional, + expression first checks if trtifactOverrideProfile is defined.*/}} + - expression: "object.spec.actions.filter(x, x.action == 'restore').all(r, !has(r.restoreParameters.artifactOverrideProfile) || authorizer.group('config.kio.kasten.io').resource('profiles').namespace(r.restoreParameters.artifactOverrideProfile.namespace).name(r.restoreParameters.artifactOverrideProfile.name).check('get').allowed())" + message: "user does not have permission to access artifactOverrideProfile specified in restore action" + {{/* // TODO: https://kasten.atlassian.net/browse/K10-27480 + This blocks creation of restore actions which refer ONLY inline transforms + in the same transform object. This will still allow restore actions which + refer to inline AND transformRef in the same transform object. This is a + rare case and policy validation will fail eventually for such policies.*/}} + {{/* If it is a restore action, the CEL expression checks if the user has + specified inline transforms. If restore paramaters have transforms, the CEL + checks if the transform object has transformSetRef set. This ensures that + transforms with ONLY inline transforms are rejected. Since transforms + parameter is optional, expression first checks if transforms are defined.*/}} + - expression: "object.spec.actions.filter(x, x.action == 'restore').all(r, !has(r.restoreParameters.transforms) || r.restoreParameters.transforms.all(t, has(t.transformSetRef)))" + message: "user does not have permission to create the inline transformSet specified in restore action" + {{/* If it is a restore action, the CEL expression checks if the user has + permissions to 'get' the transform resource which is referenced + under 'restoreParameters' in the Policy spec. Since transforms are optional, + expression first checks if transforms are defined. If transforms are defined, + a transformSetRef is mandatory.*/}} + - expression: "object.spec.actions.filter(x, x.action == 'restore').all(r, !has(r.restoreParameters.transforms) || r.restoreParameters.transforms.all(t, has(t.transformSetRef) && authorizer.group('config.kio.kasten.io').resource('transformsets').namespace(t.transformSetRef.namespace).name(t.transformSetRef.name).check('get').allowed()))" + message: "user does not have permission to access transformSet specified in restore action" + {{/* The CEL expressions below checks if the user has 'get' access to the + blueprint referenced under 'hooks' in the 'restoreParameters' during a + restore action. Since hooks are optional, each expression first checks if + hooks are defined and in turn if it is an onSuccess, onFailure or preHook + respectively before checking the blueprint 'get' permissions.*/}} + - expression: {{printf "object.spec.actions.filter(x, x.action == 'restore').all(r, !has(r.restoreParameters.hooks) || !has(r.restoreParameters.hooks.preHook) || authorizer.group('cr.kanister.io').resource('blueprints').namespace('%s').name(r.restoreParameters.hooks.preHook.blueprint).check('get').allowed())" .Release.Namespace}} + message: "user does not have permission to access blueprint specified in the pre-action hook specified in the restore action" + - expression: {{printf "object.spec.actions.filter(x, x.action == 'restore').all(r, !has(r.restoreParameters.hooks) || !has(r.restoreParameters.hooks.onSuccess) || authorizer.group('cr.kanister.io').resource('blueprints').namespace('%s').name(r.restoreParameters.hooks.onSuccess.blueprint).check('get').allowed())" .Release.Namespace}} + message: "user does not have permission to access blueprint specified in the onSuccess hook specified in the restore action" + - expression: {{printf "object.spec.actions.filter(x, x.action == 'restore').all(r, !has(r.restoreParameters.hooks) || !has(r.restoreParameters.hooks.onFailure) || authorizer.group('cr.kanister.io').resource('blueprints').namespace('%s').name(r.restoreParameters.hooks.onFailure.blueprint).check('get').allowed())" .Release.Namespace}} + message: "user does not have permission to access blueprint specified in the onFailure hook specified in the restore action" + {{/* The CEL expressions below checks if the user has 'get' access to the + blueprint referenced under 'hooks' in the 'backupParameters' during a + restore action. Since hooks are optional, each expression first checks if + hooks are defined and in turn if it is an onSuccess, onFailure or preHook + respectively before checking the blueprint 'get' permissions. For backup, + since backupParameters are optional, expression first checks that they exist.*/}} + - expression: {{printf "object.spec.actions.filter(x, x.action == 'backup').all(b,!has(b.backupParameters) || !has(b.backupParameters.hooks) || !has(b.backupParameters.hooks.preHook) || authorizer.group('cr.kanister.io').resource('blueprints').namespace('%s').name(b.backupParameters.hooks.preHook.blueprint).check('get').allowed())" .Release.Namespace}} + message: "user does not have permission to access blueprint specified in the pre-action hook specified in the backup action" + - expression: {{printf "object.spec.actions.filter(x, x.action == 'backup').all(b, !has(b.backupParameters) || !has(b.backupParameters.hooks) || !has(b.backupParameters.hooks.onSuccess) || authorizer.group('cr.kanister.io').resource('blueprints').namespace('%s').name(b.backupParameters.hooks.onSuccess.blueprint).check('get').allowed())" .Release.Namespace}} + message: "user does not have permission to access blueprint specified in the onSuccess hook specified in the backup action" + - expression: {{printf "object.spec.actions.filter(x, x.action == 'backup').all(b, !has(b.backupParameters) || !has(b.backupParameters.hooks) || !has(b.backupParameters.hooks.onFailure) || authorizer.group('cr.kanister.io').resource('blueprints').namespace('%s').name(b.backupParameters.hooks.onFailure.blueprint).check('get').allowed())" .Release.Namespace}} + message: "user does not have permission to access blueprint specified in the onFailure hook specified in the backup action" + {{/* The CEL expressions below checks if the user has 'get' access to the + blueprint referenced under 'hooks' in the 'exportParameters' during a + restore action. Since hooks are optional, each expression first checks if + hooks are defined and in turn if it is an onSuccess, onFailure or preHook + respectively before checking the blueprint 'get' permissions.*/}} + - expression: {{printf "object.spec.actions.filter(x, x.action == 'export').all(e, !has(e.exportParameters.hooks) || !has(e.exportParameters.hooks.preHook) || authorizer.group('cr.kanister.io').resource('blueprints').namespace('%s').name(e.exportParameters.hooks.preHook.blueprint).check('get').allowed())" .Release.Namespace}} + message: "user does not have permission to access blueprint specified in the pre-action hook specified in the export action" + - expression: {{printf "object.spec.actions.filter(x, x.action == 'export').all(e, !has(e.exportParameters.hooks) || !has(e.exportParameters.hooks.onSuccess) || authorizer.group('cr.kanister.io').resource('blueprints').namespace('%s').name(e.exportParameters.hooks.onSuccess.blueprint).check('get').allowed())" .Release.Namespace}} + message: "user does not have permission to access blueprint specified in the onSuccess hook specified in the export action" + - expression: {{printf "object.spec.actions.filter(x, x.action == 'export').all(e, !has(e.exportParameters.hooks) || !has(e.exportParameters.hooks.onFailure) || authorizer.group('cr.kanister.io').resource('blueprints').namespace('%s').name(e.exportParameters.hooks.onFailure.blueprint).check('get').allowed())" .Release.Namespace}} + message: "user does not have permission to access blueprint specified in the onFailure hook specified in the export action" + {{/*The CEL expressions below check if the user has 'get' access to the + optional field - ActionPodSpec referenced in the policy under the respective + actions - backup, export, import and restore. For backup, since backupParameters + are optional, expression first checks that they exist.*/}} + - expression: "object.spec.actions.filter(x, x.action == 'backup').all(b, !has(b.backupParameters) || !has(b.backupParameters.actionPodSpec) || authorizer.group('config.kio.kasten.io').resource('actionpodspecs').namespace(b.backupParameters.actionPodSpec.namespace).name(b.backupParameters.actionPodSpec.name).check('get').allowed())" + message: "user does not have permission to access ActionPodSpec specified in the backup action" + - expression: "object.spec.actions.filter(x, x.action == 'export').all(e, !has(e.exportParameters.actionPodSpec) || authorizer.group('config.kio.kasten.io').resource('actionpodspecs').namespace(e.exportParameters.actionPodSpec.namespace).name(e.exportParameters.actionPodSpec.name).check('get').allowed())" + message: "user does not have permission to access ActionPodSpec specified in the export action" + - expression: "object.spec.actions.filter(x, x.action == 'import').all(i, !has(i.importParameters.actionPodSpec) || authorizer.group('config.kio.kasten.io').resource('actionpodspecs').namespace(i.importParameters.actionPodSpec.namespace).name(i.importParameters.actionPodSpec.name).check('get').allowed())" + message: "user does not have permission to access ActionPodSpec specified in the import action" + - expression: "object.spec.actions.filter(x, x.action == 'restore').all(r, !has(r.restoreParameters.actionPodSpec) || authorizer.group('config.kio.kasten.io').resource('actionpodspecs').namespace(r.restoreParameters.actionPodSpec.namespace).name(r.restoreParameters.actionPodSpec.name).check('get').allowed())" + message: "user does not have permission to access ActionPodSpec specified in the restore action" + {{/* For all policies, the CEL expression checks if the user has permissions + to 'get' the 'policy preset' resource, which is referenced in the Policy spec. + Since presetRef is optional, expression first checks if presetRef is defined.*/}} + - expression: "!has(object.spec.presetRef) || authorizer.group('config.kio.kasten.io').resource('policypresets').namespace(object.spec.presetRef.namespace).name(object.spec.presetRef.name).check('get').allowed()" + message: "user does not have permission to access policy preset specified in policy spec" + {{/* For export and import actions, the CEL expression checks + if the user has permissions to 'get' the 'migrationToken' secret, which is + referenced under 'actionParameters' in the Policy spec. Since migrationToken + is optional, expression first checks if migrationToken is defined.*/}} + - expression: "object.spec.actions.filter(x, x.action == 'export').all(e, !has(e.exportParameters.migrationToken) || authorizer.group('').resource('secrets').namespace(e.exportParameters.migrationToken.namespace).name(e.exportParameters.migrationToken.name).check('get').allowed())" + message: "user does not have permission to access migration token specified in export action" + - expression: "object.spec.actions.filter(x, x.action == 'import').all(i, !has(i.importParameters.migrationToken) || authorizer.group('').resource('secrets').namespace(i.importParameters.migrationToken.namespace).name(i.importParameters.migrationToken.name).check('get').allowed())" + message: "user does not have permission to access migration token specified in import action" + {{/*The CEL expression checks if user has 'get' access to the optional field + imageRepoProfile in the backup action, if the field has been defined. For backup, + since backupParameters are option, expression first checks that they exist.*/}} + - expression: "object.spec.actions.filter(x, x.action == 'backup').all(b, !has(b.backupParameters) || !has(b.backupParameters.imageRepoProfile) || authorizer.group('config.kio.kasten.io').resource('profiles').namespace(b.backupParameters.imageRepoProfile.namespace).name(b.backupParameters.imageRepoProfile.name).check('get').allowed())" + message: "user does not have access to ImageRepoProfile specified in the backup action" +{{- end }} +--- +{{- if eq $isVapApplicable "true" }} +apiVersion: admissionregistration.k8s.io/v1beta1 +kind: ValidatingAdmissionPolicyBinding +metadata: + name: "kasten.policy.permissions.binding" +spec: + policyName: "kasten.policy.permissions" + validationActions: [Deny] + matchResources: + namespaceSelector: + matchExpressions: + - key: kubernetes.io/metadata.name + operator: NotIn + values: + - {{ $.Release.Namespace }} +{{- end }} diff --git a/charts/kasten/k10/7.5.401/templates/workloadIdentityFederation.tpl b/charts/kasten/k10/7.5.401/templates/workloadIdentityFederation.tpl new file mode 100644 index 0000000000..75296e98b0 --- /dev/null +++ b/charts/kasten/k10/7.5.401/templates/workloadIdentityFederation.tpl @@ -0,0 +1,10 @@ +{{/* +This file is used to fail the helm deployment if Workload Identity settings are not +compatible. +*/}} +{{- include "validate.gwif.idp.type" . -}} +{{- include "validate.gwif.idp.aud" . -}} + + + + diff --git a/charts/kasten/k10/7.5.401/templates/{values}/prometheus/charts/{charts}/values/prometheus_values.tpl b/charts/kasten/k10/7.5.401/templates/{values}/prometheus/charts/{charts}/values/prometheus_values.tpl new file mode 100644 index 0000000000..fbf1f2ef60 --- /dev/null +++ b/charts/kasten/k10/7.5.401/templates/{values}/prometheus/charts/{charts}/values/prometheus_values.tpl @@ -0,0 +1,186 @@ +{{/* + With some of K10's features being provided by external Helm charts, those Helm + charts need to be configured to work with K10. + + Unfortunately, some of the values needed to configure the subcharts aren't + accessible to the subcharts (only global.* and chart_name.* are accessible). + + This means the values need to be duplicated, making the configuration of K10 + quite cumbersome for users (the same setting has to be provided in multiple + places, making it easy to misconfigure one thing or another). + + Alternatively, the subchart's templates could be customized to read global.* + values instead. However, this means upgrading the subchart is quite burdensome + since the customizations have to be re-applied to the upgraded chart. This is + even less tenable with the frequency with which chart updates are needed. + + With this in mind, this template was specially crafted to be able to read K10 + values and update the values that will be passed to the subchart. + + --- + + To accomplish this, Helm's template parsing and rendering order is exploited. + + Helm allows parent charts to override templates in subcharts. This is done by + parsing templates with lower precedence first (templates that are more deeply + nested than others). This allows templates with higher precedence to redefine + templates with lower precedence. + + Helm also renders templates in this same order. This template exploits this + ordering in order to set subchart values before the subchart's templates are + rendered, having the same effect as the user setting the values. + + WARNING: The name and directory structure of this template was carefully + selected to ensure that it is rendered before other templates! +*/}} + +{{- if .Values.prometheus.server.enabled }} +{{- $prometheus_scoped_values := (dict "Chart" (dict "Name" "prometheus") "Release" .Release "Values" .Values.prometheus) -}} + +{{- $prometheus_name := (include "prometheus.name" $prometheus_scoped_values) -}} +{{- $prometheus_prefix := "/k10/prometheus/" -}} +{{- $release_name := .Release.Name -}} + +{{- /*** PROMETHEUS LABELS ***/ -}} +{{- $_ := mergeOverwrite .Values.prometheus + (dict + "commonMetaLabels" (dict + "app.kubernetes.io/name" $prometheus_name + "app.kubernetes.io/instance" $release_name + ) + ) +-}} + +{{- /*** PROMETHEUS SERVER OVERRIDES ***/ -}} +{{- $fullnameOverride := .Values.prometheus.server.fullnameOverride | default "prometheus-server" -}} +{{- $clusterRoleNameOverride := .Values.prometheus.server.clusterRoleNameOverride | default (printf "%s-%s" .Release.Name $fullnameOverride) -}} + +{{- /* Merge global pod labels with any prometheus-specific labels, where the latter is of highest priority */ -}} +{{- $podLabels := merge (dict) (.Values.prometheus.server.podLabels | default dict) (.Values.global.podLabels) -}} + +{{- /* Merge global pod labels with any prometheus-specific annotations, where the latter is of highest priority */ -}} +{{- $podAnnotations := merge (dict) (.Values.prometheus.server.podAnnotations | default dict) (.Values.global.podAnnotations) -}} +{{- if .Values.scc.create -}} + {{- $podAnnotations = merge (dict "openshift.io/required-scc" (printf "%s-prometheus-server" .Release.Name)) $podAnnotations -}} +{{- end -}} +{{- $_ := mergeOverwrite .Values.prometheus.server + (dict + "baseURL" (.Values.prometheus.server.baseURL | default $prometheus_prefix) + "prefixURL" (.Values.prometheus.server.prefixURL | default $prometheus_prefix | trimSuffix "/") + + "clusterRoleNameOverride" $clusterRoleNameOverride + "configMapOverrideName" "k10-prometheus-config" + "fullnameOverride" $fullnameOverride + "podLabels" $podLabels + "podAnnotations" $podAnnotations + ) +-}} + +{{- /*** K10 PROMETHEUS CONFIGMAP-RELOAD IMAGE *** + - global.airgapped.repository + - global.image.registry + - global.image.tag + - global.images.configmap-reload +*/ -}} +{{- $prometheus_configmap_reload_image := (dict + "registry" (.Values.global.airgapped.repository | default .Values.global.image.registry) + "repository" "configmap-reload" + "tag" (include "get.k10ImageTag" $) +) -}} + +{{- if (index .Values.global.images "configmap-reload") -}} + {{- $prometheus_configmap_reload_image = ( + include "k10.splitImage" (dict + "image" (index .Values.global.images "configmap-reload") + "path" "global.images.configmap-reload" + ) + ) | fromJson + -}} +{{- end -}} + +{{- if .Values.global.azMarketPlace -}} + {{- $prometheus_configmap_reload_image = (dict + "registry" .Values.global.azure.images.configmapreload.registry + "repository" .Values.global.azure.images.configmapreload.image + "tag" .Values.global.azure.images.configmapreload.tag + ) + -}} +{{- end -}} + +{{- $_ := mergeOverwrite .Values.prometheus.configmapReload.prometheus.image + (dict + "repository" (list $prometheus_configmap_reload_image.registry $prometheus_configmap_reload_image.repository | compact | join "/") + "tag" $prometheus_configmap_reload_image.tag + "digest" $prometheus_configmap_reload_image.digest + ) +-}} + +{{- /*** K10 PROMETHEUS SERVER IMAGE *** + - global.airgapped.repository + - global.image.registry + - global.image.tag + - global.images.prometheus +*/ -}} +{{- $prometheus_server_image := (dict + "registry" (.Values.global.airgapped.repository | default .Values.global.image.registry) + "repository" "prometheus" + "tag" (include "get.k10ImageTag" $) +) -}} +{{- if .Values.global.images.prometheus -}} + {{- $prometheus_server_image = ( + include "k10.splitImage" (dict + "image" .Values.global.images.prometheus + "path" "global.images.prometheus" + ) + ) | fromJson + -}} +{{- end -}} + +{{- if .Values.global.azMarketPlace -}} + {{- $prometheus_server_image = ( dict + "registry" .Values.global.azure.images.prometheus.registry + "repository" .Values.global.azure.images.prometheus.image + "tag" .Values.global.azure.images.prometheus.tag + ) + -}} +{{- end -}} + +{{- $_ := mergeOverwrite .Values.prometheus.server.image + (dict + "repository" (list $prometheus_server_image.registry $prometheus_server_image.repository | compact | join "/") + "tag" $prometheus_server_image.tag + "digest" $prometheus_server_image.digest + ) +-}} + +{{- /*** K10 IMAGE PULL SECRETS *** + - secrets.dockerConfig + - secrets.dockerConfigPath + - global.imagePullSecret +*/ -}} +{{- $image_pull_secret_names := list -}} +{{- if .Values.global.imagePullSecret -}} + {{- $image_pull_secret_names = append $image_pull_secret_names .Values.global.imagePullSecret -}} +{{- end -}} +{{- if (or .Values.secrets.dockerConfig .Values.secrets.dockerConfigPath) -}} + {{ $image_pull_secret_names = append $image_pull_secret_names "k10-ecr" -}} +{{- end -}} +{{- $image_pull_secret_names = $image_pull_secret_names | compact | uniq -}} + +{{- if $image_pull_secret_names -}} + {{- $image_pull_secrets := .Values.prometheus.imagePullSecrets | default list -}} + {{- range $name := $image_pull_secret_names -}} + {{- $image_pull_secrets = append $image_pull_secrets (dict "name" $name) -}} + {{- end -}} + {{- $_ := set .Values.prometheus "imagePullSecrets" $image_pull_secrets -}} +{{- end -}} + +{{- /*** K10 PERSISTENCE *** + - global.persistence.storageClass +*/ -}} +{{- $_ := mergeOverwrite .Values.prometheus.server.persistentVolume + (dict + "storageClass" (.Values.prometheus.server.persistentVolume.storageClass | default .Values.global.persistence.storageClass) + ) +-}} +{{- end }} diff --git a/charts/kasten/k10/7.5.401/triallicense b/charts/kasten/k10/7.5.401/triallicense new file mode 100644 index 0000000000..cfe6dd46bf --- /dev/null +++ b/charts/kasten/k10/7.5.401/triallicense @@ -0,0 +1 @@ +Y3VzdG9tZXJOYW1lOiB0cmlhbHN0YXJ0ZXItbGljZW5zZQpkYXRlRW5kOiAnMjEwMC0wMS0wMVQwMDowMDowMC4wMDBaJwpkYXRlU3RhcnQ6ICcyMDIwLTAxLTAxVDAwOjAwOjAwLjAwMFonCmZlYXR1cmVzOgogIHRyaWFsOiBudWxsCmlkOiB0cmlhbC0wOWY4MzE5Zi0xODBmLTRhOTAtOTE3My1kOTJiNzZmMTgzNWUKcHJvZHVjdDogSzEwCnJlc3RyaWN0aW9uczoKICBub2RlczogNTAwCnNlcnZpY2VBY2NvdW50S2V5OiBudWxsCnZlcnNpb246IHYxLjAuMApzaWduYXR1cmU6IEYxbnVLUFV5STJtbDJGMmV1VHdGOXNZRTZMVU5rQ3ZiR2tTV1ZkT0ZqdERCb1B6SjUyVWFsVkFmRjVmQUxpcm5BcVhkcERnYi9YcnpxSEYrTE0xS2pEMVdXUFd0ZUdXNFc1anBPSFN0T296Y0c5M0pUUHF5M2l6TVk3RmczZVFLYTZzWDhBdnFwOXArWXVBMWNwTENlQ2dsR2dnOTVzSUFmYmRMMTBmV2d2RmR6QUt4dUZLN2psRzVtbG1CRVF5R0hrYWdoZFIrVGxzeUNTNEFkbXVBOEZodVUwZnRBdXN0b1M3R2JKd1BuTFI3STFZY1Q4OW8wU2xRZEJ2Yjg2QzdKbm1OdnY0aHhiSUo5TTJvWGJPSnQ4ZnBNcjhNWFR6YWRMTWJzSndhZ3VBVHlNUWF2cExHNXRPb0U2ZE1uMVlFVDZLdWZiYy9NdThVRDVYYXlDYTdkZz09Cg== diff --git a/charts/kasten/k10/7.5.401/values.schema.json b/charts/kasten/k10/7.5.401/values.schema.json new file mode 100644 index 0000000000..b89a08e36f --- /dev/null +++ b/charts/kasten/k10/7.5.401/values.schema.json @@ -0,0 +1,3124 @@ +{ + "$schema": "https://json-schema.org/draft/2019-09/schema", + "type": "object", + "properties": { + "rbac": { + "type": "object", + "title": "RBAC configuration", + "description": "Create RBAC seetings", + "properties": { + "create": { + "title": "Enable RBAC creation", + "description": "Toggle RBAC resource creation", + "type": "boolean", + "default": true + } + } + }, + "serviceAccount": { + "type": "object", + "title": "ServiceAccount details", + "description": "Configure ServiceAccount", + "properties": { + "create": { + "type": "boolean", + "default": true, + "title": "Create a ServiceAccount", + "description": "Specifies whether a ServiceAccount should be created" + }, + "name": { + "type": "string", + "default": "", + "title": "The name of the ServiceAccount", + "description": "The name of the ServiceAccount to use. If not set and create is true, a name is derived using the release and chart names" + } + } + }, + "scc": { + "type": "object", + "title": "Security Context Constraints details", + "description": "Configure Security Context Constraints", + "properties": { + "create": { + "type": "boolean", + "default": false, + "title": "Create K10 SSC", + "description": "Whether to create a SecurityContextConstraints for K10 ServiceAccounts" + }, + "priority": { + "type": "integer", + "default": 0, + "title": "SCC priority", + "description": "Sets the SecurityContextConstraints priority" + } + } + }, + "networkPolicy": { + "type": "object", + "title": "NetworkPolicy details", + "description": "Configure NetworkPolicy", + "properties": { + "create": { + "type": "boolean", + "default": true, + "title": "Create NetworkPolicies", + "description": "Whether to create NetworkPolicies for the K10 services" + } + } + }, + "global": { + "type": "object", + "title": "Global settings", + "properties": { + "image": { + "type": "object", + "title": "K10 image configurations", + "description": "Change K10 image settings", + "properties": { + "registry": { + "type": "string", + "default": "gcr.io/kasten-images", + "title": "K10 image registry", + "description": "Change default K10 image registry" + }, + "tag": { + "type": "string", + "default": "", + "title": "K10 image tag", + "description": "Change default K10 tag" + }, + "pullPolicy": { + "type": "string", + "default": "Always", + "title": "Container images pullPolicy", + "description": "Change default pullPolicy for all the images", + "enum": [ + "IfNotPresent", + "Always", + "Never" + ] + } + } + }, + "airgapped": { + "type": "object", + "title": "Airgapped offline installation", + "description": "Configure Airgapped offline installation", + "properties": { + "repository": { + "type": "string", + "default": "", + "title": "helm repository", + "description": "The helm repository for offline (airgapped) installation" + } + } + }, + "persistence": { + "type": "object", + "title": "Persistent Volume global details", + "description": "Configure global settings for Persistent Volume", + "properties": { + "mountPath": { + "type": "string", + "default": "/mnt/k10state", + "title": "Persistent Volume global mount path", + "description": "Change default path for Persistent Volume mount" + }, + "enabled": { + "type": "boolean", + "default": true, + "title": "Enable Persistent Volume", + "description": "Create Persistent Volumes" + }, + "storageClass": { + "type": "string", + "default": "", + "title": "Persistent Volume global Storageclass", + "description": "If set to '-', dynamic provisioning is disabled. If undefined (the default) or set to null, the default provisioner is used. (e.g gp2 on AWS, standard on GKE, AWS & OpenStack)" + }, + "accessMode": { + "type": "string", + "default": "ReadWriteOnce", + "title": "Persistent Volume global AccessMode", + "description": "Change default AccessMode for Persistent Volumes", + "enum": [ + "ReadWriteOnce", + "ReadOnlyMany", + "ReadWriteMany" + ] + }, + "size": { + "type": "string", + "default": "20Gi", + "title": "Persistent Volume size", + "description": "Change default size for Persistent Volumes" + }, + "metering": { + "type": "object", + "title": "Metering service Persistent Volume details", + "description": "Configure Persistence Volume for metering service", + "properties": { + "size": { + "type": "string", + "default": "2Gi", + "title": "Metering service Persistent Volume size", + "description": "If not set, global.persistence.size is used" + } + } + }, + "catalog": { + "type": "object", + "title": "Catalog service Persistent Volume details", + "description": "Configure Persistence Volume for catalog service", + "properties": { + "size": { + "type": "string", + "default": "", + "title": "Catalog service Persistent Volume size", + "description": "If not set, global.persistence.size is used." + } + } + }, + "jobs": { + "type": "object", + "title": "Jobs service Persistent Volume details", + "description": "Configure Persistence Volume for jobs service", + "properties": { + "size": { + "type": "string", + "default": "", + "title": "Jobs service Persistent Volume size", + "description": "If not set, global.persistence.size is used." + } + } + }, + "logging": { + "type": "object", + "title": "Logging service Persistent Volume details", + "description": "Configure Persistence Volume for logging service", + "properties": { + "size": { + "type": "string", + "default": "", + "title": "Logging service Persistent Volume size", + "description": "If not set, global.persistence.size is used." + } + } + } + } + }, + "podLabels": { + "type": "object", + "default": {}, + "title": "Custom labels to be set to all Kasten Pods", + "description": "Configures custom Pod labels to be set to all Kasten Pods.", + "examples": [ + { + "foo": "bar" + } + ] + }, + "podAnnotations": { + "type": "object", + "default": {}, + "title": "Custom annotations to be set to all Kasten Pods", + "description": "Configures custom Pod annotations to be set to all Kasten Pods.", + "examples": [ + { + "foo": "bar" + } + ] + }, + "rhMarketPlace": { + "type": "boolean", + "default": false, + "title": "RedHat marketplace config", + "description": "Set it to true while generating helm operator" + }, + "images": { + "type": "object", + "title": "Global image settings", + "properties": { + "aggregatedapis": { + "type": "string", + "default": "", + "title": "Aggregatedapis service container image", + "description": "Used for packaging RedHat Operator. Setting this flag along with global.rhMarketPlace=true overrides the default image name. This flag is only for internal purposes. If not set, the image name is formed with '(global.airgapped.repository)|(global.image.registry)/:(Chart.AppVersion)|(image.tag)'" + }, + "auth": { + "type": "string", + "default": "", + "title": "Auth service container image", + "description": "Used for packaging RedHat Operator. Setting this flag along with global.rhMarketPlace=true overrides the default image name. This flag is only for internal purposes. If not set, the image name is formed with '(global.airgapped.repository)|(global.image.registry)/:(Chart.AppVersion)|(image.tag)'" + }, + "bloblifecyclemanager": { + "type": "string", + "default": "", + "title": "Bloblifecyclemanager service container image", + "description": "Used for packaging RedHat Operator. Setting this flag along with global.rhMarketPlace=true overrides the default image name. This flag is only for internal purposes. If not set, the image name is formed with '(global.airgapped.repository)|(global.image.registry)/:(Chart.AppVersion)|(image.tag)'" + }, + "catalog": { + "type": "string", + "default": "", + "title": "Catalog service container image", + "description": "Used for packaging RedHat Operator. Setting this flag along with global.rhMarketPlace=true overrides the default image name. This flag is only for internal purposes. If not set, the image name is formed with '(global.airgapped.repository)|(global.image.registry)/:(Chart.AppVersion)|(image.tag)'" + }, + "configmap-reload": { + "type": "string", + "title": "Configmap-reload service container image", + "default": "", + "description": "Used for packaging RedHat Operator. Setting this flag along with global.rhMarketPlace=true overrides the default image name. This flag is only for internal purposes." + }, + "controllermanager": { + "type": "string", + "default": "", + "title": "Controllermanager service container image", + "description": "Used for packaging RedHat Operator. Setting this flag along with global.rhMarketPlace=true overrides the default image name. This flag is only for internal purposes. If not set, the image name is formed with '(global.airgapped.repository)|(global.image.registry)/:(Chart.AppVersion)|(image.tag)'" + }, + "crypto": { + "type": "string", + "default": "", + "title": "Crypto service container image", + "description": "Used for packaging RedHat Operator. Setting this flag along with global.rhMarketPlace=true overrides the default image name. This flag is only for internal purposes. If not set, the image name is formed with '(global.airgapped.repository)|(global.image.registry)/:(Chart.AppVersion)|(image.tag)'" + }, + "dashboardbff": { + "type": "string", + "default": "", + "title": "Dashboardbff service container image", + "description": "Used for packaging RedHat Operator. Setting this flag along with global.rhMarketPlace=true overrides the default image name. This flag is only for internal purposes. If not set, the image name is formed with '(global.airgapped.repository)|(global.image.registry)/:(Chart.AppVersion)|(image.tag)'" + }, + "datamover": { + "type": "string", + "default": "", + "title": "Datamover service container image", + "description": "Used for packaging RedHat Operator. Setting this flag along with global.rhMarketPlace=true overrides the default image name. This flag is only for internal purposes." + }, + "dex": { + "type": "string", + "default": "", + "title": "Dex service container image", + "description": "Used for packaging RedHat Operator. Setting this flag along with global.rhMarketPlace=true overrides the default image name. This flag is only for internal purposes." + }, + "events": { + "type": "string", + "default": "", + "title": "Events service container image", + "description": "Used for packaging RedHat Operator. Setting this flag along with global.rhMarketPlace=true overrides the default image name. This flag is only for internal purposes. If not set, the image name is formed with '(global.airgapped.repository)|(global.image.registry)/:(Chart.AppVersion)|(image.tag)'" + }, + "executor": { + "type": "string", + "default": "", + "title": "Executor service container image", + "description": "Used for packaging RedHat Operator. Setting this flag along with global.rhMarketPlace=true overrides the default image name. This flag is only for internal purposes. If not set, the image name is formed with '(global.airgapped.repository)|(global.image.registry)/:(Chart.AppVersion)|(image.tag)'" + }, + "frontend": { + "type": "string", + "default": "", + "title": "Frontend service container image", + "description": "Used for packaging RedHat Operator. Setting this flag along with global.rhMarketPlace=true overrides the default image name. This flag is only for internal purposes. If not set, the image name is formed with '(global.airgapped.repository)|(global.image.registry)/:(Chart.AppVersion)|(image.tag)'" + }, + "gateway": { + "type": "string", + "default": "", + "title": "Gateway service container image", + "description": "Used for packaging RedHat Operator. Setting this flag along with global.rhMarketPlace=true overrides the default image name. This flag is only for internal purposes. If not set, the image name is formed with '(global.airgapped.repository)|(global.image.registry)/:(Chart.AppVersion)|(image.tag)'" + }, + "init": { + "type": "string", + "title": "Generic init container image", + "default": "", + "description": "Used for packaging RedHat Operator. Setting this flag along with global.rhMarketPlace=true overrides the default image name. This flag is only for internal purposes." + }, + "jobs": { + "type": "string", + "default": "", + "title": "Jobs service container image", + "description": "Used for packaging RedHat Operator. Setting this flag along with global.rhMarketPlace=true overrides the default image name. This flag is only for internal purposes. If not set, the image name is formed with '(global.airgapped.repository)|(global.image.registry)/:(Chart.AppVersion)|(image.tag)'" + }, + "kanister-tools": { + "type": "string", + "default": "", + "title": "Kanister-tools service container image", + "description": "Kanister-tools service container image contains set of tools, required for all kanister related operations. It is used for debug, troubleshooting, primer purposes as well" + }, + "kanister": { + "type": "string", + "default": "", + "title": "Kanister service container image", + "description": "Used for packaging RedHat Operator. Setting this flag along with global.rhMarketPlace=true overrides the default image name. This flag is only for internal purposes. If not set, the image name is formed with '(global.airgapped.repository)|(global.image.registry)/:(Chart.AppVersion)|(image.tag)'" + }, + "k10tools": { + "type": "string", + "default": "", + "title": "k10tools service container image", + "description": "Used for packaging RedHat Operator. Setting this flag along with global.rhMarketPlace=true overrides the default image name. This flag is only for internal purposes. If not set, the image name is formed with '(global.airgapped.repository)|(global.image.registry)/:(Chart.AppVersion)|(image.tag)'" + }, + "logging": { + "type": "string", + "default": "", + "title": "Logging service container image", + "description": "Used for packaging RedHat Operator. Setting this flag along with global.rhMarketPlace=true overrides the default image name. This flag is only for internal purposes. If not set, the image name is formed with '(global.airgapped.repository)|(global.image.registry)/:(Chart.AppVersion)|(image.tag)'" + }, + "metering": { + "type": "string", + "default": "", + "title": "Metering service container image", + "description": "Used for packaging RedHat Operator. Setting this flag along with global.rhMarketPlace=true overrides the default image name. This flag is only for internal purposes. If not set, the image name is formed with '(global.airgapped.repository)|(global.image.registry)/:(Chart.AppVersion)|(image.tag)'" + }, + "ocpconsoleplugin": { + "type": "string", + "default": "", + "title": "OpenShift Console Plugin container image", + "description": "Used for packaging RedHat Operator. Setting this flag along with global.rhMarketPlace=true overrides the default image name. This flag is only for internal purposes. If not set, the image name is formed with '(global.airgapped.repository)|(global.image.registry)/:(Chart.AppVersion)|(image.tag)'" + }, + "paygo_daemonset": { + "type": "string", + "default": "", + "title": "Paygo_daemonset service container image", + "description": "Used for packaging RedHat Operator. Setting this flag along with global.rhMarketPlace=true overrides the default image name. This flag is only for internal purposes." + }, + "prometheus": { + "type": "string", + "default": "", + "title": "Prometheus service container image", + "description": "Used for packaging RedHat Operator. Setting this flag along with global.rhMarketPlace=true overrides the default image name. This flag is only for internal purposes." + }, + "repositories": { + "type": "string", + "default": "", + "title": "Repositories service container image", + "description": "Used for packaging RedHat Operator. Setting this flag along with global.rhMarketPlace=true overrides the default image name. This flag is only for internal purposes. If not set, the image name is formed with '(global.airgapped.repository)|(global.image.registry)/:(Chart.AppVersion)|(image.tag)'" + }, + "state": { + "type": "string", + "default": "", + "title": "State service container image", + "description": "Used for packaging RedHat Operator. Setting this flag along with global.rhMarketPlace=true overrides the default image name. This flag is only for internal purposes. If not set, the image name is formed with '(global.airgapped.repository)|(global.image.registry)/:(Chart.AppVersion)|(image.tag)'" + }, + "upgrade": { + "type": "string", + "default": "", + "title": "Upgrade service container image", + "description": "Used for packaging RedHat Operator. Setting this flag along with global.rhMarketPlace=true overrides the default image name. This flag is only for internal purposes. If not set, the image name is formed with '(global.airgapped.repository)|(global.image.registry)/:(Chart.AppVersion)|(image.tag)'" + }, + "vbrintegrationapi": { + "type": "string", + "default": "", + "title": "Vbrintegrationapi service container image", + "description": "Used for packaging RedHat Operator. Setting this flag along with global.rhMarketPlace=true overrides the default image name. This flag is only for internal purposes." + }, + "garbagecollector": { + "type": "string", + "default": "", + "title": "Garbagecollector service container image", + "description": "Used for packaging RedHat Operator. Setting this flag along with global.rhMarketPlace=true overrides the default image name. This flag is only for internal purposes." + }, + "metric-sidecar": { + "type": "string", + "default": "", + "title": "Metric-sidecar service container image", + "description": "Used for packaging RedHat Operator. Setting this flag along with global.rhMarketPlace=true overrides the default image name. This flag is only for internal purposes." + } + } + }, + "imagePullSecret": { + "type": "string", + "default": "", + "title": "Container image pull secret", + "description": "Secret which contains docker config for private repository. Use `k10-ecr` when secrets.dockerConfigPath is used." + }, + "prometheus": { + "type": "object", + "title": "Prometheus settings", + "description": "Global prometheus settings", + "properties": { + "external": { + "type": "object", + "title": "External prometheus settings", + "description": "Configure prometheus", + "properties": { + "host": { + "type": "string", + "default": "", + "title": "External prometheus host name", + "description": "Set prometheus host name" + }, + "port": { + "type": "string", + "default": "", + "title": "External prometheus port number", + "description": "Set prometheus port number" + }, + "baseURL": { + "type": "string", + "default": "", + "title": "External prometheus baseURL", + "description": "Set prometheus baseURL" + } + } + } + } + }, + "network": { + "type": "object", + "title": "Network settings", + "description": "Global network settings", + "properties": { + "enable_ipv6": { + "type": "boolean", + "default": false, + "title": "Enable ipv6", + "description": "Set true to enable ipv6" + } + } + } + } + }, + "route": { + "type": "object", + "title": "OpenShift route configuration", + "description": "Configure OpenShift Route", + "properties": { + "enabled": { + "type": "boolean", + "default": false, + "title": "Exposed dashboard via route", + "description": "Whether the K10 dashboard should be exposed via route" + }, + "host": { + "type": "string", + "default": "", + "title": "Host name", + "description": "Set Host name for the route" + }, + "path": { + "type": "string", + "default": "", + "title": "Route path", + "description": "Set Path for the route" + }, + "annotations": { + "type": "object", + "default": {}, + "title": "Route annotations", + "description": "Set annotations for the route", + "examples": [ + { + "kubernetes.io/tls-acme": "true", + "haproxy.router.openshift.io/disable_cookies": "true", + "haproxy.router.openshift.io/balance": "roundrobin" + } + ] + }, + "labels": { + "type": "object", + "default": {}, + "title": "Route label", + "description": "Set Labels for the route resource", + "examples": [ + { + "foo": "bar" + } + ] + }, + "tls": { + "type": "object", + "title": "Route TLS configuration", + "description": "Set TLS configuration for the route", + "properties": { + "enabled": { + "type": "boolean", + "default": false, + "title": "Enable TLS", + "description": "Whether to enable TLS" + }, + "insecureEdgeTerminationPolicy": { + "type": "string", + "default": "Redirect", + "title": "Route Termination Policy", + "description": "What to do in case of an insecure traffic edge termination", + "enum": [ + "None", + "Allow", + "Redirect", + "" + ] + }, + "termination": { + "type": "string", + "default": "edge", + "title": "Termination Schema", + "description": "Set termination Schema", + "enum": [ + "edge", + "passthrough", + "reencrypt" + ] + } + } + } + } + }, + "dexImage": { + "type": "object", + "title": "Dex image config", + "description": "Specify Dex image config", + "properties": { + "registry": { + "type": "string", + "default": "ghcr.io", + "title": "Dex image registry", + "description": "Change default image registry for Dex images" + }, + "repository": { + "type": "string", + "default": "dexidp", + "title": "Dex image repository", + "description": "Change default image repository for Dex images" + }, + "image": { + "type": "string", + "default": "dex", + "title": "Dex image name", + "description": "Change default image name for Dex images" + } + } + }, + "kanisterToolsImage": { + "type": "object", + "title": "kanister tools image config", + "description": "Set kanister tools image config", + "properties": { + "registry": { + "type": "string", + "default": "ghcr.io", + "title": "kanister-tools image registry", + "description": "Change default image registry for kanister-tools images" + }, + "repository": { + "type": "string", + "default": "kanisterio", + "title": "kanister-tools image repository", + "description": "Change default image repository for kanister-tools images" + }, + "image": { + "type": "string", + "default": "kanister-tools", + "title": "Kanister tools image name", + "description": "Change default image name for kanister-tools images" + }, + "pullPolicy": { + "type": "string", + "default": "Always", + "title": "Kanister tools image pullPolicy", + "description": "Change kanister-tools image pullPolicy", + "enum": [ + "IfNotPresent", + "Always", + "Never" + ] + } + } + }, + "ingress": { + "type": "object", + "title": "Ingress configuration", + "description": "Add ingress resource configuration", + "properties": { + "annotations": { + "type": "object", + "default": {}, + "title": "Ingress annotations", + "description": "Add optional annotations to the Ingress resource" + }, + "create": { + "type": "boolean", + "default": false, + "title": "Expose dashboard via ingress", + "description": "whether the K10 dashboard should be exposed via ingress" + }, + "tls": { + "type": "object", + "title": "TLS configuration for ingress", + "description": "Set TLS configuration for ingress", + "properties": { + "enabled": { + "type": "boolean", + "default": false, + "title": "Enable TLS", + "description": "Configures a TLS use for ingress.host" + }, + "secretName": { + "type": "string", + "default": "", + "title": "Optional TLS secret name", + "description": "Specifies the name of the secret to configure ingress.tls[].secretName" + } + } + }, + "name": { + "type": "string", + "default": "", + "title": "Ingress name", + "description": "Optional name of the Ingress object for the K10 dashboard." + }, + "class": { + "type": "string", + "default": "", + "title": "Ingress controller class", + "description": "Cluster ingress controller class: nginx, GCE" + }, + "host": { + "type": "string", + "default": "", + "title": "Ingress host name", + "description": "FQDN for name-based virtual host", + "examples": [ + "/k10.example.com" + ] + }, + "urlPath": { + "type": "string", + "default": "", + "title": "Ingress URL path", + "description": "URL path for K10 Dashboard", + "examples": [ + "/k10" + ] + }, + "pathType": { + "type": "string", + "default": "ImplementationSpecific", + "title": "Ingress path type", + "description": "Set the path type for the ingress resource", + "enum": [ + "Exact", + "Prefix", + "ImplementationSpecific" + ] + }, + "defaultBackend": { + "type": "object", + "title": "Ingress default backend", + "description": "Optional default backend for the Ingress object.", + "properties": { + "service": { + "type": "object", + "title": "Ingress default backend service", + "description": "A service referenced by the default backend (mutually exclusive with `resource`).", + "properties": { + "enabled": { + "type": "boolean", + "default": false, + "title": "Enable service default backend.", + "description": "Enable the default backend backed by a service." + }, + "name": { + "type": "string", + "default": "", + "title": "Service name", + "description": "Name of a service referenced by the default backend." + }, + "port": { + "type": "object", + "title": "Service port", + "description": "A port of a service referenced by the default backend.", + "properties": { + "name": { + "type": "string", + "default": "", + "title": "Port name", + "description": "Port name of a service referenced by the default backend (mutually exclusive with `number`)." + }, + "number": { + "type": "integer", + "default": 0, + "title": "Port number", + "description": "Port number of a service referenced by the default backend (mutually exclusive with `name`)." + } + } + } + } + }, + "resource": { + "type": "object", + "title": "Ingress default backend resource", + "description": "A resource referenced by the default backend (mutually exclusive with `service`).", + "properties": { + "enabled": { + "type": "boolean", + "default": false, + "title": "Enable resource default backend.", + "description": "Enable the default backend backed by a resource." + }, + "apiGroup": { + "type": "string", + "default": "", + "title": "Resource API group", + "description": "Optional API group of a resource referenced by the default backend.", + "examples": [ + "k8s.example.com" + ] + }, + "kind": { + "type": "string", + "default": "", + "title": "Resource kind", + "description": "Type of a resource referenced by the default backend.", + "examples": [ + "StorageBucket" + ] + }, + "name": { + "type": "string", + "default": "", + "title": "Resource name", + "description": "Name of a resource referenced by the default backend." + } + } + } + } + } + } + }, + "eula": { + "type": "object", + "title": "EULA configuration", + "properties": { + "accept": { + "type": "boolean", + "default": false, + "title": "Enable accept EULA before installation", + "description": "An End-User license agreement (EULA) is a legal agreement that grants a user a license to use an application or software. Users must consent to the EULA before purchasing, installing, or downloading an application or software owned by the service provider." + } + } + }, + "license": { + "type": "string", + "default": "", + "title": "License from Kasten", + "description": "Add license string obtained from Kasten" + }, + "cluster": { + "type": "object", + "title": "Cluster configuration", + "description": "Set cluster configuration", + "properties": { + "domainName": { + "type": "string", + "default": "", + "title": "Domain name of the cluster", + "description": "Set domain name of the cluster" + } + } + }, + "multicluster": { + "type": "object", + "title": "Multi-cluster configuration", + "description": "Configure the multi-cluster system", + "properties": { + "enabled": { + "type": "boolean", + "default": true, + "title": "Enable the multi-cluster system", + "description": "Choose whether to enable the multi-cluster system components and capabilities" + }, + "primary": { + "type": "object", + "title": "Multi-cluster primary configuration", + "description": "Configure multi-cluster primary", + "properties": { + "create": { + "type": "boolean", + "default": false, + "title": "Setup cluster as a multi-cluster primary", + "description": "Choose whether to setup cluster as a multi-cluster primary" + }, + "name": { + "type": "string", + "default": "", + "title": "Primary cluster name", + "description": "Choose the cluster name for multi-cluster primary" + }, + "ingressURL": { + "type": "string", + "default": "", + "title": "Primary cluster dashboard URL", + "description": "Choose the dashboard URL for the multi-cluster primary; e.g. https://cluster-name.domain/k10" + } + } + } + } + }, + "prometheus": { + "type": "object", + "title": "Internal Prometheus configuration", + "description": "Configure internal Prometheus", + "properties": { + "rbac": { + "type": "object", + "title": "Prometheus rbac", + "description": "Configure Prometheus rbac resources", + "properties": { + "create": { + "type": "boolean", + "default": false, + "title": "Enable Prometheus rbac. Warning - cluster wide permissions", + "description": "Choose whether to create Prometheus RBAC configuration. Warning: Enabling this action will allow Prometheus permission to scrape Pods in all K8s namespaces." + } + } + }, + "server": { + "type": "object", + "title": "Prometheus Server", + "description": "Configure Prometheus Server", + "properties": { + "enabled": { + "type": "boolean", + "default": true, + "title": "Enable Prometheus server", + "description": "Create Prometheus server" + }, + "securityContext": { + "type": "object", + "title": "Prometheus server securityContext", + "description": "Configure Prometheus server securityContext", + "properties": { + "runAsUser": { + "type": "integer", + "default": 65534, + "title": "runAsUser ID", + "description": "Set securityContext runAsUser ID" + }, + "runAsNonRoot": { + "type": "boolean", + "default": true, + "title": "Enable runAsNonRoot", + "description": "Enable securityContext runAsNonRoot" + }, + "runAsGroup": { + "type": "integer", + "default": 65534, + "title": "runAsGroup ID", + "description": "Set securityContext runAsGroup ID" + }, + "fsGroup": { + "type": "integer", + "default": 65534, + "title": "fsGroup ID", + "description": "Set securityContext fsGroup ID" + } + } + }, + "retention": { + "type": "string", + "default": "30d", + "title": "Prometheus retention", + "description": "Set retention period for Prometheus" + }, + "persistentVolume": { + "type": "object", + "title": "Prometheus persistent volume", + "description": "Configure Prometheus persistent volume", + "properties": { + "storageClass": { + "type": "string", + "default": "", + "title": "StorageClassName used to create Prometheus PVC", + "description": "Setting this option overwrites global StorageClass value" + }, + "size": { + "type": "string", + "title": "Volume Size", + "description": "Size of embedded Prometheus server volume for storing Veeam Kasten metrics", + "default": "8Gi" + } + } + }, + "fullnameOverride": { + "type": "string", + "default": "prometheus-server", + "title": "Prometheus server deployment name", + "description": "Override default Prometheus server deployment name" + }, + "baseURL": { + "type": "string", + "default": "/k10/prometheus/", + "title": "Prometheus external url path", + "description": "Prometheus external url path at which the server can be accessed" + }, + "prefixURL": { + "type": "string", + "default": "/k10/prometheus", + "title": "Prometheus prefix slug", + "description": "Prometheus prefix slug at which the server can be accessed" + } + } + } + } + }, + "jaeger": { + "type": "object", + "title": "Jaeger configuration", + "description": "Jaeger tracing settings", + "properties": { + "enabled": { + "type": "boolean", + "default": false, + "title": "Enable Jaeger tracing", + "description": "Set true to enable Jaeger tracing" + }, + "agentDNS": { + "type": "string", + "default": "", + "title": "Jaeger agentDNS", + "description": "Set agentDNS for Jaeger tracing" + } + } + }, + "service": { + "type": "object", + "title": "K10 K8s services config", + "properties": { + "externalPort": { + "type": "integer", + "default": 8000, + "title": "externalPort for K10 services", + "description": "Override default 8000 externalPort for K10 services" + }, + "internalPort": { + "type": "integer", + "default": 8000, + "title": "internalPort for K10 services", + "description": "Override default 8000 internalPort for K10 services" + }, + "aggregatedApiPort": { + "type": "integer", + "default": 10250, + "title": "aggregatedApiPort for aggapi service", + "description": "Override default 10250 port for aggapi service" + } + } + }, + "secrets": { + "type": "object", + "title": "K10 secrets", + "description": "K10 secrets configuration", + "properties": { + "awsAccessKeyId": { + "type": "string", + "default": "", + "title": "AWS access key ID", + "description": "Set AWS access key ID required for AWS deployment" + }, + "awsSecretAccessKey": { + "type": "string", + "default": "", + "title": "AWS secret access key", + "description": "Set AWS access key secret" + }, + "awsIamRole": { + "type": "string", + "default": "", + "title": "AWS IAM Role", + "description": "ARN of the AWS IAM role assumed by K10 to perform any AWS operation" + }, + "awsClientSecretName": { + "type": "string", + "default": "", + "title": "Secret with AWS credentials and/or IAM Role", + "description": "Specify a Secret directly instead of having to provide awsAccessKeyId, awsSecretAccessKey and awsIamRole" + }, + "googleApiKey": { + "type": "string", + "default": "", + "title": "Google API Key", + "description": "Non-default base64 encoded GCP Service Account key" + }, + "googleProjectId": { + "type": "string", + "default": "", + "title": "Google Project ID", + "description": "Set Google Project ID other than the one in the GCP Service Account" + }, + "googleClientSecretName": { + "type": "string", + "default": "", + "title": "Secret with Google credentials", + "description": "Specify a Secret directly instead of having to provide googleApiKey and googleProjectId" + }, + "tlsSecret": { + "type": "string", + "default": "", + "title": "K8s TLS secret name contains for k10 Gateway service", + "description": "Specify a Secret directly instead of having to provide both the cert and key. This reduces the security risk a bit by not caching the certs and keys in the bash history." + }, + "dockerConfig": { + "type": "string", + "default": "", + "title": "Docker config", + "description": "base64 representation of your Docker credentials to pull docker images from a private registry" + }, + "dockerConfigPath": { + "type": "string", + "default": "", + "title": "Docker config path", + "description": "Path to Docker config file to create secret from" + }, + "azureTenantId": { + "type": "string", + "default": "", + "title": "Azure tenant ID", + "description": "Azure tenant ID required for Azure deployment" + }, + "azureClientId": { + "type": "string", + "default": "", + "title": "Azure client ID", + "description": "Azure Service App ID" + }, + "azureClientSecret": { + "type": "string", + "default": "", + "title": "Azure client Secret", + "description": "Azure Service APP secret" + }, + "azureClientSecretName": { + "type": "string", + "default": "", + "title": "Secret with Azure credentials", + "description": "Specify a Secret directly instead of having to provide azureClientId, azureTenantId and azureClientSecret" + }, + "azureResourceGroup": { + "type": "string", + "default": "", + "title": "Azure resource group", + "description": "Resource Group name that was created for the Kubernetes cluster" + }, + "azureSubscriptionID": { + "type": "string", + "default": "", + "title": "Azure subscription ID", + "description": "Subscription ID in your Azure tenant" + }, + "azureResourceMgrEndpoint": { + "type": "string", + "default": "", + "title": "Azure resource manager endpoint", + "description": "Resource management endpoint for the Azure Stack instance" + }, + "azureADEndpoint": { + "type": "string", + "default": "", + "title": "Azure AD endpoint", + "description": "Azure Active Directory login endpoint" + }, + "azureADResourceID": { + "type": "string", + "default": "", + "title": "Azure Active Directory resource ID", + "description": "Azure Active Directory resource ID to obtain AD tokens" + }, + "microsoftEntraIDEndpoint": { + "type": "string", + "default": "", + "title": "Microsoft Entra ID endpoint", + "description": "Microsoft Entra ID login endpoint" + }, + "microsoftEntraIDResourceID": { + "type": "string", + "default": "", + "title": "Microsoft Entra ID resource ID", + "description": "Microsoft Entra ID resource ID to obtain AD tokens" + }, + "azureCloudEnvID": { + "type": "string", + "default": "", + "title": "Azure Cloud Environment ID", + "description": "Azure Cloud Environment ID" + }, + "vsphereEndpoint": { + "type": "string", + "default": "", + "title": "vSphere endpoint", + "description": "vSphere endpoint for login" + }, + "vsphereUsername": { + "type": "string", + "default": "", + "title": "", + "description": "" + }, + "vspherePassword": { + "type": "string", + "default": "", + "title": "vSphere password", + "description": "vSphere password for login" + }, + "vsphereClientSecretName": { + "type": "string", + "default": "", + "title": "Secret with vSphere credentials", + "description": "Specify a Secret directly instead of having to provide vsphereUsername, vspherePassword and vspherePassword" + } + } + }, + "metering": { + "type": "object", + "title": "Metering service config", + "description": "Metering service settings", + "properties": { + "reportingKey": { + "type": "string", + "default": "", + "title": "Reporting key", + "description": "Base64 encoded reporting key" + }, + "consumerId": { + "type": "string", + "default": "", + "title": "Consumer ID", + "description": "Consumer ID in the format project:" + }, + "awsRegion": { + "type": "string", + "default": "", + "title": "AWS Region", + "description": "Set AWS_REGION for metering service" + }, + "awsMarketPlaceIamRole": { + "type": "string", + "default": "", + "title": "AWS Marketplace IAM Role", + "description": "Set AWS marketplace IAM Role" + }, + "awsMarketplace": { + "type": "boolean", + "default": false, + "title": "AWS Marketplace", + "description": "Set AWS cloud metering license mode" + }, + "awsManagedLicense": { + "type": "boolean", + "default": false, + "title": "AWS managed license", + "description": "Set AWS managed license mode" + }, + "licenseConfigSecretName": { + "type": "string", + "default": "", + "title": "License config secret name", + "description": "AWS managed license config secret" + }, + "serviceAccount": { + "type": "object", + "title": "Metering service serviceAccount", + "description": "Configuration for metering service serviceAccount", + "properties": { + "create": { + "type": "boolean", + "default": false, + "title": "Create metering service serviceAccount", + "description": "Create metering service serviceAccount" + }, + "name": { + "type": "string", + "default": "", + "title": "Metering ServiceAccount name", + "description": "Set name for metering ServiceAccount" + } + } + }, + "mode": { + "type": "string", + "default": "", + "title": "Control license reporting", + "description": "Set to `airgap` for private-network installs" + }, + "redhatMarketplacePayg": { + "type": "boolean", + "default": false, + "title": "Red Hat cloud metering", + "description": "Set Red Hat cloud metering license mode" + }, + "reportCollectionPeriod": { + "type": "integer", + "default": 1800, + "title": "Report collection period", + "description": "Metric report collection period (in seconds)" + }, + "reportPushPeriod": { + "type": "integer", + "default": 3600, + "title": "Report push period", + "description": "Metric report push period (in seconds)" + }, + "promoID": { + "type": "string", + "default": "", + "title": "K10 promotion ID", + "description": "K10 promotion ID from marketing campaigns" + } + } + }, + "clusterName": { + "type": "string", + "default": "", + "title": "Cluster name", + "description": "Cluster name for better logs visibility" + }, + "logLevel": { + "type": "string", + "default": "info", + "title": "Log level", + "description": "Change default log level" + }, + "externalGateway": { + "type": "object", + "title": "External gateway", + "description": "Configure external gateway for K10 API services", + "properties": { + "create": { + "type": "boolean", + "default": false, + "title": "Enable external gateway", + "description": "Create external gateway service" + }, + "annotations": { + "type": "object", + "title": "The annotations Schema", + "default": {}, + "description": "Standard annotations for the services" + }, + "fqdn": { + "type": "object", + "title": "Host and domain name for the K10 API services", + "description": "Configure host and domain name for the K10 API services", + "properties": { + "name": { + "type": "string", + "default": "", + "title": "Domain name for the K10 API services", + "description": "Domain name for the K10 API services" + }, + "type": { + "type": "string", + "default": "", + "title": "Gateway type", + "description": "Supported gateway type: route53-mapper or external-dns", + "enum": ["", "route53-mapper", "external-dns"] + } + } + }, + "awsSSLCertARN": { + "type": "string", + "default": "", + "title": "AWS SSL Cert ARN", + "description": "ARN for the AWS ACM SSL certificate used in the K10 API server" + } + } + }, + "auth": { + "type": "object", + "title": "Authentication settings", + "description": "Configure K10 dashboard authentication", + "properties": { + "groupAllowList": { + "type": "array", + "default": [], + "items": { + "type": "string" + }, + "title": "List of groups allowed to access K10 dashboard", + "description": "A list of groups whose members are allowed access to K10's dashboard", + "examples": [ + [ + "group1", + "group2" + ] + ] + }, + "basicAuth": { + "type": "object", + "title": "Basic authentication for the K10 dashboard", + "description": "Configure basic authentication for the K10 dashboard", + "properties": { + "enabled": { + "title": "Enable basic authentication", + "description": "Enables basic authentication to the K10 dashboard that allows users to login with username and password", + "type": "boolean", + "default": false + }, + "secretName": { + "type": "string", + "default": "", + "title": "Secret with basic auth creds", + "description": "Name of an existing Secret that contains a file generated with htpasswd" + }, + "htpasswd": { + "type": "string", + "default": "", + "title": "Basic authentication creds", + "description": "A username and password pair separated by a colon character" + } + } + }, + "tokenAuth": { + "type": "object", + "title": "Token based authentication", + "description": "Configuration for Token based authentication for the K10 dashboard", + "properties": { + "enabled": { + "type": "boolean", + "default": false, + "title": "Enable token based authentication", + "description": "Enable token based authentication to access K10 dashboard" + } + } + }, + "oidcAuth": { + "type": "object", + "default": {}, + "title": "Open ID Connect based authentication", + "description": "Configuration for Open ID Connect based authentication for the K10 dashboard", + "properties": { + "enabled": { + "type": "boolean", + "default": false, + "title": "Enable Open ID Connect based authentication", + "description": "Enable Open ID Connect based authentication to access K10 dashboard" + }, + "providerURL": { + "type": "string", + "default": "", + "title": "OIDC Provider URL", + "description": "URL for the OIDC Provider" + }, + "redirectURL": { + "type": "string", + "default": "", + "title": "K10 gateway service URL", + "description": "URL to the K10 gateway service" + }, + "scopes": { + "type": "string", + "default": "", + "title": "OIDC scopes", + "description": "Space separated OIDC scopes required for userinfo", + "examples": [ + "profile email" + ] + }, + "prompt": { + "type": "string", + "title": "OIDC prompt type", + "description": "The type of prompt to be used during authentication", + "default": "select_account", + "enum": [ + "none", + "consent", + "login", + "select_account" + ] + }, + "clientID": { + "type": "string", + "default": "", + "title": "OIDC client ID", + "description": "Client ID given by the OIDC provider" + }, + "clientSecret": { + "type": "string", + "default": "", + "title": "OIDC client secret", + "description": "Client secret given by the OIDC provider" + }, + "clientSecretName": { + "type": "string", + "default": "", + "title": "Reference to secret", + "description": "Secret containing OIDC client ID and OIDC client secret" + }, + "usernameClaim": { + "type": "string", + "default": "", + "title": "OIDC username claim", + "description": "The claim to be used as the username" + }, + "usernamePrefix": { + "type": "string", + "default": "", + "title": "OIDC username prefix", + "description": "Prefix that has to be used with the username obtained from the username claim" + }, + "groupClaim": { + "type": "string", + "default": "", + "title": "OIDC group claim", + "description": "Name of a custom OpenID Connect claim for specifying user groups" + }, + "groupPrefix": { + "type": "string", + "default": "", + "title": "OIDC group prefix", + "description": "All groups will be prefixed with this value to prevent conflicts" + }, + "logoutURL": { + "type": "string", + "default": "", + "title": "OIDC logout endpoint", + "description": "URL to your OIDC provider's logout endpoint" + }, + "secretName": { + "type": "string", + "default": "", + "title": "OIDC config based existing secret", + "description": "Must include providerURL, redirectURL, scopes, clientID/secret and logoutURL" + }, + "sessionDuration": { + "type": "string", + "default": "1h", + "title": "OIDC session duration", + "description": "Maximum OIDC session duration. Default value is 1 hour" + }, + "refreshTokenSupport": { + "type": "boolean", + "default": false, + "title": "OIDC Refresh Token support", + "description": "Enable OIDC Refresh Token support. Disabled by default." + } + } + }, + "openshift": { + "type": "object", + "title": "OpenShift OAuth server based authentication", + "description": "OpenShift OAuth server based authentication for K10 dashboard", + "properties": { + "enabled": { + "type": "boolean", + "default": false, + "title": "Enable OpenShift OAuth server based authentication", + "description": "Enable OpenShift OAuth server based authentication to access K10 dashboard" + }, + "serviceAccount": { + "type": "string", + "default": "", + "title": "Service account that represents an OAuth client", + "description": "Name of the service account that represents an OAuth client" + }, + "clientSecret": { + "type": "string", + "default": "", + "title": "Service account token", + "description": "The token corresponding to the service account" + }, + "clientSecretName": { + "type": "string", + "default": "", + "title": "Service account token secret", + "description": "The secret that contains the token corresponding to the service account" + }, + "dashboardURL": { + "type": "string", + "default": "", + "title": "K10 dashboard URL", + "description": "The URL used for accessing K10's dashboard" + }, + "openshiftURL": { + "type": "string", + "default": "", + "title": "OpenShift URL", + "description": "The URL for accessing OpenShift's API server" + }, + "insecureCA": { + "type": "boolean", + "default": false, + "title": "Disable SSL verification of connections to OpenShift", + "description": "Set true to turn off SSL verification of connections to OpenShift" + }, + "useServiceAccountCA": { + "type": "boolean", + "default": false, + "title": "use the CA certificate corresponding to the Service Account", + "description": "Usually found at ``/var/run/secrets/kubernetes.io/serviceaccount/ca.crt``" + }, + "secretName": { + "type": "string", + "default": "", + "title": "The Kubernetes Secret that contains OIDC settings", + "description": "Specify Kubernetes Secret that contains OIDC settings" + }, + "usernameClaim": { + "type": "string", + "default": "email", + "title": "Username claim", + "description": "The claim to be used as the username" + }, + "usernamePrefix": { + "type": "string", + "default": "", + "title": "Username prefix", + "description": "Prefix that has to be used with the username obtained from the username claim" + }, + "groupnameClaim": { + "type": "string", + "default": "groups", + "title": "custom OpenID Connect claim name for specifying user groups", + "description": "Name of a custom OpenID Connect claim for specifying user groups" + }, + "groupnamePrefix": { + "type": "string", + "default": "", + "title": "User group name prefix", + "description": "Prefix for user group name" + }, + "caCertsAutoExtraction": { + "type": "boolean", + "default": true, + "title": "Enable the OCP CA certificates automatic extraction", + "description": "Enable the OCP CA certificates automatic extraction to the K10 namespace" + } + } + }, + "ldap": { + "type": "object", + "title": "Active Directory/LDAP based authentication ", + "description": "Active Directory/LDAP based authentication for the K10 dashboard", + "properties": { + "enabled": { + "type": "boolean", + "default": false, + "title": "Enable Active Directory/LDAP based authentication", + "description": "Enable Active Directory/LDAP based authentication to access K10 dashboard" + }, + "restartPod": { + "type": "boolean", + "default": false, + "title": "force a restart of the authentication service Pod", + "description": "force a restart of the authentication service Pod (useful when updating authentication config)" + }, + "dashboardURL": { + "type": "string", + "default": "", + "title": "K10 dashboard URL", + "description": "The URL used for accessing K10's dashboard" + }, + "host": { + "type": "string", + "default": "", + "title": "Host and port of the AD/LDAP server", + "description": "Host and optional port of the AD/LDAP server in the form `host:port`" + }, + "insecureNoSSL": { + "type": "boolean", + "default": false, + "title": "Insecure AD/LDAP host", + "description": "Set if the AD/LDAP host is not using TLS" + }, + "insecureSkipVerifySSL": { + "type": "boolean", + "default": false, + "title": "Skip SSL verification of connections to the AD/LDAP host", + "description": "Turn off SSL verification of connections to the AD/LDAP host" + }, + "startTLS": { + "type": "boolean", + "default": false, + "title": "TLS protocol", + "description": "When set to true, ldap:// is used to connect to the server followed by creation of a TLS session. When set to false, ldaps:// is used." + }, + "bindDN": { + "type": "string", + "default": "", + "title": "Username for connecting to the AD/LDAP host", + "description": "The Distinguished Name(username) used for connecting to the AD/LDAP host" + }, + "bindPW": { + "type": "string", + "default": "", + "title": "The password for `bindDN`", + "description": "The password corresponding to the `bindDN` for connecting to the AD/LDAP host" + }, + "bindPWSecretName": { + "type": "string", + "default": "", + "title": "Secret name containing the password", + "description": "Secret name containing the password corresponding to the `bindDN` for connecting to the AD/LDAP host" + }, + "userSearch": { + "type": "object", + "title": "User search config", + "description": "AD/LDAP user search config", + "properties": { + "baseDN": { + "type": "string", + "default": "", + "title": "The base username to start the AD/LDAP search from", + "description": "The base Distinguished Name to start the AD/LDAP search from" + }, + "filter": { + "type": "string", + "default": "", + "title": "filter to apply when searching", + "description": "Optional filter to apply when searching the directory" + }, + "username": { + "type": "string", + "default": "", + "title": "Username to search in the directory", + "description": "Attribute used for comparing user entries when searching the directory" + }, + "idAttr": { + "type": "string", + "default": "", + "title": "Attribute in a user's entry that should map to the user ID field in a token", + "description": "AD/LDAP attribute in a user's entry that should map to the user ID field in a token" + }, + "emailAttr": { + "type": "string", + "default": "", + "title": "Attribute in a user's entry that should map to the email field in a token", + "description": "AD/LDAP attribute in a user's entry that should map to the email field in a token" + }, + "nameAttr": { + "type": "string", + "default": "", + "title": "Attribute in a user's entry that should map to the name field in a token", + "description": "Attribute in a user's entry that should map to the name field in a token" + }, + "preferredUsernameAttr": { + "type": "string", + "default": "", + "title": "Attribute in a user's entry that should map to the preferred_username field in a token", + "description": "AD/LDAP attribute in a user's entry that should map to the preferred_username field in a token" + } + } + }, + "groupSearch": { + "type": "object", + "title": "AD/LDAP group search config", + "description": "AD/LDAP group search config", + "properties": { + "baseDN": { + "type": "string", + "default": "", + "title": "The base Distinguished Name", + "description": "The base Distinguished Name to start the AD/LDAP group search from" + }, + "filter": { + "type": "string", + "default": "", + "title": "Search filter", + "description": "filter to apply when searching the directory for groups" + }, + "userMatchers": { + "type": "array", + "items": { + "type": "object", + "properties": { + "userAttr": { + "type": "string", + "default": "", + "title": "Attribute in the user's entry", + "description": "Attribute in the user's entry that must match the groupAttr when searching for groups" + }, + "groupAttr": { + "type": "string", + "default": "", + "title": "Attribute in the group's entry", + "description": "Attribute in the group's entry that must match the userAttr when searching for groups" + } + } + }, + "default": [], + "title": "List of field pairs that are used to match a user to a group", + "description": "List of field pairs that are used to match a user to a group" + }, + "nameAttr": { + "type": "string", + "default": "", + "title": "Attribute that represents a group's name in the directory", + "description": "The AD/LDAP attribute that represents a group's name in the directory" + } + } + }, + "secretName": { + "type": "string", + "default": "", + "title": "The Kubernetes Secret with OIDC settings", + "description": "The Kubernetes Secret that contains OIDC settings" + }, + "usernameClaim": { + "type": "string", + "default": "email", + "title": "Username claim", + "description": "The claim to be used as the username" + }, + "usernamePrefix": { + "type": "string", + "default": "", + "title": "Username prefix", + "description": "Prefix that has to be used with the username obtained from the username claim" + }, + "groupnameClaim": { + "type": "string", + "default": "groups", + "title": "Name of a custom OpenID Connect claim for specifying user groups", + "description": "Name of a custom OpenID Connect claim for specifying user groups" + }, + "groupnamePrefix": { + "type": "string", + "default": "", + "title": "Group name prefix", + "description": "Prefix for user group name" + } + } + }, + "k10AdminUsers": { + "type": "array", + "items": { + "type": "string" + }, + "default": [], + "title": "Admin users list", + "description": "A list of users who are granted admin level access to K10's dashboard" + }, + "k10AdminGroups": { + "type": "array", + "items": { + "type": "string" + }, + "default": [], + "title": "Admin groups list", + "description": "A list of groups whose members are granted admin level access to K10's dashboard" + } + } + }, + "optionalColocatedServices": { + "type": "object", + "title": "Optional Colocated services config", + "description": "Settings to enable optional colocated services", + "properties": { + "vbrintegrationapi": { + "title": "VBRIntegratipnAPI service", + "description": "Settings for VBRIntegratipnAPI service", + "type": "object", + "properties": { + "enabled": { + "title": "Enable VBRIntegratipnAPI service", + "description": "Set true to enable VBRIntegratipnAPI service", + "type": "boolean", + "default": true + } + } + } + } + }, + "cacertconfigmap": { + "type": "object", + "title": "CA Certificate ConfigMap", + "description": "ConfigMap containing a certificate for a trusted root certificate authority", + "properties": { + "name": { + "title": "Name of the configmap", + "description": "Name of the K8s ConfigMap containing a certificate for a trusted root certificate authority", + "type": "string", + "default": "" + } + } + }, + "apiservices": { + "type": "object", + "title": "Skip APIService objects creation", + "describe": "Skip APIService objects creation if already exists", + "properties": { + "deployed": { + "type": "boolean", + "default": true, + "title": "Whether APIService object are deployed", + "description": "Set true if APIService objects exists. Setting false will recreate the objects" + } + } + }, + "injectKanisterSidecar": { + "type": "object", + "deprecated": true, + "title": "Deprecated: Kanister sidecar injection for workload Pods", + "description": "Deprecated. Use 'injectGenericVolumeBackupSidecar' parameter instead", + "properties": { + "enabled": { + "type": "boolean", + "default": false, + "title": "Enable Kanister sidecar injection for workload Pods", + "description": "Set true to enable Kanister sidecar injection for workload Pods" + }, + "namespaceSelector": { + "type": "object", + "title": "namespaceSelector config", + "description": "Configure namespaceSelector for namespace containing the workloads to inject Kansiter Sidecar", + "properties": { + "matchLabels": { + "type": "object", + "default": {}, + "title": "namespaceSelector matchLabels", + "description": "Set of labels to select namespaces in which sidecar injection is enabled for workloads" + } + } + }, + "objectSelector": { + "type": "object", + "title": "objectSelector config", + "description": "Configure objectSelector for the workloads to inject Kansiter Sidecar", + "properties": { + "matchLabels": { + "type": "object", + "default": {}, + "title": "objectSelector matchLabels", + "description": "Set of labels to filter workload objects in which the sidecar is injected" + } + } + }, + "webhookServer": { + "type": "object", + "title": "Sidecar injector webhook server", + "description": "Configure sidecar injector webhook server", + "properties": { + "port": { + "type": "integer", + "default": 8080, + "title": "Mutating webhook server port number", + "description": "Port number on which the mutating webhook server accepts request" + } + } + } + } + }, + "injectGenericVolumeBackupSidecar": { + "type": "object", + "title": "Generic Volume Backup sidecar injection for workload Pods", + "description": "Configure Generic Volume Backup sidecar injection for workload Pods", + "properties": { + "enabled": { + "type": "boolean", + "default": false, + "title": "Enables injection of sidecar container required to perform Generic Volume Backup into workload Pods", + "description": "Enables injection of sidecar container required to perform Generic Volume Backup into workload Pods" + }, + "namespaceSelector": { + "type": "object", + "title": "namespaceSelector config", + "description": "Configure namespaceSelector for namespace containing the workloads to inject Generic Volume Backup Sidecar", + "properties": { + "matchLabels": { + "type": "object", + "default": {}, + "title": "namespaceSelector matchLabels", + "description": "Set of labels to select namespaces in which sidecar injection is enabled for workloads" + } + } + }, + "objectSelector": { + "type": "object", + "title": "objectSelector config", + "description": "Configure objectSelector for the workloads to inject Generic Volume Backup Sidecar", + "properties": { + "matchLabels": { + "type": "object", + "default": {}, + "title": "objectSelector matchLabels", + "description": "Set of labels to filter workload objects in which the sidecar is injected" + } + } + }, + "webhookServer": { + "type": "object", + "title": "Sidecar injector webhook server", + "description": "Configure sidecar injector webhook server", + "properties": { + "port": { + "type": "integer", + "default": 8080, + "title": "Mutating webhook server port number", + "description": "Port number on which the mutating webhook server accepts request" + } + } + } + } + }, + "kanisterPodCustomLabels": { + "type": "string", + "default": "", + "title": "Kanister Pod custom labels", + "description": "Custom labels for Pods managed by Kanister" + }, + "kanisterPodCustomAnnotations": { + "type": "string", + "default": "", + "title": "Kanister Pod custom annotations", + "description": "Custom annotations added to Pods managed by Kanister" + }, + "features": { + "type": "object", + "title": "Feature flags", + "description": "Feature flags to be set by K10", + "properties": { + "backgroundMaintenanceRun": { + "type": "boolean", + "default": true, + "title": "Background maintenance feature", + "description": "Enable background maintenance runs by the repositories service" + } + } + }, + "kanisterPodMetricSidecar": { + "type": "object", + "deprecated": true, + "title": "Deprecated: Metric sidecar for ephemeral Pods", + "description": "Deprecated. Use 'workerPodMetricSidecar' parameter instead", + "properties": { + "enabled": { + "type": "boolean", + "default": false, + "title": "Enable sidecar container", + "description": "Enable sidecar container for gathering metrics from ephemeral Pods" + }, + "metricLifetime": { + "type": "string", + "default": "2m", + "title": "The period we check if there are metrics which should be removed", + "description": "The period we check if there are metrics which should be removed" + }, + "pushGatewayInterval": { + "type": "string", + "default": "30s", + "title": "Pushgateway metrics interval", + "description": "The interval of sending metrics into the Pushgateway" + }, + "resources": { + "type": "object", + "title": "Kanister Pod metric sidecar resource config", + "description": "Configure resource requests and limits for kanister Pod metric sidecar", + "properties": { + "requests": { + "type": "object", + "title": "Kanister Pod metric sidecar resource requests", + "description": "Kanister Pod metric sidecar resource requests configuration", + "properties": { + "memory": { + "type": "string", + "default": "", + "title": "Kanister Pod metric sidecar memory request", + "description": "Kanister Pod metric sidecar memory request", + "examples": [ + "1Gi" + ] + }, + "cpu": { + "type": "string", + "default": "", + "title": "Kanister Pod metric sidecars cpu request", + "description": "Kanister Pod metric sidecars cpu request", + "examples": [ + "1" + ] + } + } + }, + "limits": { + "type": "object", + "title": "Kanister Pod metric sidecar resource limits", + "description": "Kanister Pod metric sidecar resource limits configuration", + "properties": { + "memory": { + "type": "string", + "default": "", + "title": "Kanister Pod metric sidecars memory limit", + "description": "Kanister Pod metric sidecars memory limit", + "examples": [ + "1Gi" + ] + }, + "cpu": { + "type": "string", + "default": "", + "title": "Kanister Pod metric sidecars cpu limit", + "description": "Kanister Pod metric sidecars cpu limit", + "examples": [ + "1" + ] + } + } + } + } + } + } + }, + "workerPodMetricSidecar": { + "type": "object", + "title": "Metric sidecar for ephemeral Pods", + "description": "Sidecar container for gathering metrics from ephemeral Pods", + "properties": { + "enabled": { + "type": "boolean", + "default": true, + "title": "Enables a sidecar container for temporary worker Pods used to push Pod performance metrics to Prometheus", + "description": "Enables a sidecar container for temporary worker Pods used to push Pod performance metrics to Prometheus" + }, + "metricLifetime": { + "type": "string", + "default": "2m", + "title": "Specifies the period after which metrics for an individual worker Pod are removed from Prometheus ", + "description": "Specifies the period after which metrics for an individual worker Pod are removed from Prometheus " + }, + "pushGatewayInterval": { + "type": "string", + "default": "30s", + "title": "Specifies the frequency for pushing metrics into Prometheus", + "description": "Specifies the frequency for pushing metrics into Prometheus" + }, + "resources": { + "type": "object", + "title": "Specifies resource requests and limits for the temporary worker Pod metric sidecar", + "description": "Specifies resource requests and limits for the temporary worker Pod metric sidecar", + "properties": { + "requests": { + "type": "object", + "title": "Specifies resource requests for the temporary worker Pod metric sidecar", + "description": "Specifies resource requests for the temporary worker Pod metric sidecar", + "properties": { + "memory": { + "type": "string", + "default": "", + "title": "Temporary worker Pod metric sidecar memory request", + "description": "Temporary worker Pod metric sidecar memory request", + "examples": [ + "1Gi" + ] + }, + "cpu": { + "type": "string", + "default": "", + "title": "Temporary worker Pod metric sidecars cpu request", + "description": "Temporary worker Pod metric sidecars cpu request", + "examples": [ + "1" + ] + } + } + }, + "limits": { + "type": "object", + "title": "Specifies resource limits for the temporary worker Pod metric sidecar", + "description": "Specifies resource limits for the temporary worker Pod metric sidecar", + "properties": { + "memory": { + "type": "string", + "default": "", + "title": "Temporary worker Pod metric sidecars memory limit", + "description": "Temporary worker Pod metric sidecars memory limit", + "examples": [ + "1Gi" + ] + }, + "cpu": { + "type": "string", + "default": "", + "title": "Temporary worker Pod metric sidecars cpu limit", + "description": "Temporary worker Pod metric sidecars cpu limit", + "examples": [ + "1" + ] + } + } + } + } + } + } + }, + "genericStorageBackup": { + "type": "object", + "title": "Generic Storage backup activation config", + "properties": { + "token": { + "type": "string", + "title": "Generic volume snapshot activation token", + "description": "Token to enable generic volume snapshot", + "default": "" + } + } + }, + "genericVolumeSnapshot": { + "type": "object", + "title": "Generic Volume Snapshot restore Pods config", + "description": "Specifies resource requests and limits for generic backup sidecar and all temporary Kasten worker Pods. Superseded by ActionPodSpec", + "properties": { + "resources": { + "type": "object", + "title": "Generic Volume Snapshot restore Pod resource config", + "description": "Specifies resource requests for generic backup sidecar and all temporary Kasten worker Pods. Superseded by ActionPodSpec", + "properties": { + "requests": { + "type": "object", + "title": "Generic Volume Snapshot resource requests", + "description": "Generic Volume Snapshot resource requests configuration", + "properties": { + "memory": { + "type": "string", + "default": "", + "title": "Generic Volume Snapshot restore Pods memory request", + "description": "Generic Volume Snapshot restore Pods memory request", + "examples": [ + "1Gi" + ] + }, + "cpu": { + "type": "string", + "default": "", + "title": "Generic Volume Snapshot restore Pods cpu request", + "description": "Generic Volume Snapshot restore Pods cpu request", + "examples": [ + "1" + ] + } + } + }, + "limits": { + "type": "object", + "title": "Generic Volume Snapshot resource limits", + "description": "Specifies resource limits for generic backup sidecar and all temporary Kasten worker Pods. Superseded by ActionPodSpec", + "properties": { + "memory": { + "type": "string", + "default": "", + "title": "Generic Volume Snapshot restore Pods memory limit", + "description": "Generic Volume Snapshot restore Pods memory limit", + "examples": [ + "1Gi" + ] + }, + "cpu": { + "type": "string", + "default": "", + "title": "Generic Volume Snapshot restore Pods cpu limit", + "description": "Generic Volume Snapshot restore Pods cpu limit", + "examples": [ + "1" + ] + } + } + } + } + } + } + }, + "garbagecollector": { + "type": "object", + "title": "garbage collection", + "description": "Configure garbage collection settings", + "properties": { + "daemonPeriod": { + "type": "integer", + "default": 21600, + "title": "Garbage collection period", + "description": "Set garbage collection period (in seconds)" + }, + "keepMaxActions": { + "type": "integer", + "default": 1000, + "title": "Max actions to keep", + "description": "Sets maximum actions to keep" + }, + "actions": { + "type": "object", + "title": "action collectors config", + "description": "Configure action garbage collectors", + "properties": { + "enabled": { + "type": "boolean", + "default": false, + "title": "Enable action collectors", + "description": "Set true to enable action collectors" + } + } + } + } + }, + "resources": { + "type": "object", + "default": {}, + "title": "K10 Pods resource config", + "description": "Resource management for K10 Pods" + }, + "datastore": { + "type": "object", + "properties": { + "parallelUploads": { + "type": "integer", + "default": 8, + "title": "Parallelism for data store uploads", + "description": "Specifies how many files can be uploaded in parallel to the data store" + }, + "parallelDownloads": { + "type": "integer", + "default": 8, + "title": "Parallelism for data store downloads", + "description": "Specifies how many files can be downloaded in parallel from the data store" + }, + "parallelBlockUploads": { + "type": "integer", + "default": 8, + "title": "Parallelism for data store block mode uploads", + "description": "Specifies how many blocks can be uploaded in parallel to the data store" + }, + "parallelBlockDownloads": { + "type": "integer", + "default": 8, + "title": "Parallelism for block mode data store downloads", + "description": "Specifies how many blocks can be downloaded in parallel from the data store" + }, + "estimationType": { + "type": "string", + "default": "adaptive", + "title": "Estimation Method for Data Store Uploads", + "description": "Specifies the method of estimation to be used when uploading to the data store" + }, + "adaptiveEstimationThreshold": { + "type": "integer", + "default": 300000, + "title": "Adaptive Estimation Threshold for Data Store Uploads", + "description": "Specifies the threshold for the adaptive estimation method when uploading to the data store" + } + } + }, + "defaultPriorityClassName": { + "type": "string", + "default": "", + "title": "Default priorityClassName", + "description": "Set the default priorityClassName for all K10 Pods" + }, + "priorityClassName": { + "type": "object", + "default": {}, + "title": "K10 Pods priorityClassName config", + "description": "Set priorityClassName for specific K10 Pods" + }, + "services": { + "type": "object", + "title": "K10 services config", + "description": "Settings for K10 services", + "properties": { + "executor": { + "type": "object", + "title": "executor service config", + "description": "Configuration for K10 executor service", + "properties": { + "hostNetwork": { + "type": "boolean", + "default": false, + "title": "Enable node network usage", + "description": "Whether the executor Pods may use the node network" + }, + "workerCount": { + "type": "integer", + "default": -1, + "deprecated": true, + "title": "Deprecated: Executor workers count", + "description": "Deprecated. Use 'limiter.executorThreads' parameter instead" + }, + "maxConcurrentRestoreCsiSnapshots": { + "type": "integer", + "default": -1, + "deprecated": true, + "title": "Deprecated: Concurrent restore CSI snapshots operations", + "description": "Deprecated. Use 'limiter.csiSnapshotRestoresPerAction' parameter instead" + }, + "maxConcurrentRestoreGenericVolumeSnapshots": { + "type": "integer", + "default": -1, + "deprecated": true, + "title": "Deprecated: Concurrent restore generic volume snapshots operations", + "description": "Deprecated. Use 'limiter.volumeRestoresPerAction' parameter instead" + }, + "maxConcurrentRestoreWorkloads": { + "type": "integer", + "default": -1, + "deprecated": true, + "title": "Deprecated: Concurrent restore workloads operations", + "description": "Deprecated. Use 'limiter.workloadRestoresPerAction' parameter instead" + } + } + }, + "dashboardbff": { + "type": "object", + "title": "dashboardbff service config", + "properties": { + "hostNetwork": { + "type": "boolean", + "default": false, + "title": "Enable node network usage", + "description": "Whether the dashboardbff Pods may use the node network" + } + } + }, + "securityContext": { + "type": "object", + "title": "securityContext for K10 service containers", + "description": "Custom securityContext for K10 service containers", + "properties": { + "runAsUser": { + "type": "integer", + "default": 1000, + "title": "runAsUser ID", + "description": "User ID K10 service containers run as" + }, + "fsGroup": { + "type": "integer", + "default": 1000, + "title": "FSGroup ID", + "description": "FSGroup that owns K10 service container volumes" + }, + "runAsNonRoot": { + "type": "boolean", + "default": true, + "title": "RunAsNonRoot", + "description": "Indicates that K10 service containers should run as non-root user." + }, + "seccompProfile": { + "type": "object", + "title": "Seccomp Profile object", + "description": "Sets the Seccomp profile for K10 service containers", + "properties": { + "type": { + "type": "string", + "default": "RuntimeDefault", + "title": "Seccomp profile type", + "description": "Sets the Seccomp profile type for K10 service containers" + } + } + } + } + }, + "aggregatedapis": { + "type": "object", + "title": "K10 aggregatedapis service config", + "properties": { + "hostNetwork": { + "type": "boolean", + "default": false, + "title": "Enable node network usage", + "description": "Whether the aggregatedapis Pods may use the node network" + } + } + } + } + }, + "siem": { + "type": "object", + "title": "siem", + "description": "siem settings", + "properties": { + "logging": { + "type": "object", + "title": "logging", + "description": "siem logging settings", + "properties": { + "cluster": { + "type": "object", + "title": "cluster", + "description": "In-cluster agent log slurping settings", + "properties": { + "enabled": { + "type": "boolean", + "default": true, + "title": "Enable in-cluster agent-based audit logging", + "description": "Enabled in-cluster agent-based audit logging for K10 events" + } + } + }, + "cloud": { + "type": "object", + "title": "cloud", + "description": "siem cloud logging settings", + "properties": { + "path": { + "type": "string", + "default": "k10audit/", + "title": "Directory path in cloud object storage for saving logs", + "description": "Directory path in cloud object storage for saving logs when writing K10 events" + }, + "awsS3": { + "type": "object", + "title": "awsS3", + "description": "AWS S3 log slurping settings", + "properties": { + "enabled": { + "type": "boolean", + "default": true, + "title": "Enable AWS S3 audit logging", + "description": "Enable AWS S3 audit logging for K10 events" + } + } + } + } + } + } + } + } + }, + "limiter": { + "type": "object", + "title": "Limiter", + "description": "Limits set on several operations", + "properties": { + "concurrentSnapConversions": { + "type": "integer", + "default": -1, + "deprecated": true, + "title": "Deprecated: Concurrent snapshot conversions", + "description": "Deprecated. Use 'limiter.snapshotExportsPerAction' parameter instead" + }, + "snapshotExportsPerAction": { + "type": "integer", + "default": 3, + "title": "Per action limit of concurrent volume export operations", + "description": "Per action limit of concurrent volume export operations" + }, + "genericVolumeSnapshots": { + "type": "integer", + "default": -1, + "deprecated": true, + "title": "Deprecated: Concurrent generic volume snapshot creation", + "description": "Deprecated. Use 'limiter.genericVolumeBackupsPerCluster' parameter instead." + }, + "genericVolumeBackupsPerCluster": { + "type": "integer", + "default": 10, + "title": "Cluster-wide limit of concurrent Generic Volume Backup operations", + "description": "Cluster-wide limit of concurrent Generic Volume Backup operations" + }, + "genericVolumeCopies": { + "type": "integer", + "default": -1, + "deprecated": true, + "title": "Deprecated: Concurrent generic volume snapshot copy", + "description": "Deprecated. Use 'limiter.snapshotExportsPerCluster' parameter instead" + }, + "snapshotExportsPerCluster": { + "type": "integer", + "default": 10, + "title": "Cluster-wide limit of concurrent volume export operations", + "description": "Cluster-wide limit of concurrent volume export operations" + }, + "genericVolumeRestores": { + "type": "integer", + "default": -1, + "deprecated": true, + "title": "Deprecated: Concurrent generic volume snapshot restore", + "description": "Deprecated. Use 'limiter.volumeRestoresPerCluster' parameter instead" + }, + "volumeRestoresPerCluster": { + "type": "integer", + "default": 10, + "title": "Cluster-wide limit of concurrent volume restore operations", + "description": "Cluster-wide limit of concurrent volume restore operations from exported backups" + }, + "csiSnapshots": { + "type": "integer", + "default": -1, + "deprecated": true, + "title": "Deprecated: Concurrent CSI snapshot create", + "description": "Deprecated. Use 'limiter.csiSnapshotsPerCluster' parameter instead" + }, + "csiSnapshotsPerCluster": { + "type": "integer", + "default": 10, + "title": "Cluster-wide limit of concurrent CSI VolumeSnapshot creation requests", + "description": "Cluster-wide limit of concurrent CSI VolumeSnapshot creation requests" + }, + "providerSnapshots": { + "type": "integer", + "default": -1, + "deprecated": true, + "title": "Deprecated: Concurrent cloud provider create", + "description": "Deprecated. Use 'limiter.directSnapshotsPerCluster' parameter instead" + }, + "directSnapshotsPerCluster": { + "type": "integer", + "default": 10, + "title": "Cluster-wide limit of concurrent non-CSI snapshot creation requests", + "description": "Cluster-wide limit of concurrent non-CSI snapshot creation requests" + }, + "imageCopies": { + "type": "integer", + "default": -1, + "deprecated": true, + "title": "Deprecated: Concurrent image copy", + "description": "Deprecated. Use 'limiter.imageCopiesPerCluster' parameter instead" + }, + "imageCopiesPerCluster": { + "type": "integer", + "default": 10, + "title": "Cluster-wide limit of concurrent ImageStream container image operations", + "description": "Cluster-wide limit of concurrent ImageStream container image backup (i.e. copy from) and restore (i.e. copy to) operations" + }, + "executorReplicas": { + "type": "integer", + "default": 3, + "title": "Number of executor service Pod replicas", + "description": "Specifies the number of executor-svc Pods used to process Kasten jobs" + }, + "executorThreads": { + "type": "integer", + "default": 8, + "title": "Executor threads count", + "description": "Specifies the number of threads per executor-svc Pod used to process Kasten jobs" + }, + "workloadRestoresPerAction": { + "type": "integer", + "default": 3, + "title": "Per action limit of concurrent manifest data restores, based on workload", + "description": "Per action limit of concurrent manifest data restores, based on workload (ex. Namespace, Deployment, StatefulSet, VirtualMachine)" + }, + "csiSnapshotRestoresPerAction": { + "type": "integer", + "default": 3, + "title": "Per action limit of concurrent CSI volume provisioning requests", + "description": "Per action limit of concurrent CSI volume provisioning requests when restoring from VolumeSnapshots" + }, + "volumeRestoresPerAction": { + "type": "integer", + "default": 3, + "title": "Per action limit of concurrent volume restore operations", + "description": "Per action limit of concurrent volume restore operations from an exported backup" + }, + "workloadSnapshotsPerAction": { + "type": "integer", + "default": 5, + "title": "Per action limit of concurrent manifest data snapshots, based on workload", + "description": "Per action limit of concurrent manifest data snapshots, based on workload (ex. Namespace, Deployment, StatefulSet, VirtualMachine)" + } + } + }, + "gateway": { + "type": "object", + "title": "Gateway config", + "description": "Configure Gateway service", + "properties": { + "service": { + "type": "object", + "title": "gateway service config", + "properties": { + "externalPort": { + "type": "integer", + "default": 80, + "title": "externalPort for the gateway service", + "description": "Override default 80 externalPort for the gateway service" + } + } + }, + "resources": { + "type": "object", + "title": "Gateway Pod resource config", + "description": "Configure resource request and limits by Gateway Pod", + "properties": { + "requests": { + "type": "object", + "title": "Gateway resource requests", + "description": "Gateway resource requests configuration", + "properties": { + "memory": { + "type": "string", + "default": "300Mi", + "title": "Gateway Pod memory request", + "description": "Gateway Pod memory request", + "examples": [ + "1Gi" + ] + }, + "cpu": { + "type": "string", + "default": "200m", + "title": "Gateway Pod cpu request", + "description": "Gateway Pod cpu request", + "examples": [ + "1" + ] + } + } + }, + "limits": { + "type": "object", + "title": "Gateway resource limits", + "description": "Gateway resource limits configuration", + "properties": { + "memory": { + "type": "string", + "default": "1Gi", + "title": "Gateway Pod memory limit", + "description": "Gateway Pod memory limit", + "examples": [ + "1Gi" + ] + }, + "cpu": { + "type": "string", + "default": "1000m", + "title": "Gateway Pod cpu limit", + "description": "Gateway Pod cpu limit", + "examples": [ + "1" + ] + } + } + } + } + } + } + }, + "kanister": { + "type": "object", + "title": "Kanister config", + "description": "Configuration for Kanister service", + "properties": { + "backupTimeout": { + "type": "integer", + "default": -1, + "deprecated": true, + "title": "Deprecated: Timeout on Kanister backup operations", + "description": "Deprecated. Use 'timeout.blueprintBackup' parameter instead" + }, + "restoreTimeout": { + "type": "integer", + "default": -1, + "deprecated": true, + "title": "Deprecated: Timeout for Kanister restore operations", + "description": "Deprecated. Use 'timeout.blueprintRestore' parameter instead" + }, + "deleteTimeout": { + "type": "integer", + "default": -1, + "deprecated": true, + "title": "Deprecated: Timeout for Kanister delete operations", + "description": "Deprecated. Use 'timeout.blueprintDelete' parameter instead" + }, + "hookTimeout": { + "type": "integer", + "default": -1, + "deprecated": true, + "title": "Deprecated: Timeout for Kanister pre-hook and post-hook operations", + "description": "Deprecated. Use 'timeout.blueprintHooks' parameter instead" + }, + "checkRepoTimeout": { + "type": "integer", + "default": -1, + "deprecated": true, + "title": "Deprecated: Timeout for Kanister checkRepo operations", + "description": "Deprecated. Use 'timeout.checkRepoPodReady' parameter instead" + }, + "statsTimeout": { + "type": "integer", + "default": -1, + "deprecated": true, + "title": "Deprecated: Timeout for Kanister stats operations", + "description": "Deprecated. Use 'timeout.statsPodReady' parameter instead" + }, + "efsPostRestoreTimeout": { + "type": "integer", + "default": -1, + "deprecated": true, + "title": "Deprecated: Timeout for Kanister efsPostRestore operations", + "description": "Deprecated. Use 'timeout.efsRestorePodReady' parameter instead" + }, + "podReadyWaitTimeout": { + "type": "integer", + "default": -1, + "deprecated": true, + "title": "Deprecated: Timeout for Kanister tooling Pods to be ready", + "description": "Deprecated. Use 'timeout.workerPodReady' parameter instead" + }, + "managedDataServicesBlueprintsEnabled": { + "type": "boolean", + "default": true, + "title": "Enable built-in Kanister Blueprints for data services", + "description": "Whether to enable built-in Kanister Blueprints for data services such as Crunchy Data Postgres Operator and K8ssandra" + } + } + }, + "timeout": { + "type": "object", + "title": "Timeouts config", + "description": "Configuration of timeouts", + "properties": { + "blueprintBackup": { + "type": "integer", + "default": 45, + "title": "Specifies the timeout (in minutes) for Blueprint backup actions", + "description": "Specifies the timeout (in minutes) for Blueprint backup actions" + }, + "blueprintRestore": { + "type": "integer", + "default": 600, + "title": "Specifies the timeout (in minutes) for Blueprint restore actions", + "description": "Specifies the timeout (in minutes) for Blueprint restore actions" + }, + "blueprintDelete": { + "type": "integer", + "default": 45, + "title": "Specifies the timeout (in minutes) for Blueprint delete actions", + "description": "Specifies the timeout (in minutes) for Blueprint delete actions" + }, + "blueprintHooks": { + "type": "integer", + "default": 20, + "title": "Specifies the timeout (in minutes) for Blueprint backupPrehook and backupPosthook actions", + "description": "Specifies the timeout (in minutes) for Blueprint backupPrehook and backupPosthook actions" + }, + "checkRepoPodReady": { + "type": "integer", + "default": 20, + "title": "Specifies the timeout (in minutes) for temporary worker Pods used to validate backup repository existence", + "description": "Specifies the timeout (in minutes) for temporary worker Pods used to validate backup repository existence" + }, + "statsPodReady": { + "type": "integer", + "default": 20, + "title": "Specifies the timeout (in minutes) for temporary worker Pods used to collect repository statistics", + "description": "Specifies the timeout (in minutes) for temporary worker Pods used to collect repository statistics" + }, + "efsRestorePodReady": { + "type": "integer", + "default": 45, + "title": "Specifies the timeout (in minutes) for temporary worker Pods used for shareable volume restore operations", + "description": "Specifies the timeout (in minutes) for temporary worker Pods used for shareable volume restore operations" + }, + "workerPodReady": { + "type": "integer", + "default": 15, + "title": "Specifies the timeout (in minutes) for all other temporary worker Pods used during Veeam Kasten review comments fixedoperations", + "description": "Specifies the timeout (in minutes) for all other temporary worker Pods used during Veeam Kasten operations" + }, + "jobWait": { + "type": "string", + "default": "", + "title": "Specifies the timeout (in minutes) for completing execution of any child job, after which the parent job will be canceled", + "description": "Specifies the timeout (in minutes) for completing execution of any child job, after which the parent job will be canceled. If no value is set, a default of 10 hours will be used" + } + } + }, + "awsConfig": { + "type": "object", + "title": "AWS config", + "description": "AWS config", + "properties": { + "assumeRoleDuration": { + "type": "string", + "default": "", + "title": "Duration of a session token generated by AWS for an IAM role", + "description": "The minimum value is 15 minutes, and the maximum value is determined by the maximum session duration setting for that IAM role. For documentation on how to view and edit the maximum session duration for an IAM role, refer to https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_use.html#id_roles_use_view-role-max-session. The value accepts a number followed by a single character, 'm' (for minutes) or 'h' (for hours). Examples include: 60m or 2h" + }, + "efsBackupVaultName": { + "type": "string", + "default": "k10vault", + "title": "the AWS EFS backup vault name", + "description": "Set the AWS EFS backup vault name" + } + } + }, + "azure": { + "type": "object", + "title": "Azure config", + "description": "Azure config", + "properties": { + "useDefaultMSI": { + "type": "boolean", + "default": false, + "title": "Use the default Managed Identity", + "description": "Set to true - profile does not need a secret, Default Managed Identity will be used" + }, + "useFederatedIdentity": { + "type": "boolean", + "default": false, + "title": "Use the Federated Identity", + "description": "Set to true - injected Federated Identity will be used" + } + } + }, + "google": { + "type": "object", + "title": "Google config", + "description": "Google auth config", + "properties": { + "workloadIdentityFederation": { + "type": "object", + "title": "Google Workload Identity Federation config", + "description": "config for Google Workload Identity Federation", + "properties": { + "enabled": { + "type": "boolean", + "default": false, + "title": "Enable Google Workload Identity Federation (GWIF) for K10", + "description": "Set to true - Google Workload Identity Federation is enabled for K10" + }, + "idp": { + "type": "object", + "title": "Identity Provider config", + "description": "Identity Provider config", + "properties": { + "type": { + "type": "string", + "default": "", + "title": "Type of the Identity Provider for GWIF", + "description": "Set the type of IdP for GWIF" + }, + "aud": { + "type": "string", + "default": "", + "title": "The audience that ID token is intended for", + "description": "Set the name of the audience that ID token is intended for" + } + } + } + } + } + } + }, + "grafana": { + "type": "object", + "title": "Grafana config", + "description": "Settings for Grafana service", + "properties": { + "external": { + "type": "object", + "title": "Configuration related to externally installed Grafana instance", + "description": "If Grafana instance that gets installed with K10 is disabled using grafana.enabled=false, this field can be used to configure externally installed Grafana instance.", + "properties": { + "url": { + "type": "string", + "default": "", + "title": "URL of externally installed Grafana instance", + "description": "If Grafana instance that gets installed with K10 is disabled using grafana.enabled=false, this field can be used to specify URL of externally installed Grafana instance." + } + } + } + } + }, + "encryption": { + "type": "object", + "title": "Encryption config", + "description": "Encryption config", + "properties": { + "primaryKey": { + "type": "object", + "title": "primaryKey for encrypting of K10 primary key", + "description": "primaryKey is used for enabling encryption of K10 primary key", + "properties": { + "awsCmkKeyId": { + "type": "string", + "default": "", + "title": "The AWS CMK key ID for encrypting K10 Primary Key", + "description": "Ensures AWS CMK is used for encrypting K10 primary key" + }, + "vaultTransitKeyName": { + "type": "string", + "default": "", + "title": "Vault transit Key Name", + "description": "Vault Transit key name for Vault integration" + }, + "vaultTransitPath": { + "type": "string", + "default": "", + "title": "Vault transit path", + "description": "Vault transit path for Vault integration" + } + } + } + } + }, + "vmWare": { + "type": "object", + "title": "VMWare integration config", + "properties": { + "taskTimeoutMin": { + "type": "integer", + "default": 60, + "title": "the timeout for VMWare operations", + "description": "the timeout for VMWare operations in minutes" + } + } + }, + "vault": { + "type": "object", + "title": "Vault config", + "description": "Vault integration configuration", + "properties": { + "secretName": { + "type": "string", + "default": "", + "title": "Vault secret name", + "description": "Vault secret name" + }, + "address": { + "type": "string", + "default": "http://vault.vault.svc:8200", + "title": "Vault address", + "description": "Specify Vault endpoint" + }, + "role": { + "type": "string", + "default": "", + "title": "Vault Service Account Role", + "description": "Role that was bound to the service account name and namespace from cluster" + }, + "serviceAccountTokenPath": { + "type": "string", + "default": "", + "title": "Token path for Vault Service Account Role", + "description": "Default: '/var/run/secrets/kubernetes.io/serviceaccount/token'" + } + } + }, + "kubeVirtVMs": { + "type": "object", + "properties": { + "snapshot": { + "type": "object", + "properties": { + "unfreezeTimeout": { + "type": "string", + "title": "Unfreeze timeout for Virtual Machines", + "description": "Time within which K10 is expected to complete the Virtual Machine's backup and thaw the Virtual Machine.", + "default": "5m" + } + } + } + } + }, + "excludedApps": { + "type": "array", + "items": { + "type": "string" + }, + "default": [ + "kube-system", + "kube-ingress", + "kube-node-lease", + "kube-public", + "kube-rook-ceph" + ], + "title": "List of applications to be excluded", + "description": "List of applications to be excluded from the dashboard & compliance considerations" + }, + "logging": { + "type": "object", + "properties": { + "internal": { + "title": "Enable internal logging service", + "description": "Enable use of internal logging service", + "type": "boolean", + "default": true + }, + "fluentbit_endpoint": { + "title": "Use external fluentbit endpoint", + "description": "Specify a fluentbit endpoint to collect logs, cannot be used with the internal logging service (logging.internal=true)", + "type": "string", + "default": "" + } + } + }, + "maxJobWaitDuration": { + "type": "string", + "default": "", + "deprecated": true, + "title": "Deprecated: Maximum duration for jobs in minutes", + "description": "Deprecated. Use 'timeout.jobWait' parameter instead" + }, + "forceRootInKanisterHooks": { + "type": "boolean", + "default": false, + "deprecated": true, + "title": "Deprecated: Run Kanister Hooks as root", + "description": "Deprecated. Use 'forceRootInBlueprintActions' parameter instead" + }, + "forceRootInBlueprintActions": { + "type": "boolean", + "default": true, + "title": "Run Kanister Blueprints as root", + "description": "Forces any Pod created by a Blueprint to run as root user" + }, + "ephemeralPVCOverhead": { + "type": "string", + "default": "0.1", + "title": "Storage overhead for ephemeral PVCs", + "description": "Set the percentage increase for the ephemeral Persistent Volume Claim's storage request, e.g. pvc size = (file raw size) * (1 + `ephemeralPVCOverhead`)" + }, + "kastenDisasterRecovery": { + "type": "object", + "properties": { + "quickMode": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean", + "default": false, + "description": "Enables K10 Quick Disaster Recovery feature, with ability to restore necessary K10 resources and exported restore points of applications.", + "title": "Enable K10 Quick Disaster Recovery." + } + } + } + } + }, + "fips": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean", + "default": false, + "description": "Enables K10 FIPS (Federal Information Processing Standard) mode of operation.", + "title": "Enable K10 FIPS mode of operation." + } + } + }, + "workerPodCRDs": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean", + "default": false, + "title": "Enable ActionPodSpec and ActionPodSpecBinding CRDs", + "description": "Enables the use of ActionPodSpec and ActionPodSpecBinding for granular resource configuration of temporary Pods associated with Kasten Actions" + }, + "resourcesRequests": { + "type": "object", + "title": "Maximum resource requests", + "description": "Specifies the cluster-wide, maximum values for resource requests which may be used in any ActionPodSpec", + "properties": { + "maxMemory": { + "type": "string", + "default": "", + "title": "Maximum memory request", + "description": "Specifies the cluster-wide, maximum value for memory resource requests which may be used in any ActionPodSpec", + "examples": [ + "1Gi" + ] + }, + "maxCPU": { + "type": "string", + "default": "", + "title": "Maximum cpu request", + "description": "Specifies the cluster-wide, maximum value for CPU resource requests which may be used in any ActionPodSpec", + "examples": [ + "1" + ] + } + } + }, + "defaultActionPodSpec": { + "type": "string", + "default": "", + "title": "Default ActionPodSpec name", + "description": "The name of ActionPodSpec that will be used by default for worker Pod resources. if empty, the default APS is omitted" + } + } + } + } +} diff --git a/charts/kasten/k10/7.5.401/values.yaml b/charts/kasten/k10/7.5.401/values.yaml new file mode 100644 index 0000000000..f2a3316968 --- /dev/null +++ b/charts/kasten/k10/7.5.401/values.yaml @@ -0,0 +1,600 @@ +#file: noinspection ComposeUnknownKeys,ComposeUnknownValues +# Default values for k10. +# This is a YAML-formatted file. +# Declare variables to be passed into your templates. + +rbac: + create: true +serviceAccount: + # Specifies whether a ServiceAccount should be created + create: true + # The name of the ServiceAccount to use. + # If not set and create is true, a name is derived using the release and chart names. + name: "" + +scc: + create: false + priority: 0 + +networkPolicy: + create: true + +global: + # These are the default values for picking k10 images. They can be overridden + # to specify a particular registy and tag. + image: + registry: gcr.io/kasten-images + tag: '' + pullPolicy: Always + airgapped: + repository: '' + persistence: + mountPath: "/mnt/k10state" + enabled: true + ## If defined, storageClassName: + ## If set to "-", storageClassName: "", which disables dynamic provisioning + ## If undefined (the default) or set to null, no storageClassName spec is + ## set, choosing the default provisioner. (gp2 on AWS, standard on + ## GKE, AWS & OpenStack) + ## + storageClass: "" + accessMode: ReadWriteOnce + size: 20Gi + metering: + size: 2Gi + catalog: + size: "" + jobs: + size: "" + logging: + size: "" + podLabels: {} + podAnnotations: {} + ## Set it to true while generating helm operator + rhMarketPlace: false + ## these values should not be provided us, these are to be used by + ## red hat marketplace + images: + aggregatedapis: '' + auth: '' + bloblifecyclemanager: '' + catalog: '' + configmap-reload: '' + controllermanager: '' + crypto: '' + dashboardbff: '' + datamover: '' + dex: '' + events: '' + executor: '' + frontend: '' + gateway: '' + init: '' + jobs: '' + kanister-tools: '' + kanister: '' + k10tools: '' + logging: '' + metering: '' + ocpconsoleplugin: '' + paygo_daemonset: '' + prometheus: '' + repositories: '' + state: '' + upgrade: '' + vbrintegrationapi: '' + garbagecollector: '' + metric-sidecar: '' + imagePullSecret: '' + prometheus: + external: + host: '' #FQDN of prometheus-service + port: '' + baseURL: '' + network: + enable_ipv6: false + +## OpenShift route configuration. +route: + enabled: false + # Host name for the route + host: "" + # Default path for the route + path: "" + + annotations: {} + # kubernetes.io/tls-acme: "true" + # haproxy.router.openshift.io/disable_cookies: "true" + # haproxy.router.openshift.io/balance: roundrobin + + labels: {} + # key: value + + # TLS configuration + tls: + enabled: false + # What to do in case of an insecure traffic edge termination + insecureEdgeTerminationPolicy: "Redirect" + # Where this TLS configuration should terminate + termination: "edge" + +dexImage: + registry: ghcr.io + repository: dexidp + image: dex + +kanisterToolsImage: + registry: ghcr.io + repository: kanisterio + image: kanister-tools + pullPolicy: Always + +ingress: + create: false + annotations: {} + name: "" + tls: + enabled: false + secretName: "" #TLS secret name + class: "" #Ingress controller type + host: "" #ingress object host name + urlPath: "" #url path for k10 gateway + pathType: "ImplementationSpecific" + defaultBackend: + service: + enabled: false + name: "" + port: + name: "" + number: 0 + resource: + enabled: false + apiGroup: "" + kind: "" + name: "" + +eula: + accept: false #true value if EULA accepted + +license: "" #base64 encoded string provided by Kasten + +cluster: + domainName: "" + +multicluster: + enabled: true + primary: + create: false + name: "" + ingressURL: "" + +prometheus: + rbac: + create: false + server: + # UID and groupid are from prometheus helm chart + enabled: true + securityContext: + runAsUser: 65534 + runAsNonRoot: true + runAsGroup: 65534 + fsGroup: 65534 + retention: 30d + persistentVolume: + storageClass: "" + size: 8Gi + fullnameOverride: prometheus-server + baseURL: /k10/prometheus/ + prefixURL: /k10/prometheus + +jaeger: + enabled: false + agentDNS: "" + +service: + externalPort: 8000 + internalPort: 8000 + aggregatedApiPort: 10250 + +secrets: + awsAccessKeyId: '' + awsSecretAccessKey: '' + awsIamRole: '' + awsClientSecretName: '' + googleApiKey: '' + googleProjectId: '' + googleClientSecretName: '' + dockerConfig: '' + dockerConfigPath: '' + azureTenantId: '' + azureClientId: '' + azureClientSecret: '' + azureClientSecretName: '' + azureResourceGroup: '' + azureSubscriptionID: '' + azureResourceMgrEndpoint: '' + azureADEndpoint: '' + azureADResourceID: '' + microsoftEntraIDEndpoint: '' + microsoftEntraIDResourceID: '' + azureCloudEnvID: '' + tlsSecret: '' + vsphereEndpoint: '' + vsphereUsername: '' + vspherePassword: '' + vsphereClientSecretName: '' + +metering: + reportingKey: "" #[base64-encoded key] + consumerId: "" #project: + awsRegion: '' + awsMarketPlaceIamRole: '' + awsMarketplace: false # AWS cloud metering license mode + awsManagedLicense: false # AWS managed license mode + licenseConfigSecretName: '' # AWS managed license config secret for non-eks clusters + serviceAccount: + create: false + name: "" + mode: '' # controls metric and license reporting (set to `airgap` for private-network installs) + redhatMarketplacePayg: false # Redhat cloud metering license mode + reportCollectionPeriod: 1800 # metric report collection period in seconds + reportPushPeriod: 3600 # metric report push period in seconds + promoID: '' # sets the K10 promotion ID + +clusterName: '' + +logLevel: info + +externalGateway: + create: false + # Any standard service annotations + annotations: {} + # Host and domain name for the K10 API server + fqdn: + name: "" + #Supported types route53-mapper, external-dns + type: "" + # ARN for the AWS ACM SSL certificate used in the K10 API server (load balancer) + awsSSLCertARN: '' + +auth: + groupAllowList: [] +# - "group1" +# - "group2" + basicAuth: + enabled: false + secretName: "" #htpasswd based existing secret + htpasswd: "" #htpasswd string, which will be used for basic auth + tokenAuth: + enabled: false + oidcAuth: + enabled: false + providerURL: "" #URL to your OIDC provider + redirectURL: "" #URL to the K10 gateway service + scopes: "" #Space separated OIDC scopes required for userinfo. Example: "profile email" + prompt: "select_account" #The prompt type to be requested with the OIDC provider. Default is select_account. + clientID: "" #ClientID given by the OIDC provider for K10 + clientSecret: "" #ClientSecret given by the OIDC provider for K10 + clientSecretName: "" #The Kubernetes Secret that contains ClientID and ClientSecret given by the OIDC provider for K10 + usernameClaim: "" #Claim to be used as the username + usernamePrefix: "" #Prefix that has to be used with the username obtained from the username claim + groupClaim: "" #Name of a custom OpenID Connect claim for specifying user groups + groupPrefix: "" #All groups will be prefixed with this value to prevent conflicts. + logoutURL: "" #URL to your OIDC provider's logout endpoint + #OIDC config based existing secret. + #Must include providerURL, redirectURL, scopes, clientID/secret and logoutURL. + secretName: "" + sessionDuration: "1h" #Maximum OIDC session duration. Default value is 1 hour + refreshTokenSupport: false #Enable Refresh Token support. Disabled by default + openshift: + enabled: false + serviceAccount: "" #service account used as the OAuth client + clientSecret: "" #The token from the service account + clientSecretName: "" #The secret with the token from the service account + dashboardURL: "" #The URL for accessing K10's dashboard + openshiftURL: "" #The URL of the Openshift API server + insecureCA: false + useServiceAccountCA: false + secretName: "" # The Kubernetes Secret that contains OIDC settings + usernameClaim: "email" + usernamePrefix: "" + groupnameClaim: "groups" + groupnamePrefix: "" + caCertsAutoExtraction: true # Configures if K10 should automatically extract CA certificates from the OCP cluster. + ldap: + enabled: false + restartPod: false # Enable this value to force a restart of the authentication service pod + dashboardURL: "" #The URL for accessing K10's dashboard + host: "" + insecureNoSSL: false + insecureSkipVerifySSL: false + startTLS: false + bindDN: "" + bindPW: "" + bindPWSecretName: "" + userSearch: + baseDN: "" + filter: "" + username: "" + idAttr: "" + emailAttr: "" + nameAttr: "" + preferredUsernameAttr: "" + groupSearch: + baseDN: "" + filter: "" + userMatchers: [] +# - userAttr: +# groupAttr: + nameAttr: "" + secretName: "" # The Kubernetes Secret that contains OIDC settings + usernameClaim: "email" + usernamePrefix: "" + groupnameClaim: "groups" + groupnamePrefix: "" + k10AdminUsers: [] + k10AdminGroups: [] + +optionalColocatedServices: + vbrintegrationapi: + enabled: true + +cacertconfigmap: + name: "" #Name of the configmap + +apiservices: + deployed: true # If false APIService objects will not be deployed + +# Deprecated. Use 'injectGenericVolumeBackupSidecar' parameter instead. +injectKanisterSidecar: + enabled: false + namespaceSelector: + matchLabels: {} + # Set objectSelector to filter workloads + objectSelector: + matchLabels: {} + webhookServer: + port: 8080 # should not conflict with config server port (8000) + +injectGenericVolumeBackupSidecar: + enabled: false + namespaceSelector: + matchLabels: {} + # Set objectSelector to filter workloads + objectSelector: + matchLabels: {} + webhookServer: + port: 8080 # should not conflict with config server port (8000) + +genericStorageBackup: + token: "" + +kanisterPodCustomLabels : "" + +kanisterPodCustomAnnotations : "" + +features: + backgroundMaintenanceRun: true # Key must be deleted to deactivate. Setting to false will not work. + +# Deprecated. Use 'workerPodMetricSidecar' parameter instead. +kanisterPodMetricSidecar: + enabled: false + metricLifetime: "2m" + pushGatewayInterval: "30s" + resources: + requests: + memory: "" + cpu: "" + limits: + memory: "" + cpu: "" + +workerPodMetricSidecar: + enabled: true + metricLifetime: "2m" + pushGatewayInterval: "30s" + resources: + requests: + memory: "" + cpu: "" + limits: + memory: "" + cpu: "" + +genericVolumeSnapshot: + resources: + requests: + memory: "" + cpu: "" + limits: + memory: "" + cpu: "" + +garbagecollector: + daemonPeriod: 21600 + keepMaxActions: 1000 + actions: + enabled: false + +resources: {} + +defaultPriorityClassName: "" +priorityClassName: {} + +services: + executor: + hostNetwork: false + # Deprecated. Use 'limiter.executorThreads' parameter instead. + workerCount: -1 + # Deprecated. Use 'limiter.csiSnapshotRestoresPerAction' parameter instead. + maxConcurrentRestoreCsiSnapshots: -1 + # Deprecated. Use 'limiter.volumeRestoresPerAction' parameter instead. + maxConcurrentRestoreGenericVolumeSnapshots: -1 + # Deprecated. Use 'limiter.workloadRestoresPerAction' parameter instead. + maxConcurrentRestoreWorkloads: -1 + dashboardbff: + hostNetwork: false + securityContext: + runAsUser: 1000 # Will override any USER instruction that a container image set for running the entrypoint and command. + fsGroup: 1000 + runAsNonRoot: true + seccompProfile: + type: RuntimeDefault + aggregatedapis: + hostNetwork: false + +siem: + logging: + cluster: + enabled: true + cloud: + path: k10audit/ + awsS3: + enabled: true + +limiter: + # Deprecated. Use 'limiter.snapshotExportsPerAction' parameter instead. + concurrentSnapConversions: -1 + snapshotExportsPerAction: 3 + # Deprecated. Use 'limiter.genericVolumeBackupsPerCluster' parameter instead. + genericVolumeSnapshots: -1 + genericVolumeBackupsPerCluster: 10 + # Deprecated. Use 'limiter.snapshotExportsPerCluster' parameter instead. + genericVolumeCopies: -1 + snapshotExportsPerCluster: 10 + # Deprecated. Use 'limiter.volumeRestoresPerCluster' parameter instead. + genericVolumeRestores: -1 + volumeRestoresPerCluster: 10 + # Deprecated. Use 'limiter.csiSnapshotsPerCluster' parameter instead. + csiSnapshots: -1 + csiSnapshotsPerCluster: 10 + # Deprecated. Use 'limiter.directSnapshotsPerCluster' parameter instead. + providerSnapshots: -1 + directSnapshotsPerCluster: 10 + # Deprecated. Use 'limiter.imageCopiesPerCluster' parameter instead. + imageCopies: -1 + imageCopiesPerCluster: 10 + executorReplicas: 3 + executorThreads: 8 + workloadRestoresPerAction: 3 + csiSnapshotRestoresPerAction: 3 + volumeRestoresPerAction: 3 + workloadSnapshotsPerAction: 5 + +gateway: + service: + externalPort: 80 + resources: + requests: + memory: 300Mi + cpu: 200m + limits: + memory: 1Gi + cpu: 1000m + +kanister: + # Deprecated. Use 'timeout.blueprintBackup' parameter instead. + backupTimeout: -1 + # Deprecated. Use 'timeout.blueprintRestore' parameter instead. + restoreTimeout: -1 + # Deprecated. Use 'timeout.blueprintDelete' parameter instead. + deleteTimeout: -1 + # Deprecated. Use 'timeout.blueprintHooks' parameter instead. + hookTimeout: -1 + # Deprecated. Use 'timeout.checkRepoPodReady' parameter instead. + checkRepoTimeout: -1 + # Deprecated. Use 'timeout.statsPodReady' parameter instead. + statsTimeout: -1 + # Deprecated. Use 'timeout.efsRestorePodReady' parameter instead. + efsPostRestoreTimeout: -1 + # Deprecated. Use 'timeout.workerPodReady' parameter instead. + podReadyWaitTimeout: -1 + managedDataServicesBlueprintsEnabled: true + +timeout: + blueprintBackup: 45 + blueprintRestore: 600 + blueprintDelete: 45 + blueprintHooks: 20 + checkRepoPodReady: 20 + statsPodReady: 20 + efsRestorePodReady: 45 + workerPodReady: 15 + jobWait: "" + +awsConfig: + assumeRoleDuration: "" + efsBackupVaultName: "k10vault" + +excludedApps: ["kube-system", "kube-ingress", "kube-node-lease", "kube-public", "kube-rook-ceph"] + +grafana: + external: + url: "" # can be used to configure the URL of externally installed Grafana instance. If it's provided, grafana.enabled must be false. + +encryption: + primaryKey: # primaryKey is used for enabling encryption of K10 primary key + awsCmkKeyId: '' # Ensures AWS CMK is used for encrypting K10 primary key + vaultTransitKeyName: '' + vaultTransitPath: '' + +vmWare: + taskTimeoutMin: 60 + +azure: + useDefaultMSI: false + useFederatedIdentity: false + +google: + workloadIdentityFederation: + enabled: false + idp: + type: "" + aud: "" + +vault: + role: "" # Role that was bound to the service account name and namespace from cluster + serviceAccountTokenPath: "" # This will default to /var/run/secrets/kubernetes.io/serviceaccount/token within the code if left blank + address: "http://vault.vault.svc:8200" # Address for dev mode in cluster vault server in vault namespace + secretName: "" # Ensures backward compatibility for now. We can remove once we tell all customers this is deprecated. + # This is how the token can be passed into default if K8S auth mode fails for whatever reason. + +kubeVirtVMs: + snapshot: + unfreezeTimeout: "5m" + +logging: + internal: true + # Used to set an external fluentbit endpoint. 'logging.internal' must be set to false. + fluentbit_endpoint: "" + +# Deprecated. Use 'timeout.jobWait' parameter instead. +maxJobWaitDuration: "" + +# Deprecated. Use 'forceRootInBlueprintActions' parameter instead. +forceRootInKanisterHooks: false +forceRootInBlueprintActions: true + +ephemeralPVCOverhead: "0.1" + +datastore: + parallelUploads: 8 + parallelDownloads: 8 + parallelBlockUploads: 8 + parallelBlockDownloads: 8 + estimationType: adaptive + adaptiveEstimationThreshold: 300000 + +kastenDisasterRecovery: + quickMode: + enabled: false + +fips: + enabled: false + +workerPodCRDs: + enabled: false + resourcesRequests: + maxCPU: "" + maxMemory: "" + defaultActionPodSpec: "" diff --git a/index.yaml b/index.yaml index 5d6d06a59b..df9fe239b3 100644 --- a/index.yaml +++ b/index.yaml @@ -370,7 +370,7 @@ entries: version: 0.9.0 artifactory-ha: - annotations: - artifactoryServiceVersion: 7.104.17 + artifactoryServiceVersion: 7.104.18 catalog.cattle.io/certified: partner catalog.cattle.io/display-name: JFrog Artifactory HA catalog.cattle.io/featured: "2" @@ -379,6 +379,42 @@ entries: metadataVersion: 7.118.2 observabilityVersion: 1.31.11 apiVersion: v2 + appVersion: 7.104.10 + created: "2025-02-26T00:02:04.136871562Z" + dependencies: + - condition: postgresql.enabled + name: postgresql + repository: https://charts.jfrog.io/ + version: 10.3.18 + description: Universal Repository Manager supporting all major packaging formats, + build tools and CI servers. + digest: 5b86854bb004a07e5d3ca0ec7dabc2b8284b7857e05aed79869c6caa36b6d265 + home: https://www.jfrog.com/artifactory/ + icon: file://assets/icons/artifactory-ha.png + keywords: + - artifactory + - jfrog + - devops + kubeVersion: '>= 1.19.0-0' + maintainers: + - email: installers@jfrog.com + name: Chart Maintainers at JFrog + name: artifactory-ha + sources: + - https://github.com/jfrog/charts + type: application + urls: + - assets/jfrog/artifactory-ha-107.104.10.tgz + version: 107.104.10 + - annotations: + artifactoryServiceVersion: 7.104.17 + catalog.cattle.io/certified: partner + catalog.cattle.io/display-name: JFrog Artifactory HA + catalog.cattle.io/kube-version: '>= 1.19.0-0' + catalog.cattle.io/release-name: artifactory-ha + metadataVersion: 7.118.2 + observabilityVersion: 1.31.11 + apiVersion: v2 appVersion: 7.104.9 created: "2025-02-22T00:01:59.700509257Z" dependencies: @@ -388,7 +424,7 @@ entries: version: 10.3.18 description: Universal Repository Manager supporting all major packaging formats, build tools and CI servers. - digest: f02585062c47f1d7822896ce2ed42cca7850bff9e26c558336c876f10c8ddd3a + digest: d31946e40835701210341c43a214f4de826eb64d457d06361dfe80e2ca05f1ef home: https://www.jfrog.com/artifactory/ icon: file://assets/icons/artifactory-ha.png keywords: @@ -2271,6 +2307,40 @@ entries: - assets/jfrog/artifactory-ha-107.55.14.tgz version: 107.55.14 artifactory-jcr: + - annotations: + catalog.cattle.io/certified: partner + catalog.cattle.io/display-name: JFrog Container Registry + catalog.cattle.io/kube-version: '>= 1.19.0-0' + catalog.cattle.io/release-name: artifactory-jcr + apiVersion: v2 + appVersion: 7.104.10 + created: "2025-02-26T00:02:04.602977176Z" + dependencies: + - name: artifactory + repository: file://charts/artifactory + version: 107.104.10 + description: JFrog Container Registry + digest: 91cb8a1a342c50d9c76be1ec0f38dae5ca9f2f85c47c8a456d04e7a2ec22e334 + home: https://jfrog.com/container-registry/ + icon: file://assets/icons/artifactory-jcr.png + keywords: + - artifactory + - jfrog + - container + - registry + - devops + - jfrog-container-registry + kubeVersion: '>= 1.19.0-0' + maintainers: + - email: helm@jfrog.com + name: Chart Maintainers at JFrog + name: artifactory-jcr + sources: + - https://github.com/jfrog/charts + type: application + urls: + - assets/jfrog/artifactory-jcr-107.104.10.tgz + version: 107.104.10 - annotations: catalog.cattle.io/certified: partner catalog.cattle.io/display-name: JFrog Container Registry @@ -5342,6 +5412,38 @@ entries: - assets/cerbos/cerbos-0.37.0.tgz version: 0.37.0 cf-runtime: + - annotations: + artifacthub.io/changes: | + - kind: security + description: "updated fs-ops with security fixes" + artifacthub.io/containsSecurityUpdates: "true" + catalog.cattle.io/certified: partner + catalog.cattle.io/display-name: Codefresh + catalog.cattle.io/kube-version: '>=1.18-0' + catalog.cattle.io/release-name: cf-runtime + apiVersion: v2 + created: "2025-02-26T00:02:03.144426893Z" + dependencies: + - name: cf-common + repository: oci://quay.io/codefresh/charts + version: 0.21.0 + description: A Helm chart for Codefresh Runner + digest: 15dd3b9ac4677391b52f3f0802124df97415853fec33efa15458fd7a7bb67a5c + home: https://codefresh.io/ + icon: file://assets/icons/cf-runtime.png + keywords: + - codefresh + - runner + kubeVersion: '>=1.18-0' + maintainers: + - name: codefresh + url: https://codefresh-io.github.io/ + name: cf-runtime + sources: + - https://github.com/codefresh-io/venona + urls: + - assets/codefresh/cf-runtime-7.5.5.tgz + version: 7.5.5 - annotations: artifacthub.io/changes: | - kind: security @@ -19270,6 +19372,49 @@ entries: - assets/hpe/hpe-csi-info-metrics-1.0.2.tgz version: 1.0.2 instana-agent: + - annotations: + artifacthub.io/links: | + - name: Instana website + url: https://www.ibm.com/products/instana + - name: Instana Helm charts + url: https://github.com/instana/helm-charts + catalog.cattle.io/certified: partner + catalog.cattle.io/display-name: Instana Agent + catalog.cattle.io/kube-version: '>=1.21-0' + catalog.cattle.io/release-name: instana-agent + apiVersion: v2 + appVersion: 1.290.0 + created: "2025-02-26T00:02:04.101653275Z" + description: Instana Agent for Kubernetes + digest: f594bdca1decae30a169f5a298ee592867cd3295e0280e65c6f2caa1f45aec46 + home: https://www.instana.com/ + icon: file://assets/icons/instana-agent.png + kubeVersion: '>=1.21-0' + maintainers: + - email: felix.marx@ibm.com + name: FelixMarxIBM + - email: henning.treu@ibm.com + name: htreu + - email: konrad.ohms@de.ibm.com + name: Konrad-Ohms + - email: fredrik.gundersen@ibm.com + name: FredrikAtIBM + - email: jefiyamj@ibm.com + name: Jefiya-MJ + - email: milica.cvrkota@ibm.com + name: Milica-Cvrkota-IBM + - email: Nagaraj.Kandoor@ibm.com + name: nagaraj-kandoor + - email: Vineeth.Soman@ibm.com + name: vineethsoman03 + - email: Rashmi.Swamy@ibm.com + name: rashmiswamyibm + name: instana-agent + sources: + - https://github.com/instana/instana-agent-docker + urls: + - assets/instana/instana-agent-2.0.13.tgz + version: 2.0.13 - annotations: artifacthub.io/links: | - name: Instana website @@ -20881,6 +21026,31 @@ entries: catalog.cattle.io/kube-version: '>= 1.17.0-0' catalog.cattle.io/release-name: k10 apiVersion: v2 + appVersion: 7.5.4 + created: "2025-02-26T00:02:05.744422349Z" + dependencies: + - condition: prometheus.server.enabled + name: prometheus + repository: "" + version: 26.1.0 + description: Kasten’s K10 Data Management Platform + digest: 1aa76ce6f23f0794195aae7bcbea85b646e6b7b4af621e0ef60080038b1e6bcd + home: https://kasten.io/ + icon: file://assets/icons/k10.png + kubeVersion: '>= 1.17.0-0' + maintainers: + - email: contact@kasten.io + name: kastenIO + name: k10 + urls: + - assets/kasten/k10-7.5.401.tgz + version: 7.5.401 + - annotations: + catalog.cattle.io/certified: partner + catalog.cattle.io/display-name: K10 + catalog.cattle.io/kube-version: '>= 1.17.0-0' + catalog.cattle.io/release-name: k10 + apiVersion: v2 appVersion: 7.5.3 created: "2025-02-07T00:02:02.733545017Z" dependencies: @@ -20889,7 +21059,7 @@ entries: repository: "" version: 26.1.0 description: Kasten’s K10 Data Management Platform - digest: 17b48d6d7f116c27789561e002942e9619f1359f6cd34e998221004c4be66fb1 + digest: 549a672b2fc9096da4fc5575aa66038fc5b389aaced584c576680edbb112c832 home: https://kasten.io/ icon: file://assets/icons/k10.png kubeVersion: '>= 1.17.0-0' @@ -51661,4 +51831,4 @@ entries: urls: - assets/netfoundry/ziti-host-1.5.1.tgz version: 1.5.1 -generated: "2025-02-25T00:01:54.130021304Z" +generated: "2025-02-26T00:02:02.355703615Z"

    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 literal 0 HcmV?d00001 diff --git a/assets/kasten/k10-7.5.301.tgz b/assets/kasten/k10-7.5.301.tgz index 7653155bde150b7af17fafe8956de8580d28329b..4f296051ff434055a8f3a07f5f33a65c71ee629d 100644 GIT binary patch delta 178278 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ⅆ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 LV(UBn09+CP?M~@N literal 0 HcmV?d00001 diff --git a/assets/jfrog/artifactory-ha-107.104.9.tgz b/assets/jfrog/artifactory-ha-107.104.9.tgz index d96751d590056ce1b5a79ae43b808507f06b248c..81d95738121471f5177ae01ed51ba3536fe59f11 100644 GIT binary patch delta 211972 zcmV)RK(oKK+zqPS4UkEHwcVPOlX{xO{#tH3J+nOtL_!j3ieLfIwl?+W zZ~q=#JP1;xBtP=#);hb>7z8eW3*h47J~5Ycgd@rLESTWV!31*|&hRw(+x`8z-EQ~s z?k@bd+wE5W+k4#W{;jwBWbeuD-tLpfdw=Wpc6&R!e?#4S97E%OFBcN?zjg22S9NlK zk{?b}Cb6U}75x@M-gX!eenBJh6XAlgw2z*Iz3y(;~QS z+WMR%1PfxiDR1$A7D7`Zaf~JQr`CHKZnqF(LqYfj9@2!$nfu-gcY0y3*TNZfWf$8m zoMjd4eA9|aM$(w15hbGE3Q)w-m`bRUF(JpCh~JZtq|Lq3qRsaYedQ&t+Sn3Vhs@+9` zt6*uS&1(lOEAA5wefO1aI#>?(cSYBhu}EKH1sZ8Dc#8W*a|__D0*g!|mN~ z;=SR{&f{o%XPfMe;_Ybro6%F;CA)FA`^_Hielv>qc3NYS5{{+1W7^x@?d_nu6Lg=R zb)WY4cKdt1@M(9ixBd9*?p}YV+x~0^>QDBooc{})p|c4Uhzf+!FZ*v^2bxEaB!+t(YCI>Gm=kn4 zAt{O`I0at90G$v{Li7vEk(jVNiP3q=F45%#OEhCSQte_!)A8RGp#3N!LLfOIw)rKM z6ZGK&3V+i3^PJ2=kq^(w3|(Kh#t9qZM6s`bYlu$qn4FR^6;jbF!wHHYX3;s}j$jpa zPx2%gkcbm0Gz5eKG@V%xKaiRJ`W8=#`o5-Fg>c_7e<;E!8WO}V2&ZvOQj`ldbv>-m z&_xk?NJWghxp>7Pj3|_Ntb2OPB9(|jvdp;F8L;G<3Au|QESB%23Cl$?gE=*hwvYa3 zHP)znG^4LA)w8I4nwyRNzJ|=RF2Yi6i#R6gEr6ujECSGuE@_genMW+6`s<^AzC1mC z_3r!QH;1KXz>^^b7=JVR$PDYi>su{3Lgv-fMRr)x}lSDC2NRnxmU`fyh;KjZ} zfRq_h1RU08YYqN;YykFKEkC*p5;k$!V((I`sir%mdO)N1W6@u znC>~HyZAjPLYlwSdwmMswfDMjXxkJ8wOUU#Ys5;~&qa3lz)Q7F$ zL@XWAaSo@=mocoZp;L9mHc2&9yChc^u$5$WfY^1sd3-&9zP?smQp;UNFbkjwZfVIC z6@c1}SbsBM^_RCdbebZMKL`9JlJFEudO?!emOsrop5%h!+S|i0E943wCCHpal+bg< ze&vZ2Tg6v*`W0CvG78a&rfR^N)ODaP3b^%#Bw?3&8B;{A@H8`g<1#L%cGTOO-fWMn zIMv%su{#F0s2Pp(V9f2IxK)M5To8oSumZPa=6~`V&XCARL`O4?LcjACsuc7sr$yl8 z{Xk}x7ar0lM%f_Q?uhTEGK6mF0O|RXE2w9pI|xZqfS|4V+u>McLcAjnlENK zI_k5k7A05+;GI#sUBB320vtgDh2S<+F^@xyA=I$kAmi+677STD3k3aN)vl`= zC3r}Zq7%vSXhfqxi_MeE2}*w}fUw#_X0It4*aCLUd;(%xOu-3T)J>TYPGKh`Xhg8g zIoZ-%Tc26Q8|h-rV88|ey^cW_k`vDI@dPG)3fl+n5h~R2&;`$D85Y7GC*UkJA!ood z>MPiezh!S^eH_(((Iko!Eg?#YK4pSrSP>ZX{n^<`FYLJy>DTe;$pL@bP@lFpRqY{3 z$%q0`?S<;t!GPbwL+zZ93@7x0068(>RL$)W{Xj^jfhleW8Uavs22;&^2*lE4W(mkE zo<+4pL<_&iJd6HH6h+O~5QvCPHM?6w5Z3121g{j%GtMN7Sh3p#DTw<}BM(ujW{?ua zqF_D)d`)mVCZ$>yl)Zmg;1u;2k`&W4uuc0gsKEpM`fT(oCGwK-^US>T|NU#r5fep| zEC|J!5KT?g(C2il@OaEv?8GuHJz&_^uQkR^5kff2<`lH*8!`NXeu#d!hJQFNq9|SS z-_A^plU)MED2a^}rvf{dO{aMZ5+^N~F0^H6gN|zS{FeFJ^yYu+sn~8j)k@pSj1+OT z4T3trjli35sW9{cOFUFBJ^OgpXRD>@8EnW)Ol2@)Jcx%N?=3`6AsP@sEl^sx8qR6f zQVm`*Zb2Key0)fXF-0G~VnTC_4&6?B32%4qv*MmMen@4Cr_?AdJHvzxJ6e3~J{2%K zr-%D5-W-P0STTR+MS1DMFy#Mk(zE%#3;9`ZFMJ$!gP05{PJ>=A;JwF+n!Bn$3Pn4r zhsLjQf`zzbJT~OJ_)ws`sp;XBRA`OWGW3mjO-PdN$FbUI>ZG^7(sH#azB(wb**?>8*u~KgJ9w%3_9tG}v3|6TPDwC3E zbgd*D?QL7>=$a=VwF1($l8AJzR!Ca3+kUXl$0W2?p?B<8YZ+gOnbLp)C_ z4W#0VW(9aAC%_!y)Sm*7`nN1SDQJq&lu998U99sp3VzWFk7G_Rv^ZH~OwAR*ObE(( zvV{_YF9_VdaPg^)jL0;T>e%HxDYv`?p%BR5JoJASET$(PG8@RQO+3_qGIR8|Kz1c3 zL|9F#>QCsQ+DhflCu*Qh&h>-ie@u_U?=l{$A;lz>6ej{Y)37Q%nx>9JvLjI2P1CS8 zk44~7qqg~aPSpO4hkUj`quMA;6Ok4Hl8# zp&(E^LXc2NI8L6G#`)~ghqL1!4&Po^YuT|sd-UP-`1Rqt{nNMC?_2m1D2f5mpr>n? zU%#dAO_ugiE&_su40`7JH^O5}7?GijbJu@-jl@l2aV|QRO(8+X;=oZ_r+$PYawR)! zg2+HlNKpL(tPC!is=ohQJ!q$mgxfKEhvvV9@@>6#F3aHYJlz*>S$fKt1Rl!OTo-cy zkzaci=ozOMG$CUWFNa;!T?H~w%@fXYlJgxx=V=&W6m=4nhJl3 z*a-Pww5$QC@7Zdz5hRM(G&|u8j9nDjUp^7jNWE%RE5%JNy6>yEy}y>OuvmoRa5W*U zs8JriKBM{xdYY$_PKmJq0$b_=Pr9N)Qz5S3fBo7jNVTkA(Hz}xel0NwV#B&-EYF)G99(yM4f4o zRfAZSfE^Fo(3+S{boP3HE(mw(4|wxr>NPk*g#oyFz5(U&1pZbsZ~_eP)9%x4&?eOs z%0wJisV&?Zl4C^D3(8pv&)N&jsj4Rw3nWYSDhlg)O6)R9rWgSZLOfwq3dMi(Slwc3 zso-))SU@$H2vL0=7G3^id+ZzWgkH-xzq~tq`_sFh_D{_f3y{nsrOHK@8QwC}+BL=i z^USW#>eey3YrZvr6LO%0jz~0%62+*obN${lI4SM`R8(_Y;3a3%68Gs0vYv!{+u_v~ z`WaIR@cdd@l#nF^#D+7(GLnCyJWCjkg<^nXcA4rwhGfLlnIe2jQ>+e|-mRPrWN|vl z6^RPPmRc~J#(nhfwwScHP&%MX#e(shxygkQ9!j#YAldyEiG+|0ZL7*DY5(e`vIbAnfnxppUSV~{Yt?yohBWQ?lZO#}Pm+Xb z3AB|a0BAJ9X`B$={nYydm|G4Od+ug7mblWIA@R@&z9||Xv(3*Q*U-zDN zKYTzW5d>Xd!#~p)U0<7iGr}p0U5~x4DKF!uzY6wDONIBGld}mYV#1QRkM_(Lk;7%< zed!so27TBoi}s2`(MR7b-+bA71tcnY_OMg#v{T%|&7=RDZv9&C-D#=k#H-%T(gs^w zmdTFK@wO`lcddVljdp{xq%qLaY~yg|45}`PmVp#z@RFf1k(x(WtaZn*L59WB7GlAT zP3vIOe*KcfM&i1rONxTvP)IyXsF=`nEGOiE@+cu}bjEWbP2cMITw~09bL(XcotJHw zM=Xi8PCbIIk9F6+0dvU&EKk8zR+k-V^Gp3g7$taY&Yyokf~Mny*xFX2qc)Xw+*JHa zdBNP%VFRRwn^yL=g^nb;Wa&C+6!B<8B3U;3)Ad5%f_}+3sj#F%?F&UL{QUt8mKI%_ zeE{B$Po64%_(x%l=EWtI(WImrQp`3@bE8~Sb7V=3F7Zq~P4-d-h_o7680kNJx8v^=cMi&_cy5gI-QSZ& zNgsEAM`WBCu2Q{qm%VXU7OsKynj$|uh>{#MX@$IPnkSNG2?-P>j`M_YFfej|c?SAU zrkQcdKzagJ2Ts+uUsb;{uBlF1$f#LrmiX=Q*&%-l&>MV3r}x$adLKgUNpoXGxR<+1;uhvpS7=^P%wFe>9jzeHvU}kQuKuYr{qoAS? zJE@Kd{I=lfs0IJ(uE-R@Ap$(I5KYnk>DkfC{e!dP(_b_}Qv|G370g@|lo!uWT;Xv# z&^&*E+m*h)1yp{=8A(fOzo%;cl1(ElZ6&am$7w`NMaC_MkQoWaH$BT;l)q2$mAXH% zx(&Cx_WBfXWUyfA$%KRYvRDlxeK;_D%DSS`nLJQe;lJf=i) z8iAFVwp6&lG*R&8(iF&XCLEkx51+d`L^r!*aJDH=&QXTXq{ zeig7jYO9LY9cFC}9VS>v8VQ1VG;#V@zfW@=tK(_s<+mv&lXC^X3{dg-+Nezj`ml%u zUmERm5`#&Zu{YYXf~J4a>>bvo2H$`6pLU;aw~Kz9*+aW1%*vJym4kAixCeV4&_FVd z$L7XRdwo4(No-2(-dlm4PkY|CKhf`q&!p+m0U&Aq|AncOybHaaxhaz24 zhaKtYM#;f0j@5fW;YK|bb46_pRK(I!RdPYJOih1p9wC^-I;!d%vSQ%BHCF&!*xQ!_ z4K&9|*;YxLj_zx4KTk+nv7$AODqEo4(2qax5YcyneQr+h3oP*q>Kt3b!a`rYHK8Xq zDs30l!W+9HeE#Q`G$Creh`WD%rYzP>KaL{C6~CY8X10QuU@!!oGV>HjSxhYVek9km z_33n~5hR(lKyZUM*1z$V^Ifz2%5$1xK7;Z6%TC@JGZZ6rQ^lfC^nhuie`k2F)G!^1 zFxI_;O{^VCBW=#qV-{*Y$I*G7tx|VwO^;C%t_+0ib8L{>g9JyDvipCT=6SpqZ(j@c z)vIOqad=gJ%!n<3f1Dm(5I!>u6BxNNX>=m6--ZP?!>ehsWlUjla*1ccav2vCR~)3_ z+*ZL`uhyYpNiGRY+M0fX+N5ma|8B^bRCFWrOGCr3hej!HiBbYyWVQIxwyT(riR>eI z1p6KahJa`%Vo%|%nRtIp;eh#=PS{w@FJa>`I2&ubR}>)qwhnL}QyC^;gzOa?P0;)Y zr?+otXf${J2k(WXD>ZxsLelV!P8G!r$d&p}L!Fv2dtc`VHsavx8P_y)pXOWHcvdy7 z+^6|gS}Ii2#(ZdOAvq=^dhUIQ2_Mej@0Mj?#_+%|HiOgb6)S&#dJ|>eysmGFs&8IB zxTl(|v|G>MA%iv`vKUSArroo);Pu?z)o-7`R`8HxzBoCgho{J-0g+GVeYt3 zPBPXDr}%=TA>-o?{A*j2Tp2?Q8wnK>h3fI-_ubyKldBm%f3Pkl>*azVsuW0;N2Lfp zO^%xb0EYVSEZ{2@HjS6IbtjBQBQV( zaM{KNZIpr-e{C)VASUcms67JK=&o1wBn5@v28~g7(;cSTI7gN9WTp_P?H>>sJshMj z;vk7hjk(n4-1zl9%#X1SNWEnkYO?#%pDR%?%&N|L10kQ8-zhl+p)yBb6j%L%N-%oc zPwRm=W6L^AIp6{5=@1)LI#3S6oFFKe?NREqHMJr!Op8#wLwT=Y6Tkl zdRVdMTiK9L%e>y;(x$I(@Cj@Q7w){qUFEx?P8d7aPp-xAOw?^@})N{ zCSH8We|DhX*x&)F|D`4L2hQo<8YY*bqr-`(9PZ6LrN8II#6d*oWEP^ss!%g!G0}eH z;MWD7W9mE=jsD&$#X7PfVGv6_`wM4N+h!R_g-8%rZ}MP5uF%f5p#mpZ2t+|;G$F}U zw_Xndh(qJ+5pCecXOBL-*&m!8p1%9x@Rw_&f1#~Jw3@BvZe@ZbX_b=hAW$V`H>;YE zk%mycIXON(+kbo3_d}FwzekNV%W^mnI*^s(N#b0`w~9%Yu-TNP^6zs{i{PmWj~Lbl z^lY4n2{JTLt46QT#(tJrG4rP0QZ6DUdU?lR8Qy&C%zrYPA~nB4^QZo)y(ewVC6E$( ze@i+yU!zmidKF`UVeBG=!KYb86W#Ns>tLEKT?H3e6df-4-P`t4J=Sc#xiF^w@aEUS z_?hT?@yrEM`sd37r;c4izm3daI!x02O=0Am0HEi}OiI00C)Dt<-`mLxI&R> z&^-TXxCbDN+VT-6&W5gdaQI<`t9Q8#e-h`sOiLehXgd8h#uE3@cP+<+L&bNx;yYuT z+bZ}oaAqTt@Lpe^yIMJyF8As}(r%Ey#R#EDj)0;(JDAkL!=% z+^5Pki%BzJ$Df4Vup9O?8|0ZoK+U#Mh88Z;*8AEx^V!&ebG(Jo28LB%-nBLRw<5Px zQ*ToOB!i|XOCZKAxsp0;6QRb}f1ZNDp4TEYJHePOwDj}Cf_FV0jY=Ftr7;ySLlT2B zwNRPo5RMfK3#HclZGk2fG~o%uv4R55w4kX7=dgGmQ5>slVJsotv%*kif3)x^)N04@ zYMK~dJpJacmMaXou@q2g>iKOtTF~;|Gk*BwI0nbG#5&PJohi<$41nL>e|x@^-xZ;l z^wA%IW8c9Ut6gwLs1v%o zAN5^mP+kYE+dC;&9B4nLr}E(N^ryqqcmI0x`kjB3s3(fSgmqnh+I`wb)v{{1!(qUeDJrSAX-%yTReX>ET(O_xVeRm}Ymk zz9oJjvr{ti`DzVlIt#p5D&A+{l8dkBWM+*`Kve^2QxHiZTslhGCE`TTn3$OGIuocKuu6oa5e2uVIfVLgj&Q;W zj%UR{d~h(Rt@5afSmEnietsHitzA>8=n_5$suz(M!k9x?f6}?}me&_7K4-!WY3^cWFBGpaeqC@*(*PBx)+$?>?TV0xmU{2B7SOP~Q;r6b-570Tmok1@!l(P$CP1j(A(t*b2X?1xWXCL?507*Mf3%f@J!zmO-cLyj2%}mM%gMd8P~?wq zHBfV5oH75C5=@wDu^l(g-hbXmA9G=18WisOSr*oL2!@&Q#9tm<#*Fpe{GVTMVsWa#SEF>34n0l?u(N+Ph*^_t8oWP76clp1W-ku@k1Y-5*+``sU*kx zF<2A-**L{pwULp(>Tr#Pd|y9@Dvnhaz}w@qLqFXX9jA;FqhT_aw9Z5Y54Nd#s^wWN zG(C#4JKUhG28eJpA;CzP_Vyvkt3lFD^-6NFe+JJ_>M1p~Id8@3)AkxzL2*#(POlQa zI#AUHfaASxIZgpUI5C5LL3v-77@lW}uU^swn!ct}>I@0RIn7=`*0diXN*z3Tw35wx znSi#VFqT2uTE;rwqJtv30X)Wy3G~*y1q}L!U-X^V#>9$j2kp9M?e-SBoX}`uog4*< ze`hJ4(nzn5I+9lTq~WP|r8dOg@HBy&h;iFhnkUH?O4(d@@M1^Q%?PO$TPT_^=0av$ z&7$j_@DQPmakh( zXFsMRhrBUkV^!1nF4vR>7u$^=0;43}IQ5)g)wAQ(!2kv571T!u3Cm-1P$}(oJdi5<{=$)WTUXwhgDQ;2G2MZg-IGqlbJpJ<0u=q+mO4t1`Zf)R{`lJ1`N)q08mi1X((2j@$PD` z`^~$EaQQChi4XMh^0Eri4DF)VQ4K^hHf{oxvmuk6i(aUH?Gi-G;@Z^DePbQ0NUUWO z%g`);iB{bmr^&2L1kAJ9`PXQIR77z^ro_Cq%V#hcN0}F3O%b%`y8V7GC+d-8q9y_P zI)cIK1muDo z8Iz4-OqDvCd=BV1CGn3uIW^YV6-**wD;!>bWt7j#YTwna9rrBXs(p}GOZ^u&z7&I< zpLQ%_BoVTRSy>)x6|?hbHF=6^wm>PE(4{CXgD-k^OHkdgwgoHF8{~p~FJU1C%X@7L!PEdNh38W-03K*SV{({Ad>Y%ngyr!|#aO?9w59%iR($(`%uX=n z#RQ8_C;A?7hUY78Yh6jbjt6p<5FL%WTm_i>6U?P2J~gEo=JHVI;T;&~zZDOHlLT!w zi;ZeOmH9BJ@H}BdJ1ld<_~)$T@S9bpT?EHnga9X-UUE!JnIP*jUs^HO zfqqCFomkWwa|IjEie@kIlqNIdKY!p-FuPPfl^Po?)55JSy?LCG)@Df%W*%{dcN{?Q zZV=gSK~84EU*tG}0>v?&YDZ32Wk3BK03MBZU4HZ|Ml4YYq!0HOo_sg`Krry+rNHF98og& zJn}MMUrwkb+ASiT3sh}AnKp8h=X+#TPY%Cor#^elaQqx6ih^>7E*FO!9G$G^#nA~m zIC^o4I8MjKli6j+PCQ5h2orFB3Kje4-|M|@sQ+)Tv$MUvg`lA43fv6UD+WSub2ilu zyw;l_p(&Z`5^7i{gD?z{eo*W5*+uBM`f)v5|44TPQqr%m)|#UUi0srZCGVso)ytdi*L*%~t!nnGACI(&5YT)o&|4N0NKaw?IKiD7jp!B8c|>(Pn6I|H+u7Epdu9=A zvinMQ`v+?IKrJ7r6V|$89$!LC8bex^ji-A%JKyMi3@%MG| zbI9JHLFkbjuvAEnDa503QBJ4$YH&_2waRi06^$UoLYcTsn2?@}%;kinKc)gpDn{UK z)kiPbTP6o4Er_X>;2}v|0(R$K14^sc>YMOga&VUQo1OZD2@ZXKr^%unXR80(G}Ivh zj2hJh2nfLtfra(osa89(#YD`BX!ofCUNaQ6U>D(%TRR(PQQk?Gt zkmgcrXF_=qB?_AThA(j8iuqPzMf*^DM@a z3?#>rjNR+pB=coS0=8MPjvZi3&g_nG=J(zrneNL0g;6Dcq%f*YqKZl(f{Tb^)o5a_ z-S&LDUc2CXeAV7R)yCDlk~XQ={{D8G>3z*vL_`t(uS7Y$DLPf67g5H#x;)Ngp=m@{Q=w)sZhIwHYMNe2#5g%&EYX6F?OpTXx9QbVO_a`&^G&x` zD5}8@*8L@aL^7wn(#kYkh?rwpE29xjL48>;E63_81TzjgLTy{44VlQMnVcCuA>L0V z-M8(FL;e#^Xl#ADz>HV0k!&k+(4Jr(x4+eSc;QBW)vk8dN<*NOA)HDY*j`IVPXn?= z{+-yazZ6y7)JaooX|E>utCC*tdiYqB_lu;mdfqoCvfW!y(<(u3q_F-~{eP6gR=JS} z3j1YKSoPAY%R%llc$b*UmyDn2A}{9%s&y5xr(fGO(W%K^O1_TdxsDowx5q3-{Z|Mb+I7KIjWNE zg;_Rg(rhftv+=6h!e%Zb0$hfyvkC}up{R&|J*CbX(yXHQB zR9Byl8gce|fQB;^O>jE4{$Mf@CQ{6Xc6Kqb0(C41lUC5#i<$&wCj6fZTN2j9$5=5q zvCA zjzpeX_R$JQAz07Z>w$u!QGehftQIL2+*{GNjPt_zG)LPl$I_io4P1>)ZCxJvMY3## zdIL=xPQO=7TqF}4|Hyn)_5R@ntNIQFfx_&w2!|1uCL=dSL{9ykFG84sf$lnH>xWMAD754oVhUIrohh{A3TnB}o@1el=Ez(ji%r(7t z=Ty@@lWO|Ue_226?shwR|9`Ga`G!98pZZQ-7=NSKFMrZiUg`SXYg~+-hTFGyw^jPn z^Ff~+;djILn$2HPJoeU$h&XvI#QL_bq=F%us(Vxr z0c=~O|H~yvLLql>AzCnQmI;;8#;tdQ*;0gKq6YAWrc_}ew9yA+=3+H4+PQemawp>` zdAw=xiP|Jv2*)5q(=t|pQleG~^VybB>rdH*4Z+Tn85(jNIZ?rSlQS_m0fUolF(nB0 zdNtt#_qOkonK3PY^(1X8uKkxNX|s9ji(AT;te|~*`uGm_k?rTR-bXsws8$zxX+e)h zZ<##dM37V(!ve?>HS1f$L=nU+Iw#zS0Vy-pGA5{mduTpvK*pJ|8ju*oT%SevwW2U* zfnv(7+{1PGa9!S#WZFISa9#fOuFDnI{!6?r$pz8Ax%NeWr!R*lWkeo)+-cS0~c&Eo8(Nd8w=q32k@4m&innuaPmG*F@-TDaAecB z*_*N;3}7%fGGQ*K8B0MW&PMP7z@v>4tW5y#M{PC%Y{5X17^=@DA~Fh{g1!WzfDkhx z(sk*K+YJoW3QPxdtlSnt7}~q^g_abh zM^=e{`kgPzTC(43vYcza4(>kzR0HZ3-(sH~bxk&K-!!AHx4jT|x685i=fvLq?mO#b zSTG!3h?-?j`0eiM3c-(Hqh&|>gvg_hxXcV($~6`A*4 zES^f8J(xXxK~Le*!aUr#U;K^xrOG8s8|nX=V0!N_FLu7k@_u^J^V%AEb2Jzn9lw2d zaQyb{^!Rma3TT{jHd+4SgGHsWaVEB2ymd9VKZoP*+8TO0rs-7+0{ixBF79)x=;I52 zk|k_5)z_K67okO~5_3&0v<-b!j$fv>a5)~CnnDH(0uJhmu#qd-w6%Gj?u#FVdMo$3 zyG79}&hku^J%O@OxxigPH|gaqOHUb-UUA0h1x?7f>?o_&;y6uas`5*k5WQjj8lW*a zv5gN!gmseSlDn-(cG$Oo|24!sxi6ZRz(aFVQ6N?><5Nf`qPUG? zBfC##(DfGhR0^k=F+&80Of@?-p!&&|@MGc_V^FqRXU|KbmGW6;?-I$-m`Ke%DrT;8 zp+A&4%3CvyfU963>$s3#jooS2RcqrvpKz)JVn8Zq9RSfwTS`+8jJkYHZmw8|@r9;E z66ls6QW}PT+hEdvsi4p{w)1*_O>AesqEmI6s--iP+WL!mdx%EjR;J~`=9?s=TP}h* zj$?$;d&4ci2N3#9sLM(qj0Q)q{=XlO&LFM{K!iAZHrgN6T&)tI_iaA!C-8GGis=f+T%(iFslS0ZGhKiLYRF z95yw4b+zdJ6a(z9kE`g+-ya)9XckQ02Hq=FZ>K4yDO_~u65AZ*l9>eM`fgBF0#ya{ zn&hUZZux^3vv_R{S-TQ;LHH%7(gxkK`8mqO z*X;?zB3Uj2w&}moI@DUkVsle$ z!JGFI5&$Sh#!qa2(kj@{xndy_HX0=im$>HnY&1o^myAb{M^Na+sCPXSEq&su4GI?u4|GY6Rj*a{|W|!gQG=!4QT7Skth7g2w_si!u6;1L+|L(!J+E z(#(W^$}KJ}goortD;#U zZ<}Xn8*P}IK}RcWzh)6m+Qt;g#6vmLl*pbH(^U6g4yC`Q3xad?Lel>PJ%}CGy`?o; z!icF82E15*thS}T677oJiW#%nyc{c5$Bb!qEc3ZA^Kis`OytS@hm14HB9`>g*};h` zt+H(mPwP{nD%yu|Nj&Q}nH1J^kcA$CXTI)rAMXXPd)+6G zA$L6*x!Mvtnq2m-!$oaZfXa4J4=uM zJIf1ysrdM9)Is;M_}_fawf~ITzCZQK-=8teUO~Xs1JF9QO|W#=m5uXUaK{;o;U;q7DdQ(7)}c>Pt7RM1_UOzF9cwOp{q1ck}Z_7x$fZEi>RBCAaV;u6UJ;d zN2l*{eG?ucv@y=M6|cVk^FYl3r!nS=dAD$0{cyOYKBE6b#~DdMQn2a2R@drg9^B9s zXnXJG_D?l!bXKcYZO)?kTZYnDB^iiAp=w!qO2u44q=bz{ zh>j$}LgZ7T{nH?~ErxFri-~>gk{HgQrv%M7%Lq?qVaxVdYv?ea@6A@Psw@j#WZNh! zNrHu>Q5dm~Y9Kncsir4J_1Q;H!rtR>+eq=%sDI>1AGPgY{k`t)Zo5&FZSLTII*!=8 z7UwR-(v?`c5CeCw!Y2HCCl?&FHzd74Lxt3Ucolq0nZgTi&%Oz3j9Xicmzd!u>UoU1 zd#aXG{3O$M`xNqJv0P@k{3H|pxR#GiZCb^Qf0-th^-9=yOw(~_6iPH5X>EIk9q)*LEjfV*{hpJYgaT4dw~D3l?3LB2fwWAbkGfADYus8G zAx*Q~rm_o+Kws+xQ9~}ZC}D!UBW%=1k3Jl{J{}zQfm%2Y8Ow?AxX<8LIQS|oMlqr! zi9L{3lY!d5jkrq|2FvlzSjKq$=~_jKz{inb7rrHArilt%&W; zT)~i7u|j7r2cE@5i_4Lhw?+L`8}0ZWtVK$Dip0ce%9hwPOFZtQUbq|fp0<#Nr73p1 z*7(Z`PELt%0<-Ns@O7rvD$C#BROM{s7msi*84?jrjD1o}tdL8GIb6eq1;ptRIlaW}Ka)g0Cjr@$Y(7SRS23N|2u3?gIPrV)z(Y;SmR0NfZjYKkl+@6AKN$7H z(TO2c6*_^?m}ikIdTbvM6}?`Q>ILmoiLe&G?B=I;p zL7BRT_(efGw952u3k0Hszzzg3A6V3P)=ZZ)9S3rv7Eui;&_fDnRyX4X>b1rD_UObR z^>wMZY1NKS7HGP)D$l8Zw)LMOI#j0|2qk13v9^aAc18O)2h-DAIlx}oV(6YO*8c75 z!EBi$65)_Y9G2G)o4FIL|T{HA79NNiKPdz$j^_y68%efSU{ zI;vGfX@dF#nW;B|seh-A>W1Rz7d9wteKyes`F#oW_RBl|+M8;Gt=3no{?O0+r*Dtm zzCtUv_^S1lLTuU8Ku^KCUzGz(3m0paqEu%agc> z0ZfYosq8XEHg|lCz+V0az#oBmJD2Fq{^0EJ^xY4KzdU=i0e@!QB0*ej$6!LP(9ZVe z%}4$S!3*HghXVNZEs#(T{GV@~V+bAJ(S{*BSJOqmM0j3ekz;ei*NjQt1 zEWsLAo7XDZ($3?m=qMdAWQ78fxp5g#2^)X(wbWRuxle$HXMFzuQTcyL#f-1Y^1FKD zgbi_0|0=L`vVGEV+AP3-txTOD0wQCgoH;iYa&W&)vDP$w6JpCtL1rW%siL>o;( zqi{T>se!hjLX0K;fy_1+K=QFQW(*wV!HPW`N12hl>_-vHQ#rScw}oTGyajrjHU*How+aV1FIeJn&J1A<9FPb52rO#G$a>7Wx9! zm#G0`xc3l1S6F(Jfg)n5q^S-#))y>oCUK_BfMI~&{H*xcB* zHr!}qn-gPWJK1<+TN@`E+qP}|-aOBJb?erx``4(ZtGa5sXXbp)_cV4YQ)idtK6U7D z#T?s@m9$F~jsr@u0YdKil1(T%b_SZs6%v0oPae3limKHkl){5&4;*L_d2;N;@f0Jo zPS51!-#y#Qu{7_R%;MEiya}KrUcP|x`=QyzEpan!pjWy8+G>>)>^zjz50yc1)z zlenA5$KA=t%Z=bplZA{8JwPe%$^CJ03I@N$j)F$c5Z3`&lJ8$)AzWwEccSBI8M7=t zu60{UIjZzt;gEhdoILo?>22)IaeaY8Ir3+Ixggu_k&x4$)0P3S&H0&MPlKlP75_e6 z41I!pK!U|DbLk!DKoZ@V zPA#W;BUf5qI$sznkyEFO4HG%0-iF8dLT4iZVa|t(i*mgWn0N9(yX9rkyaDJ3&T{h> zqGeeOLk0JE)#71l8}36sm78Qkd2UDJm(Oq&pi{KOrWsH%J z_)AXg^%^cvgy_q})W=9XJ4@Yy1ZoSiPbi}vzYuexEhb#>ih|b>mZQ}OIA11FNf=J> zBn)*ipMq}{9Rmqj7D>a)U@Bz>Ydd#yTo`!*TcQ*X|DZ;#zPEnq+7*k zhHlHTu7ue4LdOPURrAknrdqME4T^wfiOeQL#6bE-5fG_OmlmyU#Z^s;-k5s-Si#@b z4SK(^&!lWocYI{LS<$sp0W|>5`|SHI>n$rW7BnW}?QUSwV>}{yIs{HUC|*(f%TOpXCG(CM$!LeX-7mJ|e7q!X`%; z1*HW?7ph16k`h++ogh(h>*jBbJZm_U9^(-e*OdFD*E-Gi2KV;-PqH@Ik)r*sDL?Y1 zlz9%G5Rp8Z9~7o!xWA_W5JCCRuWz=99~Pns6@9KE!U8E@QGXF37-R4MN}RuFUIAC4 z-w%Q(Qi8aZo~Pb0{7qOPQ>o%-p@GNYED*91cN@tgzGZH?gWtx}W>Ic5ocqKAk72re zXP0d=YVs1;)O*M^@XF9F`8w(T3l5b@G@Z)*%%t}HaKp{LS*sQV<Qe!WUs-18(CFf&V4V!$kBl>wds*8)UUkW=&IXKjJXC{un*6Bo z`Q}o~P$X!f@Dt$ZH^9)pc=oE}7Jre2vIo0TX_yxM19pfQQh{R+TgGHz$Bu3rT3Hak zug`%>13vr4lr$}yC4;(O){{BSB<6not9*I*VR!APU4_HzdOO?x-QY2*@dMy+4AxS+ zKpdG2tIfx8j?f@=tUoc@$7Q1)Hm@?Tnmow3tb9&kI~Iug4&7vyELK-1Q-K^gwrg>$ z6Pds}vs!$Qja5!10HZ(6_%Af_%Nj%WiVcJ%M~ucE-LFBoG9UNoC$_@riAfku7yyJP9${&S{=c3w;P1=M6TU$0e%Ndoa%YL9qJv>WlI zCntPr@r8ek8afGTTu>zoTtkFvhR0V`e~m?i7I|^p^cwsr890BCa33(#P8l6n3Nd(H z^$*2Vvy8R71rn|!fa^2mg$T9KHc%gRZ+2<%AN$}mt+<@XC*ISPo@HeX-Q73++yC|u z(^dU*?q}YM5cRJ#mV@Cud2!Akk@Ed{Xc*%pVO&;ygefzsYK~Z3k4bPTdZ?xXGA=oZ zW{F&l*6;?)g-#d7)=z+q?Kf=CR&^1^vijaADQb#qyl*teWf;tyR?Nhztw93?w1m`J z1mWe~7RU&{k;_>Oji&iHf^_w)_ty{cjy5bvu+KG@;tgmbAAc$gV7VPbT$Rs1R9|Tf zpD=R2jXSPSbztw0AO6sIW3axPo>D)&bLeAlUGb|pwOHou+|B{?iXt^m0ghlX?af4f zRzWOd2^+0UM$vmcoW;dTtMr8^v5h8;3>@(9f<9j-n~e1BzJ;#3c43~nf`9gfRsWva z#-`hXDC3*$($grx6T2-BborxKy=Jr64bhdi+S~?Ixx2(sw4?%_J4$7|46Imd6oCu%?G-#L+cIkehR{+&aw_2WcBga3s%pt^ms0 zAhk=2JJO51al+}su8mi4gFLXOF3o$Oo1rz#c+294?#Mztzg1m=k}BiQhynk-*{2z4 zMH^2G)l3N|uveW50}oz6IV2%SY=R1**+I$%er)`YG!vNAJ{)3KX|`-L1Z7TD7vW3f-HORx4~%7cCJaG zGY@k_zz%wn>w#33U-QRG(K*zY`@Kg`Wxuv$g$97NX9t#z{{BLXbz_t-1B;ITN7gU- zY0_?2p3Nn0yG9rT*I__&m0iAUPJX<33Q1&B=Wo{~GA)oc_uuAz6W`NU^;V9w ze@+{gobi_zPC=ID%bgRSQq)V|vx`_MnQnCEidV_}rNv=K=n7w}ey=LTT(5MceNAUJ zi5?(O*ecL9^RrP{=z^^(d{mWTA0{|umvNJ*oXX?z*9!WcQiLJk^^#EaLyXbiu#|Pf z9Qn`GSC&ecVn3?;W@9|MG+pqs1WV>L1uRm=?tJSZ-74pDN3FKj?rUdWE8NBx)s2(# zEv*1uUzbSXXCF0aqnDlr1cI8VKMTH#@a6!4pk`?}cOAj+k=)fl-Iiaq^DJ;QZ*vY7 z8gp38l@maCmzO<3G4*3Q+6?=vjw#FgIp&t! z{?uK?qWNy!(Omesi;Zg_m^8NF{mvLJZf-1IrJ6Wb!Z1*8l|*R#ZJM5b$UgKhZ7!fb z;61e)?mO2v2bI(@In=e&6<9FyFM>}9>l^|U@hU@f7vBy|sreEPMH5=yZLPhC)nWqA zKK%Vw@maqdY=jpdO-qt1Pl*xnj`RI+oO%8yDFx4@?D547^+BnzYrycNJ99HXt+e3P z9y3b$bs84>sV&;?U30XO{Ey|YOGk9TLcGWKWHsgdb>oM7R8M$j-<|0A_Q&=nNk1ID zKLYLM0ukiD0`bhk(wF`Bwwr*d5^!On*a=Kh9!z8!6P46ve)5&6eFGF}+Na_Je z<=aSpde>W8McMkTF>%#!WD{?$QO$)A+}(UWI4aLy9~ESexySPLy4}$Z>geG>5=5}| zEA`GYL+fD&$~ahgYOR-X!_BQ3bj<)`z1f8+$osV!jezNVu2cN7pxd&eLC8s@gU7@D zI7*MVD#hvWwXKC`_5%dKb6l)?`ceI>*>w1l(I-_Df93QpwfFxVa>t{k^qw{J@B z?(X~)%Mj<9zAGGIQO|e2oNJ5fv_%XR`r*%#`FZn0b(3F>yr40n4^EJQ#FzdD^dbYC zboseCU7ny!UG}*n?0f9ac?Rr1+L?6KEV)X5ZW->oGFF_8B17uivmaEHrcof0Yg^8C z=;fW7lc~?Bf@W{&i^8mxZ(dxHTq{tA+I|84F|^4R?IRY|OnT!tT`5gv$4`T}3wB)$ z)Z7CXI8i&T+l)h(K8dRU>0@Opj7jZE3@5R(Atw3ZkUC0d@rHu3n}6b0r3wmtw10YG z`)CP=@ngIZB4}|X2H>VBzb#Wn2Qpp6)v}NXf5`2lF5xJ^lLQIvQI#ajt-^bPFRXqQ z2mVgY8Q!aO_E?l3p#-NFy9vxT;6n4U-7_9A9+cRaH-oRycwy!M>2Rdm&J1wjSS#7? zZAL78vy@wu(eNnq<@t_p_ThURI)A4XT%kv+zZbR9R*f>Au%_=P?nl-uybfryB&W1t z)kGD#k@wtl>eSkWz%t4&A07}gMF<=~5={pq!KXA^>=3vzki_#h>L z3`KtNtu!tiS^upK#7JnTZ9K}#+czQ1pO9EXAB4K1EE&!dE=lT5@{GWlZZYU>;^cNG zV4XzR*>y;1IX!-#A)e*V=jV6?r%iu*#P?=QfUxL=nAlb%5z4|J3>p#PxAXl4W|vU3`^=Q#G@{UKY_5(*-wNt-Bn`pG((6!718llzopNcQ%%S+~F36ox`8(TO_Q9i`({!@y(nbxiS&m=pi zlWjgyJre!!5P>yw#%f7ojCVE@dd#GjF2@@|_~5=Y z2YnpJF!T#W_2j@wLGdrd1}VhChaah)1ba+z%iME-e#`-Z+?mnst{xlOHl+%VNx>Ne zpDtyCKLR$k!jSXiUNZec)T=Y>SKlY!IYZ~SO|SG#+ZSy@Q1j|~)gWm7LU3clI~?>% z>xa061D&0|LM09AX=RRFYKjpS&O=8ZE4 zARN+ES4}Z?eSseJG<6bK?}#-!>~2;Q0Ck>U?1tTfX#M!?lfTW{O)H&e6o_&*{aMH& zKXO(&+isq*?|IIh;lO314%y>DBv;(gMfa!rymx&y^BYKsI1yC~3%nqa`cvs42noOH z28rd7B;=g@IvyE-C(l7WAKe19Yso%1IUU8gyZO1QaQAcL#gj?{xzn!@{)p5JOaC*hH?nMu z<1({J+Gn)*E~lDRn?$W(VbHII#~1#b5ituX^h68LPXoSK3XX2y0Pw$TAD52yB`+VF ztG;m{-vmR653QR!t;i1;^ZfY4Z#5bhk|mMnu4-)h@URBd^Ihi?_pZuGOG~PcZ%YQk zY65zp&RwXv8nd-4&tY;xHI5GUvXCj)K<7r{$*XHcc)og8OKw_VZauz!xLlk zfT^llR|M53DQuSlkb}12E5kRQHJHGpW3kPhW3Vw<60Jtd{K-I7YWehMVP77lMnEZt zczn7zjK!9mw~F6!gx~jlxN` zh#wqaGB^mp<4F%*rv-nOXsEMIdPQ|yhFcDdx`zOZ-UkIE=|d_HC7me$#y~tXZV(5* z!@A)^Na8rEDVYu0BpSFJsg5rX1@VCBP1(ELlLB!M;aRkH`)8%ckqx_K(B`uD6bMK# z)QI!Wb*b&J{fL^IR_t^FL;9ZO-pAb-GlN#%m<}*6Dx0!oj~|=I0#`upUdg=>;yehkrEHMv=Me68z-mMhtggQA z?IwvZ*zG_(Cae8ktxtnVAk2_g2*AV|YW6;*nLI$KiMD4r>y>@^{UAkQ(2C z1F!>$=9sar4%VR#TC6+)xqV{DBM{9q<1b?~t#uT`d)9NXgQn%&bhEicvb;2HffYgw zEpLMReu)6U4t?C{tRs1^K$Iu-5wSuMa_$;vhA0W;~%uZP9|| z&Q;K(oBQDz$ldL~?av1r-r}^&-T>oL88LmgCFF$_yG#MiFSGCs?c5a6Hyf{aNUz44 zKAb5Arj&HX_dgjP`VPtWu>8@!mxe|kX~WOo30Zn?Q1&hFq!{I+P^nf5(&>r*4E96Q zpB!|ab%kXawom#!45Ki3r7G3woh<@|ZC`$yS)8c44RE6TsM_oXI?^a+UUC)JMr zh!v017jJd}8A^gk+}kx?HhpjYf`E)%UWvWOqBbJlQ8=p-ifK(6Q(N0gOqP#V-@;+v z_t&ldK-ToTOS@n+QygG$D4Ddxy5{#G`jT>o+hcAaZ2SxPTyLp->W4D||$X zae_%|EJJxk&H=r<-gAQ`Y(z`8*gKureEDp3cvudQ(F_uviZ zPYDL`Mz!LoT6Z{y29E^u7wIPp?wIyBRb>cUR>%EarzQ6o)E>FAqYgd3de_Scao=w5 z0D3(}4mjpX)8?wfZ(~%Bc~j+~+Ke)X?a=J~Gfx@`abV$l)`J3KM}vP3ax+)!m7ec& zgEiqw|yT&HSfe&&N;nvVv<6-3?VpJRfuV*adj!lUtR%3W=DpMBuy;52bTPUlGgn~yDve! zQz2yhUY;@UU^}QHP6yFqh+(6`eKHj;i>+rcGSAH6Asm#YWdD%9&)|4~x6Phf-98?x zk%R(no2_R>efL*PGS!Ydx?ykfB5b9WTR|bcT7rT@7Dn4xC4aLx1?V=jUs?!i@C{M1 zKF+b7EGB)Yo(Dvy6Of7pVSHwUIqpgCdZ-HR1j`Mo7d@Hg^m3i(G!dso^E^V|mO@y< zYfyZDv{r$@rxMz_mOdZ?%}DQq#OIyI>sx`NKVtbAm}!Y~C(y2O=!ifX1x_FksPOX6 z>)^AHdkb|Khr+)#J9~PcBlkv}x5X@0BPYzU|Jyof0(3{@v5s>7JGa?=7m^~4?llz6 zpPQ^dXFumP?ZsCB_6jBhCwL>vr}h~zjhe!IdmvcC{N>N|25A$t2>@SudGDSqf&mZs z+}r1GI5ZJ{ALfU=Y59jfRyq8zq69IQBCf+`F%~Z86Mtj1TEm)1D4RI3-z-O%k`asx z^)D|>J5LSCt3OpJ6ZMslq7D*vI!e8jVJ0GNdrH4e&AKZ z2=!o1E3AK|6Hn&SL?`?L#<&VyWzYNL*E@8$QU~AUIK+4`biEFdS?5F>+8rQ|}4>Bjt~G%>EY!G|x&_tzV0z3NEG zCJt}~L(`1vt6;f=zDOqSY6Ss(Bg;pNk=#MoraWy=mQSZNSMOdG1A`(znU3@$bs*GM z@6Hkb7)X8X=>6CcR<&8~Syyu43{-oTlcjF#jlzo8qT*XtWrPS+OOSz__gBfn@mC2f zLdIp67|s?NV)4)ETgRMfN`jnGY_iqxmzc!TBXYn#GU(ADX5WlpR51a7yt9U`|I-l zp1+U&%x2Fka}?~xC|lP*2Vvr_Q)rSEokbx=V6+-yJ_~P< zz|yYI#)?rne_x_+RoWnaA>eL#)Dk{#fdv4v^m{kiq6TluN8y?1 zY^Gns`)x!^JH*ruZxH#I=M(geV{kTHZMQ#TElfgPz|xUYJxsMg!sn1d`qxMDu0@x{ z77^5@Ms}@2rpzskiFHeM&~Nm|!8Suaq6=H-iI=j<>(fk~)~i!N;OQiBYU(mU2#>vS01N(<&2d zE=q?#e&_d7$V#c6OO#x~8iC$1U)e4DETBa=G`;9Ai@|yvPSSqkHcP$>O3E?6&#)3* z7S{eQN(p{^=KRzf`Mz4Ei-ybWnL{@0c~iJKE~fUQB=Z2!=LePeny7bG%j@bt6A(4(TvrouS%FvPxQ%|4=^RKmWr z%CIcu{hYSzpb$~6;Ts!Ij$Q{Jfj$z59WO=^mIgwdUh;cV&79dslzr}T zqHpmsg&8pB=i|!yVZ?$@tWStc9h4e$eYJDW(|a*qb8#F}hkt^`LGm?xG`)$b9Z2An z-EwN%N?TSH!oclF7;FqKbBdUk&hTl?dVix zV$VVu+%=q#lxiM8aypqmK|)(JjHN~YI*;2I>;sGvCZ|&n|62=~EHO6gcXG?%Bb=#* z(L+dU)Mm0|R~uSRSWcu#mZWK>jx(iH|2LXUy`-;3Xw+QB@y~emM%UFScpYJ{vS4hs zzF>O*C20G2Yk!1=f&^WH=`n$`1HP8?t_tm;6Z)<u8X9Glybj}F`l|cW~>aQC~ zlrCTS;N~ey*~UvvDd@l(r3|ll!9gRp64vkjf|(@B6gGpRU;}7Ha*le_>hs}rF6}^r zTH3W?n%2C`x8g6v7vV0O7@O^eca&h=EqCv6!Al4)?B9;yYm>EKY>B=fBhWQyxGef! zBsy%7M+$F;23@I|^I@NwqC9fEH{FAapqr2Wl_1O2X1m@`92y(zZ>;G?GI=fbVrU-p zB!SfZ_!C=jt&w|^KJnZT57w2+6@LZ5tO)a}YIJFrVE7U_KzEuah$G zC&#+alx%LV?fsI)d<@pUVq>yEHplpF9;Yj(O{ zF-W)D%WDN;-u7T-{Bi^6f=mEbHdlg2L7h?BfD5FlC&?eB_ z+5$rQG&XOEq;>Ngzc|qim>24u<m4D^!$u-o<9GrCS$%u5Sw81OOF!#6 zrpV&RTGQ@jYh`R0OfgbH1AGPxc*&VJjnidLra2OZU*_Y6v2yDUXL^wp1XpQOO8C5( zrLpM^a!is@WK_H zVmN`MdwH~8J6lGzU~oXOuQ=RPN^=mC%yu(_L!#eb_glaI%F3=v>^a)?CAqh_C`_e5 zexp>wcCr2m#93&-Jsn5LOZOEzRi)j#tfh}!)Bl-x>cCJ1-RlT|bc@aZ> zjQEV$cO`Oo!nPD~w!fBet&Q)i3UT<4Z9#2vjykFBe@yA=yV1YPl(4(DMb^;YwM4>g z(I-ZR8Z?og6Gs9ZToM|wk_CL$IgvkwE4;t{!?ha6m}ZCzH51PeDv!#NmTRutp^wKJ zNgBKf1TiGw($e~ztBU=I*L1=?Jgt|Damgr?iEY@>LGK6qDL_lkwycxT5B0~$68$CM z@}*vb)+hnf&TK@BTy6(X!`5X){!l_m8o}4>Mw~orVWb#vt$V(i;MAqb{5&xGBH-Mh z$o1Q`ZY1+{VdTJ%XSvWjanf^urYC<6E4RhPn%QE*AZ|>Y(^;e~_4|;{@36X@TH&Lq zjKf4~Y2@wZV#YqO5=-|2If&~<*9bk#lzQ`F&I~Rq=_%d%!R$8T2r-h(k)qU)94T~5 zA2XPkzzv{D^46OHYQMDXNRo11e=>C#$x0!YDCBzKmhK<;Lfl;*9?|Y{ldbd&YJm3+vkg5I01m5ISwsMl0pV^ z5s-|t4FQ|FTx++zdl_U2Af+~rPuK?`Z7#5Fs-PB8x)wh)McqxLzifHEr zVxI=bi4`96^XssbmkZFf)TpaF(ILZz+J-{R7gr#zb~tND5-F!BytsUpHN_|_4U(!p zo6$slB#5TiWsOBiJw!0~^>YQ16mZTNXXwJJTQe^Z*$VRV6vb5>zVwZn(RY$z@*|if zcOb=WX&>u;{phZmacxMhdQz*fOSLjF?BKlcdC!5)T$cWy)K{c49e8oXVdreheAXkb z_#;M2bVkyh$sBq0$lR-ZCPPf>lZrlEAk{Qmc-r_xQ8csq_Th$}E4jMgC`Yz6!;bu-w+Xa$d=ilutAfnTA^{V<6@IRZJV(NE;oZIgosv_Af;6u;1Yfc;SNVP=WX`2+4<#8ptVu9yM9XF@RF%$3JZ- zads`CvK*IPPb`z)aPNvfpDCVa$sz_^*^$=%mw)MewS}!_@+|dbz-&1r+_Ga-`c#-! zYTYA`TF6OrPCLm<(cE`?^I0(uiQGwInPIx4m=$AC!b*0dX2W*k5;P-EqC%@c1F-jQo_`UHbl`8Y47}O`Tr<688jl>L7AU`ykBT`30k2-pvD{wc5@I4aoter(Ea&(qddMiKfa&@F% zD5?l$B!(LmV;6_&ZoKqg;^CqH?~PT9SjDX7158axLibH4{D2-7p4|YyR!Vch%DGHi@+Pj=Sz?ImMBtDAsg!luu zB?VdJFdvd{J?5!+XoF+lx&xkXSj3hxnk0htVK7RN(Wc`jx0S$ zu$C1;bOwcS&rKg#pm64MNEod+om_GOUhY^nbM&~#_lg(v@U&v$(?!I z)&cWM)F~YU9@r>4n99-F%*rXJc=PN(9U5CFwq*--d!6PoA!O`0V05e|LZ-wXfMJfh zcHS5J1J+Z_)+=>L^K z^<&wI>g)^U7haS;(!r0AGkS&hCw7{QO}>j+&5msLX54T|+7x#Kctus2M5c;W0^F?;iE%Jzw-AU5 zI(mil_iJ3#(((rOM*V|_ zE=D-c2~{Rf^?=fp&!XDqdV}$TGcel7lt(Zdyg~_vNZ)Wsqk@<44(E>r_3USBun?Cn zn8urH`A+wlWP-v6U*B*CvFR=nw*Ds(g)b!3xA`wgY^@yeP4vvpTCbwMPXARbRhi7M zW)Zbk%w##GQ)M+O9jjjn3nc|UV&AWI@L!nv@3h61su7s!EB^zwcV%Sbj^7SwJLkz7h z8ncxOW3#f81eLuw6>G&ZazMo+I;BlOVsv1cvmck2#WjHKE==S5ch@%6yjD6j6TVso z=OkTge0gMiUJ=>7x?-G>b6#Mt-LFES<{oX--Eivy=Z6t=Ecmw}N|*j+5$~I_GND0; z@ic8t#DYRN?j5uq(~)f6e(-W1)_y3VJ|gInrSTsH&<5*yMN`>SB)}>Hqp$jfAWlo^ zRgk|<7&I@;`cIhLo~l|t>T&BD>`c>rY|SYSG;=V5)RBeN_J-z1ovw|5i{3DvA+Aa- zqkQgNYn%106{cMqJH4J8{SbS2=LOl|Ppz)!zEsm~fN3{;-N)T~N8I-y@6s}?Yelkj ztg#G+Swn*`_&!4y1n7;sV9t#7R)yL1W_ZabU)hejT2tt1=&y_&ctXzyzIKy;BN)=< zJq?H?)gvMsVUV24y@Fzldk)QCQdQ@ZnEVY1`J*eV!m0zB0G<7})KzR2c&^y%i3MKE zoamM2owHfJFik*MG5E_{qJqcx!KF$@7kzdu)MDGWs?}*iB>-H6xU>9kQ<`)IhifIC zFceR?xC`9aY6kUVw&dce>w^cr>YvtEhIWgAEx8I!s_dj_NQz8&grw$wp%F6#u<#C5 z5u$DujBE=QeCo?QNvrW-1t9|_6*m(#Fm0lUgI|)#7QeCXB`*Zez})rIy5IB76Hs?R zTMaU|!X}t%a|1FvM2{2{lL(T@L@f#?vEC-&}t>C zrAX4GhWE4JM@Lnd_=u@!d&_ovgEWZ-HN%efKClUpSGuJSGju9)Tkc{8)-Dzq8i3|B!blTyV%N?(MDn-d^9_{T`d$X|9xeF0k(11iB+x zZU6S_J^2CNWM4WtQ%{)}0;=tF)3rM6ilb4Y*e*MQ@C>p>Y<1b3-_Np{aC@#^8Y)p_z8r5kI*@^fd;^X{tCHmC}PR`3= zxt3N&ra81+y0rSWT}YNvx5_<9y8kDZlGJtGw}-=n#o}DqQ{aqCQw0XI0_5H9t(5#J z;2&@JwSR05^ex_ex8UjBB@7^IbOwlD898h0n`ModvH-oZjVl?paN7a^txnM}^VR7Qg9{UQyNL zqfS*Z8@7UhN%*B*|E_TiSE68JO7+fAP^N83IegAL+M(@WX>q@xJ#}K}CkFf-)u)^E z+@ax+Xe0VvWMV1BNC@jl(S{EFNtwNbq@YM47*P6+&}q{LvADr1z2Y?2;%vjA?V8TM za+I!34;_Zft-`yxFeKz2T*bWnGP6Z#F!DTHm8V{}~2 zWyrk~P`lD&)1y1qJ7{EMwxsd{z={3Crz;}-Qu;AQnCj5@X2$va)kgV#)V;{$BB^CXl!My@!831Uk_=B(ETO-i#bxygZy9+L|5y z7D$7W!oGhXh1~9&O+MhfKdPNgzPK<+gJ$lTUlHk5WnL+x94_d~qv7lk-xj~!i+O-i z$8*_1gKMn(!T&J&B2aUa`nMOAL8frVkV$|BKiEa@Z^0QZFs=KFwzq4klN+N3W%4QB z*WkJiP=XP^qbx1^EqFiZU;ac4cBY4Nj)WV(@`uagjC3pBytng@?U~>LwU2@XkJtHO z5Kj>x>iS(KMSj5b@4{gG7 zuT%wivo7uv!$R$FebC446v9KlaGouW&CjG2v;}woz8+r;n9k{O1AExAq@ZmNE+pC= z37ch@n+oF!r&(R%+AfZ{L-X)e_n1*%c2Ozpi?0_kuA#eMgwP zKuO{Q|FX|%R*7mFHI(gtR+MBysEtw|Ho|!|a9BxcWluVMfj9Nk4(CZt_dG}>m{jBm zi=?}q?USMA13& zH<*c8|2%55vKZLr=wzF`>yRQIv!K@_EIHAn`5sI8TA=P{EytZvFjDTwudc@~VE$tW zfTUlal{4m4u8hWVuR5dc${?wdJx7Kd0ZI_}W*}kRqjl~m-T^iv3VnWHAi_8`j zE~R2kMx3UV&>yasz9!V}srbj^1dbb=0V(U=mT1xxEi&N!?R1z{QfD=zvZi+Dg6lM+ zAx*wL8XX3+`Rw1UJAHOb6M@bqXp)aW$@xllx!>a-S8^aesPEu@^p(Uh=;7$v3lZe1 z6rZw(gkNMxNzI?dp>!e_XwvqR%!1df<0 zDWUMg%NCFGGS0~)tRmulU4S)SI;YAR6^i<`!K>nUl4_;nS5U$>UPn_t!Q!D%=~4#DvEV|ODb z5`9vT96fgu=&@I$Vjww)`Xun4Mmj_-D~Gz=O~Bw;zD_tESFW zwL0%HFTU&)8KWX+UCKr%t3o(`fqo6jtb+Wp zlGfz=AX@5{l-J}+n<+&0J{+Yfu~igwxo`Ex%$`wCVU_AHszs_e{Ad3q&!fS`Tg{uo zIGP3{M>`qRTy+4p9qil3t263E9uD*JCw@+`fj@C(k;v!&!FD%=qRF3u$p|glt*Dl; zq$g#;1x;fxT($M<&hn4n5&z`BC|sKm|6_l->tjyoo3q0(10|gD|FCAMa}l>nHCBw0 zrsF{kM{?yPhqLAO?=2jHKTSBub9VVBkK2_is>vIE)&q1Mn9){Jr8B>;>)seRYDB#^ zbmxD{On@WDcu8iDBemo~ex}@j7xHgF^Ty%9A;BZT+7SPjJ&*Un4KBKWmvP_$Q(68g z$*N#lRiR9NH)XhTdm#L1+0AToDr)!VuEmVK=7Z0rhOzJq6~Q;RFtz*{ z&!dDKAi58RW~7PSU&8j2@YdVH;Ogs=MS*GMMLnvmKJr_HJ%;h1Cs}aY2MN|Ot}#WJ zuAbs>!CkL&;r<<1=V~E_-Q%*L_U=>J_a^a-*C`>CFRSL%;NY#^`!^sDwexdf2fvF@ zSt8bcBI7540!F&N^de*?32ed0b$QSBp3U7UFc51#RDvUf47@{i**6{gaoCupew*!3 zLczmQc%%Q*ZA^+o%bv+L7Mdb@L8~;5hGPWv{D3`xp7?Tdb#~|C!PAJ*63O33Bp9(iacvCk28pX zRe0C(OqQg(;wkzw`en;>w0oKs{GQ{02^ihk17CtV`0(051-A(#B!v&fL^N)gO&7NS z`PJ|2ykJ@muEJ(^j|f$Q?PpV}$sHXV>^pQ+sH5?5;B%=GGPO@9suB0l2ntYVQ-Gk9H{^iA^ z81-s)Wgi+xPo%K72Rp5z-#0X-v!ioaNaZX%9;5q9N!Jtye7)4^LPlX*Bodo-yFq-l z?Mfd=Qx*3Yg~9sd=rU9NVrF%1n1`;-VYwwLT4HZQC*@U#O9Y#qlv^wAD-d0zx zs<-~Z3C&a26?8oGh(v7B=hL3kj-{4ly_h%$#de2H_ZOb3H z9+$;7nGTXfT4hy;YVqsKUfBb_wO#y%6{6e~S<6vyJ1k_R&t1RQZoIx!U?FGYKQ$#A z$snuEkZ15K!(5ZI5%xAAP8MTyP#U?JabBbUm(-s~%(KmJX8$jk{{N=*Bn+5cH`iz= z-xA2gM94G5DiE3t$UW9eV?XCR@A-WI+f%WRI)b3)s>&Ks=mXs%oM`*qnCwJ^>%JD3 zmg|n<36%j-CB2v9j5ZqdmAKOP|8KT|d4mjM+&qPwhIko8Jwrz|LTKYUdQ;jASbU%Q z@{$22vX03(?W*y6fF}_=7J1Y2%N8;Mv$REs+>P-qXc&%V^H2fEgFEUK`s;Qw6qR^X zHTnE?fYs9H3kBHjvZJT z{2-<_Yhx1~*Ed2%NHw(TxARdrm1m-&?-BDDYx&mFqx>rr0?9QXY2eD>k-lg2W^&>20+Galqc(F)$c>*iP$7?WOnqG$x%Vm zoU$v5d!`On{kH?b5o3OPjPXS51Ns_VqiFcze8kj73pgz1+6Js&f*9erdW`l9$Y?wA zMeV?!oi%OOX1)*nZ`}bQ*TskRu)i_j57*?E>!jsHQwLe^Q=qK6W}5N;xBdX-fy%Sx z+B&~uG>+WkUx;x{Oo+fn67TrFR7nx3UQhDi!a4dM3B51}3~ax~T`K29Y86amEt<>y zh$iRd=+J4Omf!z13it&6*C=qV|34W8mT?j1amlN?iPb2T%UDChZ(c$tE_LvjdN;%%ZgaEBKGXyzX*Aw4s~+;P~YJ zRz(lcRg00YF!N^_cD@ZIF~^7087hyGy#E9HFVF39XJd|XWFEq42~;42nYaPcd0^lO zX(}23!I}l!66UzM07$ktYJUM4Kb_X5Zc4fpbFPvum7|J#DA|C2*kk&aj9%)^JuQ+ z@KBqf9ihGlv?T3i(XDWf3#q45EET2FJ{O*JM~4!UxXNvJF|_lQ%Dg4w9Ag}{k2Afe zAGMZXSEsP;Jx`EF6K!J2rWO*(nGEwrbkxd!og}b;-2M@RtmQ+!7-kTz6%gA|GYYH9@A}Wi$upgD zvMk*7W+o0tUQmUx&78qzOzQtk{(=7o{p(JWiEX6v6tIZQFLo&cwDcv27cZWMXS#XTsC-?SI$a|EW`Ta?#bf>6@;g-}S8Z zKJ^}|)BVujqo)JL{>xkcV*hX5l)%`Zht$bdE`<)})(Fr5m=q?~{`#7w^O@8&qA^7EnoyyiQ+i2;UbmlC-L8|{3AA(NPm6Ljk6P0}qb z5H;UqL{&`XgDe68s^A@bc6;nDZk18ZEDY3}lkHvZJareH+GHF1nI%p;ie0ykqYM_e z$x8j2)B!r>t-kxeJPi3^1?m`u&(~KvTd9ZV8>P&#tO+-T&Ue!|pS_VB=vS99<;b@- zr|~TByI%Uo8gF@&Pv-my{&)(Eo8IcruJMomck&MaM*q&IIOzZX(f|Kf^3T)ivAw~i zE6_8Wm&=Q$lV{q|3w;)f;GU(yX*w18Q&m*rE(D)te+Q1CGt@_%0}b73M52hWD;7ug zbrVlPXGq!?fUo3G+JpEzOKyE5@HvrN#}w8oj@;XIXB+CBt_^hN)%^TIX=reM(?i_x zr<70%hg!yIv#@TYN2CiPnKWk=uGBDSwIlI@sP>!?>BZydouM8)B{}gt=;5ItXeS3Ls6B}pUr|&0QL`4RLA7jZtk92BP z!r=$Pv%&wGKkCS2-lUO&NBQ1_f1a#eMG>Q7p)HAZR#;6L1A(M3i-o((7P`642_pvt z1h%1__)$9Lqd-7F-iJp04-nYK0s(<_?EeM?|NH=Q{y#s!X9@HJ7l3IXAYeWDFF>GI z_CJ8Yde(}8@nw!QDlutrm4O`s%bGmGxmTd;P^!E#>p|+ghFloIXeF7HG`WUcS}qVT zjiQj~rB+Zk6^%lAPb1Bbypb$Vd6_4D+AYtWf%+kJ0zJw}=el5p_jHOk!M-O1_&kmK zVH&XD10A%&OXYZ_dtLChN0@RYFK|L$1rei?Ua`Me(^KH>E6jqvJPmnfU_odb1T-3> zV1=T#d5T%Y?4dhZtq9;bWrT4Iu*0h>z-69F7U zMn7%P9ht8&qOd#M#g9zaFk0-xb%K1pTFVulmw!Eb$N68bsro*ZpomV9+B5Y%K~y0g zdtP_cK5?scmv?8ovHkfZHr3&Yr^n}$#g>PxG^HCtVuqHn;ME;nOoCRFo9r~stAO(t zc+R!xqzAVtIf0YXKbd;?6m7ZJM<%BR1+@?|xt7 z%H+3{sT=zB0bA>kUX76g2N6{@xNYi^$N_$pRIm3VnkKgkA|pbAm?MM@5OXdeqM9k% zqg}{{2opz}diRU=NE~)(;lnd$o@lkk-Kj1l5r9!ksKW*c!B9&g@PHi@MsoT-_o(3L z+10kSbrrZ>h*_`d2_b!5enkGxt+VN0H}(~*m=6Ln%CF#O)ma9-wM8LrJ1rxHogcGV ziN-`H)+Z>n2>4M$7JFC%fb_6{@eb9AlwbzIj%6VB3p@b&>$9y)j8we;dQO-tBG0VV zO!6}#pk8Dfp~RemMwq<>P*;vF7YC2T4MHR}7ZpraIsgqMQYgh*A=|m>LCuuQ*&qq#_i33yMW# zhyTV9ausIoK{NA%tm79B(2PRB^CAV|6T^}(#zpE@#}bLIZ{Q`?dyAwuFvjD*)VLv9JGM| z0?OV_6<`U8-K!>qKu2pi;ZPj}wji!-yqB=r)R1&RUaFsWRTFrmbUkV;WtukU%E0x_ z5Fr59Fo%E$NpTDp8-L`3vIc7z7txR?KiA@CWYS;J64@EjNBV=w&TVFq(0<0DbnG$7 zp{0r2!&M--Tejl*+%s``TDp1IU%rj1Wfg~p1NeVFlRnfHSe`zuP289;O}ceoL^1j{ z>Zo^oHbvy2lG-{4`C}j3L*D&TLTjfw)IejoMK;9@Hyg_?g(J%C$wiRF7Uv#xt@;sB zoF9)I!A)Gj`19N7&B@+biNP}G(8R746Yj5{A2x0tJh*w^)N5vFkmJLP2M%8@nhg#t z83Dz@_}zR0Q5D@-kwk`sdAWH=$duq4-TY$5wnHy_?!nhxyT=5_AB}@`24D`$kW(z0U&)1J= z-LZYfUk&Y7+H(7~SVX06f0d(QCpPcXdt;0LHkV>3Byn$s#ybg+r7{xKtgr&QGTe0L z>{yRl^44lBpLVucyWFQg@d%s7Y79Nj(`z25930fsb2C>8*@|`5G}_F?<%fXxx*msC z%IxScKCHm&U&DMt`V?APz%kPVwa0W<8ro+RUiA-3VNmvV;fl>5LHpeje^tUne<2wbmL243IYE`Szl!(Gz{l{QB*D zQ=dc+fyBfAGd3FZn53=Nw?%SY)1| z{j;BYTdn43+;=V!PSAqkSZ#kKU_1x!tCy~_Uc`N6`WcW%ifthBQ_p0*q<(KyS5h-A zkvBX@5v7mN#fZT(SI-S_ambQh_`2$RGBd~;Rl%!2k>9>VC?g1Dk;A|H61L{fmLp@A zM0w6y_`hi(v$dbFS^!yv`F~i2;`je%6>b^PZvQu{82ZO5DAS(*i&fB6Z{YnOR`K+I zSOwwTe^`Zy)0jw?{6ANr33L^l({JJSKv!}5`TL%bE1aR{sBE9XpH@hVMh|s_@4fnr zZINBWBz3UM!iF4VpELYTYv-64XCqF|()jj+Co%0O zVmobh_3`%;B6*0z3%PvBxXX)&hz#3k9DXSPU@ZN~-^%K8Ooiuo2St8+?5(*4r;}mm zD#te@D;HvRem3Jvs8=B^=x|{?SI5TdaU*x!@l!8ZlyX=8F;+S>RI*k1=OUVW4mB~G z4s&?Ln^o-belD7!ep*X>+2~9CS-uos4u`+yR&}hhIuhs#$N3IjmZuIiXriv6L6}uF zuSWu#%5_iYilY}pB!_scueChTkOO&lhqR&{H0@nIh!$)aX3J;x`%8Qx9$^BAN4-n5?dhPPSSlgXAJ^H&GCtMD3)_??jcPF%3 z;g_mFE2hqC#V{$=jqQH9REt8EivoBSt{+qv6fWiehzr0!a*xTa|oqwGfM@YXBH(H3CFX{E>hqx zXecu~^emF^8LS4V>s-!rByfr~=6fZssVuedSg4qKqA7+n%OV75S{0NmkryZG zYmne&!w`C*Z=0tT@^Yh6%bQz+zt&C^eef(pQUZ4k@$>*LnzUyW`t^Z@n9e$z@v(U- zY*n|YZNEd?$>Vs1!K||P|74}$Q&1r3$0SsbP^e(<5&fcXqahBu{-p?AP~2r_N2ezt zIpy)46RPAPSosrwC+HC5U8Aa}S>_koORgx|d$URK2oNlh6&}jR#;c*MR{mp>c4Lo$4(f@nY{~Om1 zZ$GSPZ_#e{b3wpx;XC|O0Nrhr7oZv6=+G3Sw$o#nvq$Q&YH{=l`1QH{?W3!wr{;4j zptI}w|E)N>d^Z%ofQqAT190|#Gmf2@v{@c~I$w@(EZu}^klXV-=s>b>5j{U0rIG)B zoae^gYr8FQKT33^HTIW#xd6@-*A2MXl}sz>$Hb(|QR9U&F<| zw3CcpA1DNwL?>K${BoVMPA$jyGTjMF?GW&f2D|_O*O`M~8;W`MZSTNjfAH&Nu@SLa zK~Q_j>B{th(iK}tJK(TOY2Qur=Aq`#mhBw5G>T6FERbk1(vORF_ZTMCKwcx z$``EDGv3aHV14^l<*@i(XMe6U=l4ouC1ODSaxB!$0IWaz#PZPT@7dFWz&>}@)_}{K zfRnXZgQ+jiTejIRM%>)yDWNMCp|tetLc+`C&cL`_zgQ(Wio`0I2yxWDJDh{TL4r7b zpSTb61lCMU8^rsl%7sy@N^s}J4VPHz29}fcWo!;t$V`XrXeW4Q7*J9`|lm) z-2KzqMMq@7IFMT|h^zvBW$ zvo+?zq*AS7)hJ|id@2*dvOi&+6Q6)X75=qihLxPXzp4Lhn>!uMsa+5>Qgx4=5PmGg zXbt_CYJ5t{FTGKtNnchJLjjf`8R=K=>>|e=^!&`cn_kSe9$Mh!@pS=8xQZfz6Q0YZ zUu;Ow-4hma``*Vtk4NDCH+ON>G6vu-cBzv^%_VMMd4=-zXh>=jB2-@A7xq02(kDO& z4EE}-g*g_Bjbk`pfCKHKB`9_}Td?w38uhjhuR1+wk_>qAa};G~$|1ueGsD*0g_5%P zR~Px)9ALXCH#hdfvTyn8>M#+Lcgp>U4AjmV3Y9W()UEr?Nb~Pog@=5FyZlc##0po$ zpDu_M&NEd`Ex+1qs&wsJ?B!E0J(gSQWH<^<10~ zxwUCyn?Oab65yRUCaYIWI5aY!;*!yT80X_j84%@LQ+x*l-e7s_$o{CCx)R`q|etr}Oc}@J+lE<%s3y3c8f~hiQJQ z8+3v;FJ}7)O(pYuYn9t4q6d<#7nt_ce7SJPQDqj{iDMb;WaVmJrN6jh$7B8$=^v<7 zcfgM?xYaL9Ufi$zkMhe`0HvZrRn~g}>w8kfRTB+eb6Vl6u<45Sb)9Lm99(*q<3UsV z#c(*r(WG(9TR3DoqB}O?xq%OTuY%42jdhf7NjPuG-N(YKr=+YN0lQ;(3Vha|qvYn4k!HcuP4>zFM#REnuqOIU$SM~IFY-{I>(=EZ5)C_+8tb} zyj(IMc_q9ZFg*~;d<1!KF?MP`mru0pI54Zq%+9s^ZSC)%VxO<;B~$a|qE%mZDfrek zr}hB=Zb>NoBW20OM@O%pnz|#|KV-|b*W=_%Y~RYNmQPmpes(EIbFA9fUnKPaG<5bj zS!KJ?4n+0-a2~Z7P+9BUvr-O}6=*sOz`RyrYZdZe*Ptz|v&v+qoGooQ&<*Y~Cdca6Qrxf= zelv&f#(Ze(;@n|XOcOR7-<(Yg@W~JUE@yfYRlH13P`d%Mt+i{cOgT<=c|DEf!x65$ zlW>HVrIVlRK@CTLAxV+#^{uC&#b<~BiiyU_n%wzYRM8u>0IVfWk|1!WO1IysjUuRd zxb~PlD62@?N&<_Y!L^lmpyZM#eT&?qR1TKhF&+mbnXB3pB2#FSz^cI&fZ_LEq=U{~ z1~YwNiY}vsQ1z`M=5{r0vv>i4hv>;m`e z_h?1XFTt&CZ<_nB*`^-Ln@JKe3s_y$>>`=w#M(s_`CE3KbQklV7aj&L{hePjMh&XGMP_Tk zwE3(3(+4@6Q+}7%T`n0eDoPK2AEpk&T{XJFs2gyCjio?MO8w=s}F!82o*k+EC!`7GxYlxDDU(O3a^4@6LIe#i-m4b-e42YgCa`{7NyMu0G^y^CGIfq2vBB8 z#91EBV8{R{hJ^VDhIRrju%}m{1*?`!+PwLsT1L-rz2J3k+l%gPEFV7x6cXV}89cur z2%%_g_Eg7ypXgYPhloQLyACdZh%|wq1{NMwf38I-0}w?6`F4WzhRW;W73Y400}s>) zAjt-$cxJLPwZ_G_<>&^kGR24BI_pY~zjBqPj|BmyOq9JuVy4h_B&FD3mCo~E-`<7B z?n1;t;Qt=Nh~n*&V}##;a^;A=(QIIXC`9z>rJO239C*=yMsh7>3!gBop&sY-=8hUyHG_*Rn!a8u#_;q zj8CK~KIgUrlFQ2U0jG+3)AN`4GTWsF1u$V6bL(ZMsION*Uhv~iP|$fDR=vcp=Z6VH z;0Q`0w6b_zA5@C&DI5 zdQ1Rn!w>{u3zKGR{7-oVOkS-#8tq=w0ZMq0Y)rnuGdhE-wn2L>PxLLC4m+5Gapc%& zz_Z`NanShLKnFtQE9yrl_$d>8&~(jK`I}h>iauG2hgz}nGRLC?rRiI+>olewKUDVW z0!VZWB4=!6Gmd8Yqt#8r=}vX)K1;E4vkUoP1wa(91uO0jVkl1~MvkPAqdCwodyfmm zgmW+U9u6G!Wfo^$Hg<5i@H*^tcK0IXw62aRz<>x+4Nd`$sN^TO!M9`R!5W< z8^GTm?LV&Zp%n$^aQ?cPn4Q=OC96>9vwcshF_<3E;S&)ABAZq`XdIo{!s|p7-EsIZ z_8~2@?5;8bD{Js4d!$idvr4Q3>y8*}<(s{t$q8*kbi3fMa3v1A%%Dpev$Mn2=y}+h+7Zj+i0<;2LZw4t+YEO~%quA9Vwo+;Kp#o4Rc|a3Y zca%<=p8>j_8+;7b$@D%LnxSFYP<kM9gH!Vo5+o8hBll1m2V-O<8`unK*qR*FfhGPIBG!BrPyOr*n@b9EEz z{*=R)zfz(E;UAQ&?};_k0Hp_QkYZF+ffQ$yG`WGRo>*7ucu86=;n5<)Kt?*?rOy(A-DcEsH*4`0Dn&)~->=00Jg~4ZHKtp(|SJ zHyrltwa&*gEC1JrdSI1X%Y~DD$w~{4qa~SV(vY;OCdBkmXk(C_o;q_qHI`ai^xF>Y zD-yvyI{b19rT)SIJ zd8p~=W`(fzK{XOat0nPR<}S=(Q{q`L#X^&{eQ_E#am6rrycVgIwELjbo2K48cBA`O z6dqf+ok!#a#IM@o7iYz07#!!l_iQQ4grr6pcvAN&;oj*v;EY-(sQCwNwe!5r^+(^H z>h2dNSNgdm2;JS6e`8PG=$CKn&M}}nxjf|yI}41QXZ1_(`oY69y2uqa3QH`$Hqb!^ z?)w<|NF(UMtp5oNYBvJu$o0`;rur{2^j3{h`Eg zxwZZ~g3j0>z#^~c@yloHkcfQctNd;5*Bj9W@8w4wuQw2ovSvLa#5bFm!1DzI#CUpr z0%?789D;J`6Qa>)^wAF_Ok}m5-FYz|uO@)k^sdA+<1yaQ$zJ2{L?PmDz4Pnp7zp$! z`7trQo5D)lOns+&wi*aeC=evC59?Az(zpYSk%syN2w&D5(T<%trLIC3EUhK9X=joC z{g^CTW8Tc{IN`L0TLJtNV`e?Rfsp54$&|djneUEWmml%G-VR^e-zWdcEnTDgRXOyi@?W`hTexFMGz)s@Znx2Dj3uWCQqi|+CT+}*?|z)qgVq}mAs zFUvPJ)*Ws{s}}uHw+L(LH=jf5zXGB^a6JKjyY870t(V(6!4X1`C|3x(Np5=qPzRCDcqKE(VlCuPLeU;@e!QSx2VKe8J3VnqrDLf zdveZwJc^OotM^jx*_~rrt-RpKgg)7@FH2{x9!1gr0m+v4($L*6eZ)Fx41?kzfPgQ; z_qQFk-DL&nZVP_>Jkk$9y!6zg9SG#>&gZrP#ph4}{@0O1p2%Ari61<945Q0W zf6sIWc>$}O@@U67YI6CYOnLnCcAqoe_OKTCt5YzP(S`NL`l7kT@%i@eH}T#3xuTxZ zB9ZZPyf*KN?Ky<$M)k@0+!<8rcP3U-n9Bck6y>8aaqXhBtiHl74;Z>x8oKfmf=QxB z1&5djx+;pTY;r3?riHK%e7MO}s6JuMA61E9RViyE>`MP^&_tdRgE9G*%g1)f1d1b)s)>v3J?>d>qA`w?j* z;6$lON8A}1_J18LW1#kGGXoVK2M3g1F?~!mm>o| zoA|u()%MsoSo+mc5HZSYvT%)GE2ZN5fg2@$4|L9cYE(z2UWu5nd!#f5b>Qwm(a9%20u!kB^)^g)Z$>2 z2kCX}r-?36G$X@RvkYv)ht_6_eojYHSeKAgC#gx}msEOWl(AS@)G~mROfHv_k9GjS z*0eN<(XZjiaJ!K{j36^Mi-JKz11!|ppfm#ux4dfum&5vLLW{tZ%fj}s<1YTwGp@|1 z(_KGYDEf{Sc=dqyFeEPY-4*7yRe?CokRlt31$pz^VElDguPQ$&L6w(uMA7WJu}L`zfDiW|&4gljmOrqiRzy>iL8N=<0U77J$i z2?!$x(VB*qR$AN%_mjPp6kCz+}dl|Jjl{m1h%Osi9#o4sPnzLMBBVfc-=yx}tMF$9VgxAF%d*(RMgVV6j#k`$6De}zwKxPO? z6t@Jc0ab2w{6|V?^%@moo3?nGYq+*QjDMqcOd2Dmj7oKXbm29{6}dUvA!~ENuXDNP zPY*X@W&xemxhy*~R1)#J3_!Z|2N*4^l)4667KENO8>F?lSZT8AI8*i7j9R#vDin`m z$Ff$|*=rq}dxmR!RPdsP3Ilt5j6~xhx-v(C#k$+C2&iXN)OL?&B@y~Q=5Ip+Gl)|RVZbWxfb|pn*C-S# zhla{`g*~aBw%VE|VloPcZ=aSdczW+#FWyeh9s;=M^)s{iuvXhEs#@(PL*|SedJM3? z{7M}Wt;&b|*fJ0dkVc1M+JO5`z>@&2fPU?J!2xxP`P9T;-*Zr*h2Rjrhe|H3SW2|V zs+rGW>ZOLdKx#rQ5CA6V^eLyuIgJlj5XC1p#G-^ZM(35y;l{UY%P!sJ5GPObO>u<2OAWC2XRDM_gEubq~m$i0-1 zyWG%Be2(0q*-CNxXbbE7P`DCPvPe`JaaFiTNHe}pJ7){=n65uuMJWy){x1{`N?%da;T%nVAVYg zYkN*k=Ppd<(*F!kC~CifOx$Z?V!NHO%^6m04_jD`0qob=7O+JcIG?@_?mqHx*&1b# zOWIe(qD`&nH>eX^uHJ=kXFQ}@$AS!ongHE6?1m^l4uTuisSb*x2G4Ez}?0SVDB|Ir|0u=|M0@e$%Ps3^Lpc8;l_%O z;KR=kz?JjvBfy4Z%X?k^U{owr{q`+jvzF6~+@(Q#uJR`o-N4r~ri>TJHh=75DMqdE z03H?y2)3w$@Dww^VzICcU^VB4@dGT%s+;{%!hlJPv9Jc#R)?Cz{LSSH2g# zycV!M0BnvF>mFB|pQ3NLByuKm@({Ij6!Zd|dzLo*RakLT=C!_jG?7|TRBzD2S855^ zBE!i44vd}7S$*Hjdz%eh4bqBMzJnb7w%`SJwqHeNomZrwXq8hooKX&cgw`_`3UCGW znfvZfkPdk#G5VXBlpGe)TmHPvY^IsG4pubwf~*fVh6&B`?TD0EpjT!D>O6^%E}4@q zA+1-H)^l;DvB;EmEW{vwy+={fc6(x#P4teF@(nYNmJo%-5Liu_WyY0 zhj_G_FcS`JkZHijAH#KHZ;A1xo3P^F`_$E5Q=sLp>~0eoenc}0FF=o^LwrceS9rHZ zO$`$(Adj=xf`p?CcU*z50bquO&MWH;PWQ@8BDWJii^spV2|D4~?)?z5ZHt9I9R>Z1 z*1w+eE?RB@*N5I9vo%cnSa>4QGkh;h5#QSNNLPio5g^k1%@;~Sko(PkNac71fv`l> z!duh=G9gfW2$%ygi)>-QXmjAN>F3tGK=`;0P9{OaRo%b|Dds;r0L$Q(d0j=)6JX(J zFvL&{cSerIo@~815dz`($o++VS})&T{yHHLgic5}h7SsS2#NxVJ^xZ(Eq#qK9ybL% z^u17aJiSsE$WHa@K+)YTjB@hdk^Fus^R(8t_gjL;9vJ2P@;m^2SsNTTM04_EBg5|j zc?0I~4i5?7H3GB|fRAT<%f1{W$%D8!l%#|Q!RKFTJ{X^Z|ErIWXZt<<-`nQh*;ah_ z^_0xZ%uQiOyL<*;{g>~bGhbpn+$!g$Oqc7}Jx8h8^;K;&3Zim9cAlL&M0u3Gj zj=`U?f3gTUzFW~bdq&HHQMWeD{~?YVMKcZNTi}i+Upge*dV%V`CHzw@(!{?AYrI78 zNaurz9oJkr#Z`DQ%DIc#S-52$K%I^i^rQWwU`ENw+RH@)o@bXZQx}Kly1(>%Lz6mK znrhZd*1Dq>C6&2Sac{H@8HCgf*4j4_(97|78|+R(eMM}d--+4^dq!vd-sgFH?FBt8 z^1XNC1uAxTurC}*%ZG?g=HOQR+%34F&xoXA-#Y_3jqL-?8{V#AW8=r~{a<}hU2iG$ zPf1v38DEwIROf;*_!M;U{1~KDgoxWPC$P#K<4@vPVVkeQ2sg$(~q2SDvtljg7=}N-7O1<|SeH$>2;a)hP< z7Yc?5@Q5pMtjI7MOmW#$@T+1fl5hCeIG9)0X=Q^STlAp0Js7#UJpc$o2_@uk&LBl% z^b0qY4=Z?4T&yruQNqb{PDI8t)r@&P(0%ThY7=s2LCzh3*0&a?0m>HZN z(715)$3nmLcoc{-0}>vKfp;cu48bg(FZ?w&$w8f8N1aErN`W8!a;w~5kJC5pVx$M_ zN(2)cOPB>kKW`9taB#eq{@S?ooW3GsYp!vOoNE~2F@3AOR^Bdgi`hd+M3GF6yWnmg zp6?_Lb{U$u>bf&~-nvRFS#_~ydzO~k78mO!Q$le-F;sFz1?V0ZeRvH2nRhw;GYXxv zs<5kr8jiCbE5j!UjGFSnD!K5xJh!JsIjIXPRVYN5S27`&-{!|q1&YFlv5zan^U4w{ zfg1pf4EQ^Nj=kIgt^pib2nv)te(2KvI9gmq2DQhF>gq-iD6jAvMf;$PJup~sus4EU zV9O1p_#l4ZfPkn&_io!;3)tUY&%x<=lB#llc1vhHAfmH`ta@RLz!BQ;LKygCo+$o|L~qq|>5k*_FbNU}VHwPk2^qpjkDXM7T!o zNbe*)9j#iV(r5loSIY}>y^w5*S&ECI5$*l>aXW)=3xLeCi?I_;v{<4N@v6o< zh>v5&IRIIos5z|N5hd3EdxozwBKn$G`5`$*)lOEjzu2pZwXJ@sCmt?j_e~#XojTw(>kKPAAK>b+1j9-Mt1zeE z_N7iHLr8T(sQyZl$(?YyN6OfxPgI2xLT2m%^_DFnp98VhfLye_UJXkM*A5x^WL2@M{ zJhID$$QJV2?)(<0EK`(Zb@8)na zjXYmeR%FX&cMqnnY!P>1b*?s7C@|N6_X+l`rWY&5zidv#W=YnM_!n*+LZ~j#OjPGY z_r7g?UR@X1t@dtUV{8xFx{~=aUK5Tm@lJr!;^q%8vTm1@!=E3|H=a1fewqO&Sb1MD zX?Z1KLLfUqr$Fe=Hnjl^%ohfopXMw5i3)BjaKut&lnrKsMwV?_EF=1)UcC%^q_L%H?=5niqlhe}ikd_mfG zyu0A;X!Ld>$9?PAG=MYuoI(u3tEWuqju3y}yS3-Z`(61rZh9+H7rbraybjV>+th|{ zNsV0=fgGj1q0Rx`8>)bC-QkV!^+jrV>X!lv9qgw$l9S0p5j3X5!qj{zFQx~&gXeoL zLf!ZgrUOp877xUYdx~fi*kp>sGMq*_b8d8vI(RaU#@KiFL6+7-F*E^(#T1HrJ|vQY zfXO>p+TkB$G`v~>3(G;YzP+PR4%v#c-QLEth6_HzTj=>6z70U^5-Rd11BiCI0k|)% zZc7{5QDCHAE@6HzKYSSzo*n-8U!$6~3hY*R-Fhl`KV7xQlsw5Ska z-0YPhnNx`b%u zy1Jx~jK1HJ?3n`W^@XrD`vm--Kk?ekXd-}jeB0qm*0a+}+Y(CZ*m5>0Xhz$feE-#B zLK2_6)uESXX*LTlf?ml!J3uC`icV=^FQ6M@qiugAzmZl&h(7(2oX;P_rNqsHH*uMH zzrWTbm_m`1WWq6P6)J~+oM4TSJS>F&H(VTVLR4eW?h63utIUVb+4le^mi|DC>}EOzws-A(w> zYc}r8Y09xmX8%3!XUEQU=YF}Ve7ni%Z#9I~%0qAfn7>@IYMRpJjEhBwEn)v;@7VUsqCsEfJ854aHUvG&v5{nM_XA1EDeB&eq*|VBi?lU3JFqTUJR})pG zC+Fuvw-2GBmM3)X495Nn#M9a)&@DV@CKz+CIW@2DMr^|h{BPDhon4LB9|apt;LGz^ zN(k7g1gYr3`0Bjs-L`z>exoYPQIg)^FRulFSu`k`?V=#4+FSALV3cm`UB7voiz|<# zA?TVAjRT>NLQ3;vW942?t=rH2k569`mj{dW8?rbM$Ce{uQ|Vz`8%kVs*7M|Nu-d~P zIlW|*W3RF6zP1ITz>biFFZ6)$OiA5u`YD%;y-c`BEed3v4td2>r(9S<2j2mqFe+WZ zNwcCwpxD5P(yO>D*;5(y0ZzBu?bT~F(c$dXn}qJFPwJhNikec5;$hxT^Oltkj~Z}5 z{&O7o_SL;l_S(pUi2LSUXE^*SK{|V62xN6?WRMpC!ApQ#0qhRtS$#f zY1v|7Qp6S%-;C`7Gj2$u{#+C#1=}uFWNxbFUsvST9SA5Ys~8hk9&S1@m`SbICPfKN z{;m9l5{BM+%2ICY_8=o}mG(XQXO%ZMWmip__KkN^J+r)&;M_iwqk$zGri;2fKwfhq zbOC0L$=wcmw!Yk@y{>+ET(f5iKV=;72AAxg4r9Ls+-ky9G^6EKGy@r&6sQ4 z!Dr-+pXikzL#?vDH7PYUX=y2wfEJzF@)PUt!*(E4m}YnpxT{gMD9KE+`MX_2?drW>}P~1~x&Tyt584C&ejdW13C}7Y* zaNW~5J)j@34=`hP=p7u0BzxX;0Y4c$lvB^{zoppLd27HSZij4KIpmOaM`mGX4Bzv7 zQyd3g-xH#~`I|po(83u71%RyNG?=ue5N^n}HDnc>URkLMv)h%6vRe+)#~n46OyR!k znEoU~*ayevj5;K-pHSvgUCAW&aYP*b3UwP6PBKTjY-O7U4g&LX|K4AtNZ3t*Z>KiUSF2%dLC z(&>t1#rhldZJPZnijQ`%dpv{eE4^YC8Mko3o^F@esus*&FPrU9;I~%=*&YpuJ+0{% zRmQk@2$@PA3Sx5YJX7hhlm?j`8_d*@y<26;I}&i!35{v@E|lEW++E9$RWGf?pXfE5 zL{me9+Z}mf9pDvu#Vi0 zwFqk3=2&q-%!Xa4n)16ryXD*R+q-VO5g!V`7Lvgi&4i1*IpRec`L2B%uEEmVa=Yr+ z$7dJEFW;PMu(v} zPla`ov6K)-MM4~@0X4wsOdgwuuF5Rvi5p-B(2463=S!L#W*gyPI<=2c{<_^E$G1p zcGiel+V6B7tz!4kB0#%m5I{$YCVU)+a(nJ*EJ;$<)O^fR5an&ziP-H<9?o0`c35Od ztvV%G4sQHGIbJSK<##1ZD=xCNf{O?Z$os8UU1$Y*7k9LON(#{iAC!>uVOWetEW_*- z6^UTK`@lydoJH?oWixi~ptt=yUE5v7mfnRq>0q*M#~DfyG55Oe2YcSPzeawqJhoTAa|tzeN>+uhsSerOLQw!3@a=2`8U zJ*anIxeK#@T^!Klr`Uy)eB3bZr&9Q*$bmK}SLNc+a5IE`Ct&w143s-_@M%!1*F0=> zEG*CT=7jA4FC=$ti2hEZRRd4vif1f|Q&uCs83=xk2(k$kcX51qPC}X_sQZJj)Q}QM z4y`bwa7AnQ3OtKP9+&|QE<)xqjO4+4_1wTP^*!aMOj={mY4<^)C8m0AFFwt zQVUU8LM)Ox8q9i|4CtL`iteClclr~{``w%*@i3#Aqx#?7-33Mwr{h276ZXefa&P3GJaK->C(L}`#2ghJwYj};#-%jY%FH2sgJ&UIBF>j# z$qdg+mZ~8XVPvzUm9m56rq|G_GPVLEQpk4;FaH77YfC$pFAJ_NbPIla#bewO0 zc~+$0BHMaFdR-93#kL!9Uf-6+S%}GBO_?h~EeIJ1mKu!pby&lD-rL<%YK&sJR-{n2 zKm~JkadQ{NO$_>@3B(}eOou;V{Q4+XGKbVv+%$dVauoFe9&sio#z&aGuVY8?>vyAT z`Q?cN1h;*l&)?>6^j1++)N*_^rDffJG)hhB_xBG$TEZ#j^soHc9JVvuamun1!bh^GW+ zYB|$(Kjb z!G(TQ;h-k0aGliC5si}~W7m^^jEMVzJz3FV98&LgjxnVURAbS# zwiiX*4OCqKsy~ybcjDxWEt)#|LueFLlv*iT!r}l^_YPO)M0SySKXM99GvH zma0WEi&BzjG`wM%$~6J1C>-se5~?ra1QIG{#zh(ySyEQBEQOpfn&cB5Ca5HN_rBRH zD}R;3ir(7iq#blroRqGgHb>3U_ikmQJGxMFTaGWU)-Y!3X){Z1h7KGz|XtxcB zw_vMLm}qnpn&xrSID_Q``3SXN8iBHMzd)um7T^Wh)s7;o?%26!4{vw@t>({ar&FX} ztJ@LxRTRvCm*t`MdC2Tt&Dw-vZ>Z_J}%}#4~P$?5QIA|TVAtlR`D6n`6RLRI$ z3Re(G9M1x|#RC<8y#Sm9u+mRb(>#X5C7>aLD$75zT>pY4)ToFPeUN*c%_zh+Gu&A7>LR&yD6M3K(oo!hp`9Np0pi;C7> z@}#+}12@3Q*jA3pnPXaQ#dIh$F?CT*{XC)S=ZP8Ti7B~%1)gP;#0>W?zu|dM2Kzy~ z7j#^tYCFj&`v$2LQLD)_(%Hhu1+{fG&WV%|C>q@jnOh+NtsCq*4!%}1>mCkD2w=K9 z|D%XADdz$B>-mgffSA7UrCN`6jn(Xqw$VZ2G{ka6&W2*7q}oXxQLA?ti7?XOEsYrV z+_ym&lmO~~leBc8=SNZXRh{e-+ie0f#w|E}h^HtOgRRl>ExTi#!`dCwg-)wT@j!62 zOOnr(#+2uTLA}AKbkQnAHzHeuY)HcZnWW-e>Z#)}_uFtBoZk;VvR;z*tu5wwE zkGM)6V#6)DQd=<<&V9GHhNoe-=ji{6aclG25_WW&@!5<;*C3ITnJ{kaw4(5F|e#O=LFlZ0fsA~E6W6k%_XO-HlMl;w9klSmWX9Ec$1QY1Zh zd{j~weDOm&=ycRpAFN!1dG}y{`=RvO-MaKzBmdzYtH^#``JNjVFf7D;$nUE{qSO}K zgWn)f+B@fFeU&Afcl9;i$(GeHmgOpcSpNRtesz|nBX^wox9zH?m!a0nM@rU##Q8Xd zf8514u?f#twopk*oan=~ee)K~A003bJ#o#y;N(^_C>;x;qVt86yYtw|>+?dY>f|PYbjz7b z*O|+KnTnNLlu1IWq9-&XvpBpFq?joa<*OLW&`(%0HC`H{zv_;+%W5n+Q*rAT_ewWr zDgALwje}G*zZ$qsVAms{)g5Dh*EZjn+SpD=MaH5cjcA%blnLaPWftJCw&{=q_jUzk z=g0uh(C$M!=vwqBkvF$QEmpu4EREziJe^H4V>f~6KzU(c&9^aiD^)pd3l4EGD7fkA z)w%$OXNq&Fgub57B{4jJ+o^Yrs>TE<3e(i(m-Wr2JKeR4cZ3-pVyM6 zJgwl~?p< z-F@A>*>c`ohiLYKu7+_K?3pNl!;*ykx_{l*6-G>83&c>Emv{BchZW&)7^itg=j8p> z8=Ki@N?kclEeWSvs{Fg!I`+PXo3iKJltX*vorP3e*`T)N#E>?X8yM7RECUt`T?izy z;M+P*p?z3?RtuMyn{2FtgkfSG9Bf-Dz)gL2}!yvj}e*YwPhpi;R(%2_Kqt@>2T|J9atfWVM6n1P5RK zAUeT8xszQ@V6>9NZP=kx+EwaW{ku31CsNPi5NV`;Tp79%lk#cckAb2mATdBs3Y?v2 zs^4vl(@Nrty%GDiiD%bS`>VOBu+Z1XB*aS2M0rIlqyv)}yjM%f<@*yZ^5Z0ybGI+< zm!gOC9F&Q~OB%!QVF;TTnM@O~DWzi-D5f)h>;S!*0aih_uRtd41ifv$kUhPS)_#d! zxtolC%Ki`g3{jt z#^iEWU*W&t=Sx#m+>Or=+cmdo)#~OFjZv_F{1oNQmsd7lV5h^_@; zqn-}d0%7BGA8N>C@Lvzwv-573k=2l-VdCWQl^qCP`;Iybq#VSqtW{dsQX5+**z;{{ zojqG=VX#_@OdY?bYB~-SO;u0PSu7;lAU6u3^Lj*cy5K42K+u*0afr$l!KO4eYu-bD z5+~QZyZCmM+Gq)mRRfyEIHC8+b~V18xmzxwGDc0x8FY=5l@t>l+<_0L_DL}4gP6)vd|UO3=w zOsxY!DaYW;jc#cgVkSihBJ`5vAX~@A?3os4z`QMwe-?+}0V-y0l{niBUFj}n_IrBv z>l|F`YY?Mbju^d*v>&LN%e$3-kN%S8lYTx=m_i_%Vu2;wo1$A+u2Z{>?I_*qD68z& zSHJ*TjwyE+c>q+7u?snw@Vk@@ReS(cTO&}I$Bn`YA;pk{@GMO*+Fg*4qAHj0F-hdD zxuaPs3A3U!uXNg)vYSF(C_e!_{%N-;L>5WWwVd@NXKhfry}M{bP#A#vS^0#fq)jeW z)IprOr{A1V(!cIo*aj_s{k>Ma?LJz;(L@mq15v$~W z3;_0i!D18js!7LA%pSXHpMF^pRDhw;l6V7*FB1&N01^i1p%1TMC^F!VQjLw>-&SB+ zN3cE&T3`H9T0dzkXm?C4t-Z2prvmenGC4gtzWi|V`rXeLZvI1m#(55PIZ9LvQ23U| z5mtj0%slKwfoV_w6^kY`x3PIDYeC+0;mo;h=4=&hRS!Vvbs>imyI}vs8hNqm8SP?E zD`>YCFWI5h9?mdK8ZS3-KwM2cW?2k&T{zv9`cl5mbxj)9XhplqEY zkCOz7*y*kpc5Kssx1v*J>=@8#pWUx{R^CCU)Zwyy>MeL(Epp*|d*;2ebz&Ol@z}mn zaTGMkU*Afa7pEisP*`!uQiz>#Y(wCi)OcjalH4VjM$QG;HFebXm)TDQgJ|%r_vhP2 z9Eek!*$&lVy@n7Hc((>PM2JkGIKX_yB@k>@q|@0k^nM0^Ez&VNjp)WyL+GqP+)A`*|0{56><0%fb<)c?D4ors$kJKHwLgGtsqzex>2G^g+5(>UDjq-hO=?y(oq?!E=XF3>-*2JNzADbEiS{O37JQz-0madj~oD6~$smho4S?hKbT}e~+%qm(Xs-fr*sdA2 zyfWLxq|}hvR%#Go7SD3w!s}>wj)&<~byM&T9P}z(Ou>cFHW11bd`j<6Ok+Z^{uZ|? zkd=99$D@ITVA*9-jN??%YZT_)$?sdJhX$x843K;Vxv^EEpe$1XWZyE~$W$|r2@^kb-W6%obIiSO!aY1AL7 z&-{z5>M4dv%&;Mrxu6;d{;0Fc;S`21GppQNJ9VTDPx; z^t3$Hqb3Fc5gC)pDwTB{?}v^7>jq9yzl!p|RCa1XoUWHjc(78e%!)aUZOmzWlz%{f z{;835emD$zLHocHUsvg&+|gcedFGup%&@(n{RPNV+hJT1uNQJIy}CWkh*H-Q0*xjT zPE9Ae3)S*BV{}TUJSq}KhAgBKYp@+a%J0C7mofFb9bw-T!aVS7C4vk&&qbcmnNqsn z(wXhQ;FOcJuzChoyTWRu?&Wkws;qQ>ujXCT922rY-C_iBikay#Pc4XD17Z!I%3iSl z*->FH*tfIp>RCGsAg6g8(gbr&uvB!Z+9$|hIVUF=Z+CtfSck8uJPmsAkV!zgti-Gs zCUKZ}t&-kw8oi_mO+%L92s|awo6BwDXlCB2&RiAhQURw;W3DNsCZoK^Ga4p;EE-yf zZNGV*gNta=x;%ZUQ1AgWen&fQ0Ug%ChP}^^4NGsFQ>_R+T&u9O8hSd=8?&p#bjao` zo7YqW&MCdWM$v)%r`wIw!Z(o&VhkbHr194aTyy^6s4@B zt&3V3X#1v)>S0;4(H)Ja^#0v{jHQm6V_!E3oh{!oI8b&fmYt^~KF}5=BtiD|J(cR? zZtH^>x2uEII@fAwuXDS0BD^fCKBlJfxvS~|KOsHGEV$-itV@K|qk$3&2 zFA4}#OZCzR9YtjTxy$B;Z%Elr0 zx@_Wb+b7y05W3j0b&TzQw!0YWCmg7)DpaiRRk7Hp!aUgfW1zZ{V8))7h)Q~>kDTYe zzph-d(L5NGwOKpcLp|A6RT{GnayhcD_Dw}uF6$tHEs8+= ztc!+{1GEBkbr@5#|E*cHm&&hY7~LP!D~2uwTGfhPXpvG!&%zsjpcZ363{_!$M*}p0 z&vudfHH%dC9vV(qGof>wR5+nn&aCp8-{~#21Mk&;46MV}qXM;Y=9|%!gJ+;AuG>u0QxiHiHO_;MO?sBY)J-jF!J16D zjvrfr>QTbSD)m7e5s^nMgIksGF$62Bo*me7+rsRWvO59hY?jQ+{nWhjPtd!qi9vf! zNtgx=Fgq;`G#X&->_KhSspfhVB`M1dNpb~IQnnDd0u6!g%*&dj1sx5R^ukPg-X zx!-QVK_r8*AfrO+)wp5tKcRl60bcITx{`#}4LTZsLM!MRnq7)J)HwU&$t9$zDM2~- zyWlCIMLv;Vgq}sC6?FF=fCfQPhL`7AJS7oo+g2lXMcxs7Z{LUo>2`=@JX^h8xcW4d z3fi_a2SctG#0SbbT)gTV87F3f3?}p?4R4CsX`HFNrU^ZzIhB7qu)C%F?v__%2GPpx zo+zMyu#5x1AZoj;F+~tms=^KaQAoWTd^Wro+Aoa!h(Tr>&lc37Hx(Dj9n@q96PjUd z&VE;4LP3TFMot&1ZYMhN$pybtxQ8Yf`E%f`plh^T z)je<+NviGUw0$sqVNjQHhjl3v(|4R={XY3`x8fXzkE&K(Hj0#|jD)G_ECrJ@2woq5 ziwZ#}rVqs+l&1NnT}qI?$Hu`raE3 zc-w`!=bElFM_qsoaHWD)&_1kr?LiXUu}ZS`kVE$Peo1x$NnNa~17ieLbY(C&xo8xR$tG8x%;HDRZJwpTwj zcZ)Qof*@w&Irnewdk*^GgkW2XsaJ2<@YLij^=6v zQXW=J+Kx$flw*Rag*u8nsUdi4zlm35L;=ab~j0lP~mQ7b4=XX;s z%tCF|Kz)D;^n&)jB?DbsF~R5MQfIrT)0wJqV2~Lrr|85k17*Kd6rc_{U169Ez*cHn zX!8*Wcb7joFncB!A8@jLs7o@KWDuvvm%Nf-2uf&?LQM;_P)b(pmvNDQpiP1=YC!<{ zCChV`)upj{3YDhKP}$Q$G^xBO(*wk}G`k^eI?Lx|N@rRG#4fMD{OY`Hst({8z{|%e z&nGN{+$OkVd_0C#rmhwQNaO68DGY$lNpH_qgV6T#y>Tw3lUxl_%gb&CKo8?)&*Ems z$&ou)I2SYlm}a+^ZtjqOg=F*K_7_50@TyHeZk5*IEhFJ$ZeOzbrT)x)da8_;W&>$e z>XtfnmF9`O7>!60-!S7vjChEZ;^~zBlc!)fXE$O8%E;zBlDdwIIAS|0%SzXpZK^?= z8BbU+$*0M-T}G>B84sV~o;}01l9Oy^t3k;DdJ=+ZG|&a$x0Hc@B$wB9+kw%pfuYVo z^JVU3L@DJHx)NJ3tp!tY6QGsmCK4(pmJF9l7(1341O3ndw=|{Cpu+-`baX8uf&J&D zVsSrgZVs$pKzmleH(Vvp)1u;sfUEEFLoF$5E z_$fMd!1DQw{ovZ126l3WPW2WN7n;shti!0by9yc3b=!x9YR`aB(Ts&1w_86hbYE5d zAAJgEu+G9T*K=<%xt}+Z zJq;Qt$@y-eoMvOD^37ah5Y#|`4kVFwQcI9pNk+?ynT%~nwAMv3gso+l<1&0ZTi+Ni zw}jotx{Hrg!Ii>3%NSNelv=@9Ni%j7ljCR^mksbEP4e>U?D)^;7eA7d*T)w>p7sCm z?Ah~S_G06I*|XDkKVMv*zda*w&wqS;?CO0ILb>Z)3R+DP&KEH+M9PoQlHFo~cR~Qe;SfBJT(P7=_z>B>rP zX`Enxb(5Nk8!f1fYOa^OJCFWdat0cT4Tv=>s+S`MR>_5kDU>kNLiuSqJu z$~))=(GS}BnjG;86JJIGyIPh27=}8P#j@ofS_VYV$xgMW@XpqjyoqOEh;5E|){KVb z3}d(F()JBzh21AUQ` z*ac|QQP`!RMqLYNiQ@3NLiWY?~FK9Gzf$kchyKBs>9rRp( zAYB8b?afSgWy*Mgr7KipY~at8sdO|HD=?lS7oMsD^WaO~VR^V?To8fklv<=%it&FCT`&K-{azHwcho?-nJTR*zNX7Y({U<}xcVjXZ^t?Il^dHOWhy>0C^GsZ;W zmd&a!m<6y84FY=jhuYx{o9`Gs(RQbE>U%+;7S`BN9!G1^H39g}a0h;lA8ONo@pP8N zqq(6Jc`?JZvvYD^=*D)vTPyH66S-tK> zo>RdIl=BNu!EoIL!+G>urMGX`I#> zY+#BeORc-eMh8Y%BdlZ>J)4s~r( zT5ql8cDx_&$EOn4j&Bz zRWLACh*!3bs}e&FL~~2mv4^!B^`Pr?Z(E0swH!Lu)e(Vn7X?9o%#e~c*6SPV$|oeJ zV;6nRt9DcRb>?9=zXSrWb!PyGB7@@PX>*pbX)KtoH1CXK(wf$xv@J)^=$Kfb`4XdR zt?o$0JPm`8XY7*m1lr%aV9oAXDM3Y~6e(y>rjF#muelDOLg_0eyw3E zJ9H?UlPi>{)&%vg7R5lKhl1Y_W~*-DN{}h_rInaH&bUMLZSIagB~W?f9$BaQPbaD{ zq7?tpC#_k3H?nh(8@CJ+m z>7vHo;0)KRn<}EbAMEYCvse3$?(&ea98&_!QppU~DpVAelt|54lZ2+@!bfRyU0S}K z3=K_BI(uERRfw7+g6vaCrG^j~t*hy{N7Jo=jZ?pWK$N*ej45SAcx^XKi(?mX$8O4E z6S%V*WdnwrtVS_6&-wmn0oGIb#j6 z@6h4jZN)VgBEbx-=B2DVF(clRY%^eoCcED;muw;f>Sp0DE(IMC%h0ED@J z(Cw~|I^~sGhQl&9?+~DvD_AnpRnOKi8r=_DqKv5N@#af@ZJUOCYgL== z5(v8v1V6o@mM?`gr%9Yrsbbo+Ko?z2`1Kl!-+^O-ZQG#z65CMrY*!AXaZ;Uxs^bL^ zN*uO(d?$WVBioaM@(I<*DmYQNqopu^9T>wTHmqX?I&e$@EvKvBT6?8S7wO1jJ|ctu z+0{>HS0DcC?VAs0|8;$J{Nd!?#l@Lq@&i%`7uTGmi1u2TVp#j3O&w^jVF%f_hkR%g z2o`H1-r<73C&oNdRSV(fKoB~MEld;n-}7isU2rw@`*SSw1E;9uj~NZwCCg%e9_c=? zc8py;#$uDiZyuvt9YfhCFm;Kd#UK3u+Hi@oq6K2mbFCBVSO6vzzq7w^I~TW+oOx{| zTe_boz*}oZ2I~&A<<%r0tS$=qVFWcYi_epU9rI8R*09IEueE`3u(}{^6Mm!m9=AkpZWmm?g!i4R)V)Z84+I&YH)J(gF0|pFm}Cz_f0OS zS|UjXg_*>Wg|Sxy27lpf=RVG@Fm6tT@W#Y@qL!qn&9A>kvGOk4zZ3gJCez} zS%dTTOv~tnn71T8RhQob(XTGdRn(@B$;sN5h6m=bF;QRToefGj1^$13RRSdE&Sf%y z_6?vFmff}xOyYuaxU2}lT;$0cepXe>sveaht z)V%@;ho)75rZE^D9t}5Dd}nN9UR*U@+4SAvZ4~huxV9G@Iv`9abLY^-6NX~^t+n(T zw`%DoSy+cPpx&u`XsAXKCPm~|Mj>&E6@qP&9OlSbs0TGrZBsmdTjka+5y~*NEd-Oa zu+E*{3l1y2=e0*kA0PUlNBjM@8~XlN)Kmgp3Tu$4z@O7 zM>DAf`<20L)@PP~Sd%;1I(_-!{neXo2fIyFt;HQ>;8xjxIyU8?wc8?F7{)cnN<?_WNwRA=Z%&=)o2l;27-v|jOc zIS}^#<~1X8ZY;X73DQsj$tIZTcWE|^^9-wr*#tiB7a4Oys31l7897Tw zJPR2V9U)17JT++|x<_3o&sj;g=p2KrQt)i*!3Tnm@&S>;+Jt7&z$AT}%r?i8Os8X@*S0wl11q{$ch(7PaJC5EUpSmSciV)n ze3O@pWfp?%HMj5SB*!y3d(sJ7ep7n+PN~xyyaOVCf4^&jn*5eSTX$3td5j^Cbl~$q zbX9IAy~CEq+ja;W%-{AA+9+>E2pJ4%c*D|Yph%OBr!yr!SmgV9)C10Ft(wtR*hV8D z@7Zm%_wRofJhygp6CS7u!d;w1AccG2L@u`oyTCJ%fy2~3-xtLsm3H0-Hkcy zgj+*@zNL!<`*6z@^I=)L7CZKwmO}dvy=69I*GYlsd20ycot%lhS*bnQ(uFE)_cHTR zU5mq!Uk^ew+){2iC(}4RHu*AqMA~*3n~1RbNp~%&-F4#fI|~FaRalk^lJXS#!>uxi z6S?259pp^m_0`Mc6D@bt3VoNQN+M+Inv;ls-=$D@OsA8OKb^ij(QOfUw(XUWa>Rfs zz5n1ePpCKKV6-(xh?p?Gl>&)^*2aH)-5);?bmz_a$=OB!?9-0XYPDLu-Cg*9tJNy~ zztd}X_x_{3yT7-;ySKaF+xw4JyW8pP{Re4%+G*5(3MmKwN9#9_RbJdT@*xj>6svrH zquAtg@9B-Rb)i`E>)HIz8`SU!A{vFTVi*?SPz`+N>hjP_x-o zE7~MtBDIVu)D?uBl5C2FX-tu76B*KfkUYwi{%I&s(3Qe9V?a4LEJ!n)lfLfE(v~_5 zFUAvci21~^Fy^81ym(fbSjcDdEFMpC!tYX+Nf7{zHc<14XYoH_JO!uj4XlDB$T*{E zt|c+G56%SGm?q>5KvyPFq)I+jeXc;fRI|V$W(o|}LxG>9_yO;R-P(I>Oq}fOLH|~KwjzEuSGWFf(g5;=D_tCZ7L|6uoOYI;%$7D-?6Nr z4Bc`Paf<(LfR06#ysMz7JO{Z08JW_Qjv1z0kBBJ3iJDkL3cIlU;T&gA0k*p{cd^7K zo{_CM-p1n*lX!*^GNXJ>W-Nn$RFkc}*8jB)!$~0-H(moOa*@*%Q~)udnG_WeV!Ta; zEM=oOjA`OScjjr2{4c!NBwIX#e`TB7?x{oaRTSUGQ6XVv#KjK=^xS~ePlC3MI${TI*lF}t@(0h3&g36|y!470tP@B}Ue;aNP6 zq26ZY%&YWR)2Wen{TrmbeQNh4HfxLM9efWPld&m{&HaQjZW2@EzBI6QUdEC+N#gVd zHf|WFfG1M*+}4MG4u!x*Gz2EmaCoHI)XJpf?l3;Gr+y+yx#ID)Ctkh-=E^$bnGMvM zg5srS9uk;$CV;~#ephcPtcvFN7S9$=zTsSo9IH6eB++DB6vJtpE8?N$4!|y$ITpQ0 z?evE8IJFXk4mpaRAMIlRR8A)C--MEGd2@Uj|zw43@CU&nk!C$M!$B?ID7Ka5dL^3B+n%$sP%ScCB!4P~zUzV6%FKOY*f@w!VQ6iahQHO8Pi04Qlf)GZr$SA?rdn||>^C!(A*&VejS?(ez0DQ#4J`&? zxiVsZ4Lb+gf(O)f4(O6XliY9rbhg-i$YwcogUR(}gbAwN$lGLwYjsX}f5|oux2xLaD3|dsc8JVP@*({OjCWI-#E=$^_ zrlz7`@mXha!#M(Rch}I(l!Z(Pn#FKCM_HVIj>t*Tln1ZC5gTpwTmoQO_Oj;RLH$^KN_Kqo*r==?74mwy;;|dj8U;~e` zLO!#*F=p^aND6iXa>ZQBPyjD*GbG8KXtDd8*n4@r4&BvNdIB=v^wqzM1#h9moG zGg0lOYAIR6szR_K7PY2~<)sp4mUbO~Z5X|38&_J0oQ&ms<@8YB$XFQ9VrWU}ioQlW zl%L8%sY$SKPwabOqk7DtGbS2ttSBw@D4lo|(hOab(>RrQg4&~ShWDz5OPH$0nk`M} zj~qH9XDp|2+R$>dqr!uBkj^WMa|Sj8T7+mo420p14TaqeNfsj}<)(%sOaO|1=JvX& zwV?^3WPv1&Jv9uNoGJr2V;N?}adqP$0@Yhdl~wzj7HwnkKz05~@smfrdGYT0 z{N!wtKuQF*ViI}`DRMjGxVOt80W~+Jva@hr4k)d9Qm9m*s?lkrW3}6=Lv`KlS40gc z11y<%@GB=L$PJx!|TX!qLgkcU-b=^Vay ziMfYl?!ER6P$V8%IwM);*mA%se4aI`yG*sL=UBOvZcwvJqtZt~%$U&DD)opgp8-xc`rHzq~CXVtdB@}-;kuL6k6~&{uXPY-{ zgGQ13cP8~67b6`gdg^M2R=Eic+Okl0Ts!LGhvy6YZF^l{bwOeGh3J_btSdRig$?)YIg#=Pl@&qC-q}1w3xiu2J zHSN{7j8x!;T|FPw%%)*~uZ=E8T(63_);V*P0G=G$vF6zcRJ>Il7dBFN$fqbuOFZ(- zGDcG=X#(DX?18eQD=&bqYNbFCkoQ=gP9B;rxt+`rqT?Vj_KkU2igb)wju~sA>B>s_ za>TMB2SvT1YkgivHB(|1Ow&Oa1c9P!q`i;${sqtFOU8Qx{AI|0Q7Mzxb`0vBWFs&& zQOsB-Sj5l~B(H&UBx)d(E=->bqY{lXhKN5`Hv}{?cF#hG!~@|q8)6$PS9A~M3+M&p zS_>dT#ZhZH+SK9$k=w1nq2hF5($FDCkt{l8!jWi1!xY(9f7+HSDpg-0t)6kiTAh@d zYVt228kSMPQxn#IiE+X-!*Die-YLQ?9;%}-N=H17tM*xaoAkLUZcxJlaHsYIavBRz zomeKH`k7{OU*_f#n5housKA2eEmbT_2H+GUj2YHeu!cP_b@zpx>K4?uq~WBjqPkDV zx$qBnn?M~ZvU%Jm=lxCc^0#~0V7q70)FMd3Gdv4t>R6rF#h}>nU75eBxXS0mYvRI1eu;7sjcLe;i z6xXqrqP08MC(hCXx2#SO0(MUs~lUD(aS z(3k>$zll?Im=0ZaEe2aled2mna@h$-gdKAp-NlJ(JG>zx=d&3dvj)fnMXr+NiwwCD zO-4m(g&SakHIX3D`6COx{lI~l*lqwGN&&rWh*L0(F`Y)YF*r$8J$^1ktTsUhHz@FU z0}can97>)bcR^ELIkMA5!?_ecPo(JXU)QpKvv8@qO-rHSgmbilf~C`Qwu8-;P%>gb zPa04VWCraeaS<~#gDUDb2fSm`G|tU^G~U}p&zZsNx8CBLsGT`~aXp zU%v#k3&hNsX2#mJI%GCv1w5pBCeX)wbC$(mxRx|{Q}PPm^QnSgpzj+MvGK>Nozs3- zW1g`_4w{9N_*PR&>rDJ*KA+EjCBNuSr6n)xukg4+lE~4SeDrC!0ik3|K44-lx4)Ml zv_7KR<08j{ty5w=R0EdcB9KOdQAWgZf3$u+1Zx@fOm-TPX|B=zp^#G*|J;OlbwXB> z&=<)bD&}m%wNiTfrKcAQ?}0@U_-_=ywcrig)@RI{f<|)Fds0_9BV&Hcun@Q_a4Ktw zmDfdeUBTBL`~LW;#Q&QvZ=Z6En$of0!2X=}ySgQBp`%ct^PfMh!7Wy9@=uhm| z&bLiD;_G8~yzM}4`vuS0(X(gdRmP{@B=>{%ez1#L7a9@e4(f@!g~~}tQxrF_Qf7pW zG-!@8G9;VIvrxaoQ82fZ&m@;uj>HU1kgzf4KZckW-e^ zI1w8g&&Z!`8;5q#KK$dxGyfhOd)c9g;QDp2c&aX9a^Vs8MdPClDDyo_xZjat5|1az z-0gU!wvu$cG5{j5?^CyXnaSZL&z@bpyFLSy@LN*bPHCn>{kqMsoxxMD;R9@ib6e!1 z6=itx&phlvItQmUjWs%qQ))p^bWw3o1yAi8Z7 z7lfR|EX_AI27|$nipj zv(LA&CbiTGVkZntm6nZ)4D}p)aKT15AQ$|uK?O$h4Ne}zxNCx=K9SoaW8W>D1CU_ch2>ar>iYg$#pqlr|< zfz^!cqk#?X8Pf2E6f@+9P&&w|a;-|NAP>{AUdpGhNm^06f63mPP$xCmkf^LC?Vm@v zgXDtKQ17mM|8Tdzv^e_s`OKe^{ujnOsycO^2)C5MZyDnO{jc5ac1rr+e(N#+`}2IX zH2EHx+7+YG{D|aP!8RP!INBh#v-lBt^&3AM#4Wde1hzRt6l!gj{oMk1yp)F6D`g6@sZtOq%5KxZnTz-PLKIoLybLyE;Pqt7L0B z+6h=5cs|nH6sFgudz94U2#FiEkcOOFwM+hyt#1GDe~BaAkw4+Tu+j!Ts>@{SUCR@p zkQB~^<=DFNwkXtp1j zE`H(jyfW8w`QBH+qQ808`=yD{p2ys7-+lID{mvgu54Q7c*V zIQjKgdn#(Dafk&+v+PtJ&*S$!5A-jV8ocRaQmx=h=cA4X1Ml#F5WFtRZ00QDe=>-c zKO*f`>utPXJX~pMRJHsShj#a72Y(i3e@neFYZ~L}fh&FW%804nZAygtb}+Nl5zU+q zG?=)|;@dc3V|FG&iVjlCeh{(g41-o;=FnggWqjuRDR=4O?E1si@z4H!Ab;Hylg-9v zGu&)!0@^=S`wZUm(hYb@Uc4aQ0q&?xhd+Y+KL4JCG=K1!I|w_MT=#ntapf)Rf41Zw zDck(p$``GIOx`tGh>sQ4SHt@(AM1ABI*d$lvc1;Sd{^dx_SJUhpyu6nOTPT=t5GO9 ze|ok3?>tTc*4YD}-zH4p^uGlf@f*39x6rRyoOnEuuU??QnSU9H!X3Mg3;cf4#J7W( z--tMY8|ujuT1>#7s--Gl zwqEf37{c?-9MCsHcOKQ4Pfdb8w~=ClxPg2pJRK?6xp^{@?xHyx{a|B<>BtaTd{r9U+pPJP|>df918XKHRYR z$H2Q`a|=%}$W!|OO{%aqest9Dzd348zjs#5MlAb)VeKEncs5~~fL!M9&Hk~}Y|@h_ zy4A7g=|*t)!!H{ebL33PLF?e)d-YZAVL(yplqQ!v%U{cbG2}yosO&@CD{?jNI4lv2 zG!3ZL@nG?=__#ZG-MFa(f2sD12X|oo4Isb%`Y~%?Z|UWgufA%9T@X(H-&_CddB$e^ zMexV%|2tU1w?;*h$Tv1gAh&ttC!I-T&~(vqEo=#meY))Sm<>`IfgqcK1m!*fmvM8G z{0lPe)10*AoGNQ^#<$zhtZYovZeBbhTi?mu{c!(b#Irk^MJz%(e?)#DPoHj^`y{t9 zCySmX--&R-rcA@tL#n(1Q^?z-w(_yCk6^IbhlG#G&MnP$532-HjsOL2Abzevn7u?u>=A zb=&^eZD)IDD|og|e>|vk7Vlt-#vK5&^Id!AyABy&lpm&^k&N6;K|Bx{neaPb}<3&!$VXO6h>46y) z;=_c|h-HwbEN@TwnUM3Fn0=T@4nW9%Kl?vB!85W`2g$?Xe?@l)u?KI^(az4wx9wS3R!fakrK>=QgpWoEr;&4!a4CZ2c2CHEH6b6W`Rb` zAxAqq-^rWrPXs9C?={rXcZ%@Mc$Sw>raZLy^Vgze|)!QkCyK%2RvQ zzQ8?m(qr_Mb0_{?`+H}~E?@gf=)?o_x3mA%Bh^rbe?#&(kX)cn2dW$rXRsCMJ$7!u zl--LT&oBOq%y<-#R~(y-s`7QWx|X7w5DPhJP6-raGer1-(P-qHK}%}dXXJNBApgQt zQOS|_r?WwHX}=>aRX?}@-8D^4_gr-xHQmE94e#TYrRxluMOBt17dG?PLr6}uG0QK@ zUuz}me~4V4T&nNf#eeB&pX#R?|CPw4eai5!hvL7x?M}BG|JB-ijQ{#9pRXYP``dT? ze&L{Py|?)Dl-|FavGl}twLK#3z19X%jr8#;(;8P>mZtRn8awPCk#4J14d(+*>Vy|* zenfWFcdJLt)dasi95L5!Ro^;T7UOqNit%f=f5w|mV$mnxsCKKhk?|sD@M~#5%DZ8f z)oHb?`B3deOB){j&9GbXW9g0d=Ef+4(zDT*7<ULpT`zgLm}b|2&aKhNh;{`>Uu-@_8y zf2t&Tw@h9;OQ~_M{P-IA;~Rvin~%D@Dg!}_D9(d~kKHfTe9K>JpLhLBT1_`!*P7QW zKLHc3{RO+lHQBxVU+oi>dAgN%O4HH8ROKb}L-`FY0#)8HKa}4^=)b{^9IyKC$JAC)n8P<^SlO#Sbb)c=EZwg2_DY41__Uwu~}mH$WO z|MBzHeQM-CUH_yR#n2@=`mM6yL;8QGSCaoat-Vg?QU3cJpJjD<9>uaHVwt%ne+z`N z$8rh3%Y?u5efgjEsnUOlN^8<0=NU^l{ms<>Vg0{buK(6)b#@=^zn|sv73lwu_1_-r zzkSm@>6;gn%4w5+l9I_%QdwDA<%9eFmO2S(_}DMM*p3FvBO@`Luj#`jnxedw=So_8 z5Ie9iHG0A9oOCjX%IC9OT~>_Ef7eO02K0%WQQ0Ir!#Ld$6ZKa!RR8#w0{?{E(L9{! z$jDJVp3)f!Co~;1J%t@1VZ!s)%^8WcsYoUQu3c_+rKk;C0d*lcy17Q|O{M`u^#oQ26nmf2#bKs9st{YUxlb9`2>|5x<@tW%ff-&2tJ;-bmQ-v3WseSJ*<#r%ABpF@`( zNMzP7g_1j=vx-QQ4QU7x4Rp>?vr;bh<1hp)F8@r3RC|FV^}Q#r`3+0;%2YC?eA^7F zUWb0K7g)C-XCjBcbI*#{q{%sd&250 zfJvxb64%_V-+ka>{X${NH8fWzx zPg)K5MUHumvA?9If3@tq^t?XfiJ2k4$eFD*`j^yEfA3?^Yx^?Cu-52bOkMxI&j1$o zWe#BVp})Az;`crUZ-HOtG}fQOmsW@W?LJle545EJto2_ytxEn^Yrpkq|M?uBuOR;G z+mE05>hr%2K5hC}``fUp^(t01cM+YnQp48U!H>!e`=93ie_C-<|82ZvAC(yPKgLD3 zk>#Tr)8UuqfB){Es{EHT@hP2utH^(q{;zxcdyn~FpXIZ<|8L6jEDl9U22qCj8ISsP z9WO@W$BY-V6No0&?KC#urB|HiZ(}i~c{thp)B^eDntrp*b_=Qm7$LTmo*ey$rNSuy1T8wBu2|(r}YxW6( zQu%r$fQ6C#E>H4){zt(I7Kj<=iAE*)yT#5K%ce23fBq0hq_y8`ZRlqtj37>Dh27PB zK4V7&OXbwjG(hZx3-*EW(GmIXmyM@GP*i7{x4!9FV$N zGTHq5D9i1YOZfN$uE5Q)z~6b1&x$;7_hzO#>*Uj!+44+f-CQ<1U#7&tXUfP8GJo&>;Au_NZvhf$M~<$@=+}*Xd00% z_Kycim!vpkGaZS|ZL(GO@PwYyoSw#+PBntIe`?SF&}>Q`bPgrkXqx{){^b+ zl;^cCEI8V_braj{q#jK?c1Z6}XgFd0_@B%);ah#@s=EhWznUYVx6b_i!r?OOZ@GDI zN580!P9FuS>8!HeIzDz<)hQqvxCgP$e=(TZue-0$T_`VBk%in*|3%_7@9mObe|^5= z0kZI6%u6+^(>PmYjPg4GzXHgNvHE^VTGH1I`P{}d&&xl)_~nTpCz1nAfA_hTw(s@SLD1aU>420e5cAdyw;^~&_#aTm(+eme^g0F z0(2R~5)_Bz>(6&QNR>I)Q(;YpSEj(;!W7>Gj-}X|c42^vO%je3&l)C=jFH{PB9GH? zA6VZ>LgT613(t-y7fZ*CG|A~arPDZs$SRkW!tON4EMrYniX(XB^!WN1IM2)DlRv+| zB(>aeSDn&xhj6V!aY*$8Q#X|)e+WDq5p#rOIiZ!JfMoM}DD(FUL+NLUfmj}kcy0N< z6L#}2is_JL4e}i$M?a7v&Ew=@mifSWrU3KZ=uG_v-->_5X}(=*?WeDMF>F?MeGfg)FAa@>SK39j{cHO=xxli7a1?o=!y^H z^!bkWgMA(*X}+JaEM#d;$Lv+cSbs)C_I$^G#2rd6=0!)sH-FV{)7M4Wc6MIzj4&Eb zh)379!0xu)P@O>WNG*HNe_iqxoz6PcaFGOJMq32zAvvRjv{vkJ^^S!q*~c>571(Ao zObU^+tc<|s`zn9%5jxDc5Em@Juv4c#sp8 z#w^W|R(786cz;(PpT=o_Ml*&)ie+c_EG%-K$yXQtwhAwI5OX!ofBif{iO+|Vu_@0D z2=!Op+p~<%SeDPhod3h-KY7Z;CfZ7V*o0d2jrzAAMT1At;9C(5Kn0MjDH4Q6XfI5N zqrh2Zz>kcqxA;A9Nf$8`AhXSpXtwJ(2RtvY-wT-C^Bws2!|PGkpWrQBZy~i*!o438 z7T#R4EauURZcE-~fBUy{yy*2hk6z?VV9yK*sQrA${$ih24W(bSTdh{>`Hudxvn z-Gn-7g-v(^d^i{;JiIZqE6)mMO>TACf5`;vTVr{)hB0=``B>r-YT=vPAN^|cw)5NG zZelm`O=HO?Kf)tKXbZt2^S5)^zvS9|Q|zPk$4fB6NSuEd#q==NOz?|^bPljf84 zAd)<3fTG8PQw3JME` zT7}ar)T-CzJoHX9MYnm|U5n|6uBD4zs{?;50Gfr9_?9_Z^zqMq((jU=x+^KrAFS~ot{^Bk4Ko$p-=r+p0a`_Ieo!NH;C;MO=m1$yFZB0Qugm-LGwXxiQofv0 zS9_tB<7n+GpIDb$@4SBRZP8@H?2Dun-Xk{QUJgu#RIrHfRFji1iCLPfihTf!%!+Kr z1rtVZ)XMYH-PSW%yxj|CUbBPd$Ip2X)ey+ve?mh)B`N!O+uv`o`F(m@-}+{xs@q{D zdbOTLw1Q%|8jD!Zck&UoniYBJqus`A!=v5i6GqjpOBR=N@JEB?BjSHUhz~vE9-0Nc z6aDT_-33y>!vc$I>tiCfdC7oi{#SrUrKc*V5Q70LpL^S@zXYJp6VG?dFRPqzXv||C ze{}mb!1tbdzT^D5)TEZNv0gU*Q%MzLZ<%E)&{~F_MYbBxAohTb%VGxf1$0*+zW@+8 zhWzWHT}4QRV6XFhR<>En?<_q1#cCk^9QQJe(odfvIp%HmOi!ejQ-5# zSNdXjGcZ4RBie6Q9^phzjKAd(D-U6R@y2kzUWI=A4>A51f?Z}z#N#wh$FLvXH~O|Z z&-j*Anim%BM6=6~T3G`v1?~f48=` zjT;Mp*RR0RN^EB%$#yo`)~V7emYitUwp>ZhZjR%sln6;YrbsPG`BF#kZ+{;Q?jWd( zFWI(dbI#MXNN_M1%nSyD!HlJWBJ8%eD=HZA>CgD~)b(RS0xj=t{mPf?c6%E8S8j@b z#1}{%^)3qnMhmlh^Yw}=*DOf= zQ#Vd6R<8DncBk3!`ujEdo4Rq@2=D}lsd~Lt_G&c9>Dz2|5A)M{sBynjjepHzTj(gwW`qQ1NZ_^3w|67|*2D6&XDx)=BXk$m4EM9oHle|w!yEG1*UGkKk0o_8&fIdY zaIC+C{Qd_)mg}9+M7yiDn|F8Zi4K6C`t;{=l(T4e> z8%KW_Lw^`Uk9)ASY$;J>$er?vxfQ@o`fAdDh3QTeW-E$Nc4zPV213K}FFxyzT)t z7}o%HN;A)Q{(jVJ(6o6fSechzNvgLp?30ln-q^rG()as7RMPdGO1;|a-cB`YEVQQA z=&qAj-q$v$+*-iv^mvw6>cQ5dI?UV}5$p7R$nvGzIulvvfA2AEZKYBbf$4gleXeMY zin3bS%GwZ8C~K`0Y~a5ag+Ert|AYUm>;J?b#cqnr$@o$FSh)WG@MibNS7!ak*ROW} zsQ>d#{%w2*l^FVNGAjz(v3;Fv8tjf1mxv-`RCQ>%IMbK|r4B4Ndm%Ksbr;m`Os|I6cA zr`u>-zZFH2$UybaPg?axuUQx0fWG&=OgBH(KGi<9jvI0)?v?v7n&2$RdaYY~U+Xl0 z@S?qDe>Y{oYu`}v_fA`#?uT}xQ+1?ql>cG9-Tc&OXS?%a{|d+X->K6hKAKskNPf|2 zv`=csmZ8lO9Ea{?!SHxJ((JdgNRjz*B=rXgcH_a=inQ^q ziMatF4}BdTC zNlFZ_H8A6v;B%t`GGE6-KV(I3{SdL;4@(T{4^T3Q{b^c8opCe^JjDDrkT4X2jME6s zLO&&#K*~3wIsE{mS%N*(zeRqS_@i+O#d~l}n$wK2{AkD>5M{)wO~4+TW|O}5g8=ne zf1=s`Ez-tb*jSZze7Fh|wrlU-H@nAw@6`@KJ%Yl}m?(DpeLp?^txS=lDn#caE0E^t zVi5imNPftQ3?&7?kaXSaeC~7`$Gy+BVX4|;@+HxJ@-5Yt^gUIN zW{c)Yr&~KZYP4B5!neVCDr}$+LDY8xe=^HS>V|1Tr_m2lf0jTABoss=e^7wof@bS< zYbW*EQR}1uY5GFX4WbY`N(;t)K%%Nc2%)~^ilpjr#Q}&kZc-w;!IqzpEyK1bp_-tA z)ICC<^1mGUQe%m#qy1Ex@rkTD{4b|a;NW^kwfBvqsM>Z=o$_nKGOa1S0TAN=67Z)HFb$&TQ&D^?^BSMFTgRte;JseG+3p>+4r+v^ zRY%+Y*7=uCfylh2xfCJNRnPO|9E0Rzt#Lw3LBlQ?yKKAcaGYxOdav8!e?mi;11frx zeJ{{4gue)SF0Ii+#_5KLr-&9BoGIA9Qru0j67jL?6h76CnsurwJDH|MVeLAF!f~V1 zseO>5$CXOe>5efHP|?5*`7h+6qApNEm|vnOhS8Dc?4E=qW?M{OmM--OMtJRpFf>jSV=Qc%vY*R|Vt@5i+fkdi?8n;FZ6Kz`5+tvr zAP=XmA0&nL>2a^#crSqv+qhS69M;Z`x>cug<;In0npS{}6{$b?e|MZQGO?<$!(i~Qf) zw>lPbIa{s!r9HEvmc0R86aShd!6fo<)uV>OVr;VO=VKDZQ&!^j@RF3K^JccOG_@pgK>mPA<78$e_8v;K^y2Wf;7p@gMLdd zA@Cg;NIKtG36L>PXEE(z=oc1tGK+&UVz}+2a|^~7PGMeXH$F6e{kIpf@(Wd>$}gT! zrphl&5n9&W;|Wdqg(f4*I(~pulwU9*SkQ-Hd%uyE0rB4MLOyWMK{?E1bLUb%sv zU2cdN2)j`>e=jcZ+c<@dlUBE}kD6cz`@BXZ*s|1Afn_Cd`#310jvo%_Iy1pZ;*Kzy zxVL1}Ox=1!p3mAx1kz;~U5D8PM|e*%(j^=x`T*ny%X~@Scqr0^V-7`nZ?vH(A1rfi zIfZ7YC*7$w5Y!2*m;{PJq3^I(vzINKOleE3XEQGXe=;_u@>;mmR$}rYug%| zPR+bs4$m-mwN1Ir=H-*6gR3{XwdPSrH{`5jTwv5$T%1-ALP^*@Smrh*n|o__QcP`q z>cXA-=}L~*JQNh!u%Vy}W$vddd2J2x%mpT7bORkQ=OcF&{5Hc0I1dC%aHo~(Ra5za z?XEWye?)z4=n~(SiLN73*~e%Y%|eemEetmVE6+Rh<0Pe9_*v-T7*%jOsPH3>it58- zB(@=SOuL7p5JD&-fM%<7k3v-oy{l zJ9L)d7#+KzJA&OgdWSkVB^oi2J9LRPhd~J^pXZW`9c3BBQa#vwH{_o8;+^iT zf1Fd6BnqO8+A}&Mti(M!N8>0-!GJF%=mJF{aznwGl9fARYv zB3nGvpQT)xwoZ-+B(nCnDGD+6z*(L|=n~^85jj6aV;p1P$1u7sqXW7SO9DHnm?g%h3KNFnJrkULi?Cz7n7*AZ} zCdk9dAolz8@MujpEc3zp;plsIe**HoH~}g2MmRl&akOUXbe6W_Pj2kH{QyI(*VZqOO zjXHi1+#-j9B6C6JfYN#&ofnmUF6dWB2R&l19w2%}M{VJtcc|jwt4cTve*(1k?)fhI z`c?E={Hr4zHRs4F(&rAUR*|#gpbOJQqoQ{R771ekf}ssF@-IWPs#Dxj+ip9H95z>O zU;*)^c&CDxOMlaM)Z>)-BcBYQ6?t9^8iiW-%WfY?;#vIlW&p~F}jYGkq6F(W0 zhe0&*lW3R%oA%vgTuJ?De{T>4QJnPL(Cg9VfM~12;2^+mTy?(S^PP!P7`%u3o?r0! zt3SPc{r1hD$+I^0>a!q#$GdM|?UHAoG10mF(JR*Mee93Msr-KT&p!gtDDY_03DGmX zAR3Ku`~j!Ok%!yvwcvWnq{nd5THJM{E2~JJaR*lOsBIt>Vr$ns09F>K>=9q3kdNaF+!SvknCgMg3EUQiGUX$rKNe8zd1* z+*>&6N{IoK&;Xl0MgbnCr8M>@6YPNzkh1dXVU)_Hnk=l*e=G(bEu&vB@*<-DVyN~D zo;82sr)7nxozxv%c4K!S=_=*=S?SeILdBcR>+XJWkIw2`m6+~0#;#}ioP0NN{m}e? z1+{l?r}j=?%RZAa4g%5V1|W)>bv|%+3L)D>m!}a6OG;GKNc(0z2R8~dh(@Zmq;uwI z&AI6Fcl0Zwe}FX`+WhJcbo9KqD<#APLucP}Q_QPiUq|s}f;@jX#4!%j;8vzEzaHas zOtv0i0)99-tGw^j3x3ig-}jQ)bQ;Ae_NqGt09Y!q+q+itA^X`bu|+?eoN-AhW}0kk z)9z2m-hDC^q=Ti~Dc(^qb7_L)91<0`D4sL){j_3*4b(~9I0e~e?`sw$Ng$N| zjEWq%!%4UM`k&~z%A8=<5`zxyz=+dmYL1v`fBnH=7RO|tkW`CX(*%|95Pe0X7*A2@ zdO%3edz(8@*}*%md7eT#n5DUJDE zf5$Xd*H zW~&V`N5_qBE!&L+kW<{sSisau+t%K&gQOz+j%`~sbwg>;X|z8z4@7u3s1e6#({0xd zXk2;b&Bu15R8dXYv?KxYK4Jg70K}B^;@)seI0SK&)ZB#-l%rDy-a`vQa zMN=FdiKpt=0)wVa-EkNe3AD2^q}P zO%m+AL~cKdQ$x-q_|BUW;_w49c*Q6n}Phh)6V14~+yy-NL zjy`807&KDo#P!?;I*2h{PWX(FH5YjihM57ExaRBtYf(xqk%!*=u(KLa_SI1|;=7f^FtNXcWYdsUEDn12f6m}R4wM?! zBW1Z~^OX^Slg2Ng6zEh*uCbY#j-~``t0HoWe_OZ!t~fHH zb8yLl^vtYV`%87Izg~0;?iSVLolq;NTs-IY-91FBI77;k1oCe`HulemI?_Ro(wm z!srW3^eZy)&|?0D&3s`_0DXut4%Y*opjNv zpA_#nGA5s}zBv~ye{HyGkN5az?ORSpBa~jwjc%*n0vj8-bWXtb9k{_@77*Jnz~Lwz zBbLwE4T4*gMAX5MfXjw7$Dv2I4VTCb{7Y<^oTXB<)<4^z?2N@sx@vI`ek!UtSKf>4 z8^x~)_X6Pv`x=dLFip@bK`~G1&z;=Yc+*2+6i#;u=qd_o(g9I9*5++fA=}B}1hr>YlBto+UGqC8NgNdhFy8lEa zy5HfrCThUTX2Kz*5hBgY3=I3v$LN|M_7c8P!$htz`rlcSk|Cmrd)tQ#vBOyaE=A^K zB^yYxxuX4fe{i8T#mw^Om_C2=PqgKSNs3($MMHS{4!u+G-+pPw*iU%yEZf&g60sio zvs4V5MrfrnD3y+bPYWM*VWZCS|3YS4g3%k&6ATu7QuqcB(3^LPmx_0B6pa#NbFA9z z8{!y;@+NyIPKfV@Y`dg6$+S<2;qAxO;+E&rTLw;%e|+R@^Jdy6Y?$4CZGiQ6j!GVB zK*8}YCzqZdqtX{~H%w9R^!ThWa-D2%QVB=1X1!D4Or^e~A0%NWBM!~hGo`YiNs%1P=D zF7eG^f9!@M49DRjRI;-B!|OM%cPlf(!zBW?Mk zJS>s!OMX~N$GDWZ6I@FDiH0O>LEoWwTPen<Y|Zm0^XL5YPc%I=Q`v-{v=_xY=yP7sA?c3YOMI)_ z2x8nnIzRePe#c}xwatG-QmN;-4vRllIFW}Hy_!vP6 zGe6{rFew}~3|z8g%|d)LrMj6fpzP#KkZqS^?~E2us^pjz%I6220J6Ms7F@p#0Kt>eaGPZ`^lTU@FcIB=IWh(;Br zW6MD_qUZ`0zu+rJ^R>wZMRV$9Fuk7(Z%scPs6`qUN7sCIW={0=CZujikQ-w(fAyz) zxmSGM%@hrYN+njVcK=oeQGuU3#qq=s!O>}nT_9T@fL+)SW=Rwxh17I4;tK>jRWNJ= zWiB%jcLT2X!R0y&X`JW?r}U~euo(5bQZJ<*ZD+K*Pqf{4Inj3DVsT)}7m;XinhO1W zb|=9{987UsMx9H43Q?Pc9Gntxe<|6}w>z2u8@TgK0s|pL)ZfzUG`c$Gcb{v=M^t2) zZ;U$DvSZ-Bo3=y>!C5F+!0^>N=)cwQUo6|gJ(+z+FwROprMJEWdZ}G$o+ef52!1)!|Hw zU(O;Ub;H{ff>BFo0ttB3trE+kfs}}YjSW<1*Ya?9<;PJ7VcA^t6S|Y73sgErKOOdv z8$|r4FpmhFPkd(}4X{gmD^vPN)5%TvuGcASY>;o61;tU>XOF8n|H!+a4pGB~@sjW8 zr$*K=l>N64tT2v}RR4lqe+IFBLB~WpDX`%-!YjkI^wVJ@ys8$rV8(HH#e*9DL5Q0% zi=;V{SsPv&&S99sf9!k-pOs#*ST_#P zfqk87Er~E7g7#Z5lU&a`ibm}yrHVu@$_g*h!zSd=uy|CwJ3Ko%IH?^s&UY^6eAS$| zq36Pm6a1Bl!bD`TXDUt$4GE$Vd%WlHEhg(j=vOjxJwIwqF-0pWCu7U1>qor=r=S0R z>ZaqIHJ4fKl$Dj{e+zrgEUnhwS6cJc{+S~p#=p&QIJm7kp6dsK0R|N39R-R=3t|9JEdj z3%nmDvqiyLblc%4i64%lOZdgGEgBT1ADZ(CPs1qnhj=ngDYz)`$Tp^^@hDX=Zi?{@ zjYq&YDN%A!e~1YE#cS%qM5Sk+OV1{yXEZkll)Ee5N#f57^MXFYZKOE#igyZ4i0y*t z6S_B2#f|8=h%1j~^8U8tPViZh2?C^+4|!o~DH?AFubTC0am)8mX|`=|aN?2RxvfYf@-Qw91wr^_UyE0sAMkIf$b$b^Dv!blXElLSu2L@l9T5;5mv* zM!0botUv66=*MW1S%Q?MX-|7jsnfcKNZ#TrA+Nt+VFk|;3~{O~*xDbWX_O>BYzmqw z&o42Fe_~>c4~Qj`j$P_ri9*ahEv=5eB2pT@2bfi-sNi04r_)M0_G7O!b>sB5B3jG8 zoPyysaEb=ZtkIU~RDs6ZYI^)(A{EiI?L{V?V}uxU?aL!;}Bx}sfamdgjIVMDma1Bf!q}Bz1n~M8jYh_oES3D3{xCm zxk1%={Tl7PLNDO|ii3%6iO9s9$B{=gpk3#h=!s*K=1{~0N>C8|_Yz%$OPo-b2wZ6f zf9Gj%+!dpE7KZeM3!O9OzDlVSbhgn~BmkISm$H6uwYT+vE1G^|197V!D>F}Q6PN2n(&*6j1JYy= zg*5qNLUzVvUie2oINA7VQ=ebch2zF(e**r7Bnrt#r*7;{aEfCJqqw^d2e|y2e&|kM z7hl}dpN}K@ypE?h^b!<>Xaw0}_fbgpEgKu?Y)UMMB=futqLl2TlUkv`B~2BR)mOHa zR})z3GmiVB@;1kx)YRSeWmMBh1By~G?2zU-g8>O26B|^n2F+di_Uy=#x9cMdf7uR) zVEa7~XX}>mPC;&gBaCiXid!>6yKNeU>&Dl9xWM;#q#$IP>0_6NCF4+x0()hoT~434 zAsoNt`}mA`m{IEyED3E2=!?~<&)Pp4!AEg4n-aVB?uY#n!)C5e2vs1|6e`}mR+RRqldurZD6UT`$KhG6C+1Ll zC$SW1b7;ap!4h(^?S^BPQ*$hkGWkfT7zp~IYkfov#95q>!PCN*4WZrvyArq&5r z=P4!0UyOu~YNQnJRKm=y21hP#XQ68Lal-=3u>1Zy~9Fc6x8M#Zkb>g!{I^zsy>!1 z;`#1QD<D^PgK1^!PB?@|_C8|c%R>icAf8LvE6sPE*)~y{iPino^S+`1+J!>_0*yV-Uv;&LfQ8Y^KJ%qL5 zoK%H?jOJywz4pV=qP$GnMMX0jhh19aKEQzLTy`y%1Fc>s?aPzr%3iUc*l+#~2S4Qi6wz)#r4+XPZJB2hfM z;3sWryEWlmO;ijuj!i&~>{g3gR4paHp-q~s@XOcBSsza$FWE$uX&enIyOjYLPt1W; z{Oh&}XHSD8e~=Lnh^$v8(Tpt9!^nWc)<236QQ;7*Nq_H&-=!gPDrJfy4zDb0qfEHL zX_(Q?Mw1h603bqck+$xv(`Z-!{O3OxPChhGex*NmcYa_$KeStCr`6q^9}B$c?#|8* zt8*hhJ#L+RXvv=pyujbZSxIWS*4wPwx~I+r_z;vLQNUyJG6z0lmw&~cKn96^P=%Ow5c0< zj7UVNf28*iGz6n}#-Q4@^sipuo?F-xPUk1oOhJyIyu$*Ox6ojUig(ID=M*Fzk|XX) zgWW0bf;wj*55N@oz7$zm_X$)8L8V~kNTRr{scGGY`NpVdwvS-*L~hLkmd_r4F)@v3>`4m^APe`k(U6B(Q#wA@`E@2Jsi;nAdrxQ+mawLg%V7*BTo= zGG;8^nLy?R?K9Q)rQFNXp*h>SL=Vszcf$ktzSCmujJ{0*ndsj8x&1($dnZ%&m}+EaVDn0(d-ftjYlhhRdaUa zPlzfpOtOg|9=kWKDGq6%RPkTFHm#k*u6pFY|^K>F@F^z*pMca38L%obkX4}M|SFYkUjTJ+FD2> zTD275fwTbD!ty2Lu#O^vDrBW5OKZ&X$}1OCzRS1?^^-6iw8E)^on}keNpfnIr35@j z^*rUZS&_Ar4@~w_UOkY-W;%-rz`qBZsh#Jun$qe~*-gNG5X;RA1+io$hVFK!(|@{~ zx^Tx>zi|ovE*f5_+I-Y+mQEn@or7pBD85 zrXmYd2vPBlk&LcUidjgXS2e;^cz<*9mx_7jv;*v5*48dhLT!*S&@AqN)IO3QY9FYV z1qEb%t90o40rhS%%2+@#&g1uR3Mo#~Hg>&Vh)r{fV^*y#V@G&?+)JY#m>Nd@dV)X| z?rI5^BKEiX#hl8gg6pQyU{kmQC3k6SJ3+KZFYD`HR>+&uRH80|GokXgh<~2;hb(&@ zPErUF77*uX*9`H?T(zaZlG$K@v4_1f>S7v=7h~5em&+;(4BS))uRyn@e4DTfo#KNf zda5eKNSRUHC#5&jHfijXaE_`~kc%}gUzm_Mib$rq(X*73qJC?JFB#?>R5`w|edGU^Rr0m(h zL%E`B^kBL)7w`r@LZ;3$D|d)9yR=6XZnd~Y{zj#v0dmA#?xSbnOLZvw=$ZQxVgllQ z^z4j9{OqG=9~={1H5DnP;REvSI2m9j`RlDl2V6D~AUchsDUO#;LVp&hAc*DdaWsSD zRLKJqNY>(B;dN&INywrIMSUTQ_FZo!48@5sMe@}6#02fe7>%OHV}}!GsUP_1EqAWT zrA)182F@APjR%)P2jl2EL__SRa4eJ*a^qOZa1+AtGwEB*^ir^fuC}8{Rd(P%yoO3R zPe0IaeDcFS3#nW6oBTN2oG@E0%65Y^gZc!84L0yLA1AnC)LP#3be=_( zNeh*q4EuctIb^&&D7J+h7zq7Q$imkBqHz?Z8FQJ;3bC$rTYs5!Q7yhwoe~vC+yk0vVq!hAoCn(Y zAx}tM4I{2_S$}CFYZw6X!>i~LdsJ7R7a3$0Ld&;f>?z-hDSm$52fyu@rg$<1%SPR9 zi0`4fx@D1&bB}i@5Kr0==kl`)#}HizpVq>NWbE=QGbkCZE}S*%l@BynnkTTPO9q7b z%3F)-VIz<9V;&dwR7Cw{Gx9XtIPhtPHa~%zI_Mghhkq^hgr3+#yDS7^2s^eiI*G2y zPe^Na?Z#nJMhZ}Cd`)go#Fpt}TA78OFv$UmU<-(%Aw4VI(4xpbJH>K?Yg#X6f<^8Y z=*8uEMj$GMUu@&Soe(P#Mq!C)C77v~T95(27UM+zP(dJ}CHZtS!}Ws%Z6y(b%p!YS zhz*Cx4}WnfY8XV5Bnri%fgH~K`LUd4gemykAvh}@&Y7ic5HT zw;wuWN1G%>!{4eHF-5A1U>TtnHQ>+bBG9bQ_kY5(5^))lOu*ES2+GdC2ihPN4rHXN}?gLX2MH@ z7E8y^&SQ@ZeVj@01gBF3VzszMgqrll0tUoq*}W88C=`Knw-fkEon@NOk16(ZehNnV znSU!)`>4+%LsW6x4(C~NLjhtcDzv)kn4VeH{g~Yy%@Tz<1>mTckcO^P{ES&^Fz4|O zA{2+FCm<^ge?l`5u^M~}EEE=VXf;l3HU}I#E9Sd44D&8#HzobGAOG^AWHuf!JqiX)Cwr;RfW1?W@hU?=!r* zAG16RtH`vva6y>Ju1&VTqU!-F1=|jkINDhdq$e|%*dz29X|i+7>rBXbR<{~!Q-6Vx zz&_6`qE61Tpg@L#j)-e=wzcvkxfD=FXeWUr5vM>KCsDxeM)TxKYYrM;xdE?>YYvyD z2R4^?p68sW`hhyGPVtVmo;%eq#XE;5J7@8M&;|xVU-Gtdwibf}g8MOT$t7lSu(_=o zMQaYz1ZhUU(s$j6UawJf_1I0u^nWUk-eT^kfk~<#Bdr$ys=8%kT6TAbD?2^g$6K1} zL(T8;R?M%3F0(kGBhd0R+=Chdki1A_lHDPV=)&tqy|J4dyXj!8Mz1eg##EkRi4u2= ztth+96pqk2We%sf`^DjRBfdDry)T)w=?f)aC=`xhtqDWg&aqk7yUenLwSR&oXw3C_ zsCgq$4R+%Mols-KVxh3U z1xoIS=>BvqQ>EGI9kmW>M;}|AF5Rop?8Jm~Dq-Z9W{e;8MmTLIM`Ys0QIf7V-)eVN zJ+4|&9~~H&W)U%#ICWzP<9|OdDh)5RS-HmGdiJmzh%AE^YC9rB80TE%s6ZX$1bmEe z-|kMexEVUM7Pe@oT%kbcEUsKJt!;zmpE1bukuw@JH2HeG1rf{@@ejnrk&JHX9;1EAr00=INTAdU;=NAI*~MSr2xS? zNeB?KvzeEo_h+nG7Ldq0+U$~)y$}cK|#=fs;$|S--V-v(!D5MciU&vF-G+vf}cX5WPdvcy&)l(ie18P*0~9q z%m!nqsKAn{Jrqr)bhib`?OgvJi>zDZD1sAF)e=)3201p@%hmite$?xW9F6PQR9O#F zraB4tFf6(b%JZSf4=yBG7TpLXW9#~`EI~o!?;W1=vGnX?>Dh7VS%*o6jJs#}N@rA= z_ERq2ad2#Ow|_W6U(pwO8%THf-0ASdmV)|24{4eR*~JBn1$$(Y6X^gIpI?Y;lx7JN zs9q9X;dmHC*GBb{A9nU${prvvuMiUry+&@^1OX zwx%{uYu#?6eNx^0@{cd)JEcE=x%mD|aZ_vZ_O1At_whf-rx)MbKg5YU5N<8lbXET! zDzb0U#RVZNU0exiKGDG^T84^qRNNrT`5tKwtdgzB$!vj%ANF{WdLDB&@k4(yn=Gta zQ6v1e`+w|N`2}N?_vFDId!YGiYOJp8E|@Y-@jd&!$9~t?4aE1372WJu3A65QyeQMt zhCTQz>&o$mNK85H<@s~%$$L4#%o?oVUc$d|@I>4tOlcG z08z4gXyMfBisq{~fBy0HpSWbEu|FwYkNp%w0Dn$t7)O(m3zAn-yko>xo+390P=H-X z{0qmTvFpQj6249DVQ$9$#4rP|TqOrBPbeaKm6)Qz*p1x*S#Y2!{qF9{jW_6jo;^GN z?tCBG?q6KIm^*U=roes`pwAfWjTb6Z0aUK3SHe0@@njmskfMy2(csr1V}2^-r>NGc zoqrZKcs+c`D9Gw5g&vCWG$44T_y*jxthfg}?BbiWH}OX_cSUa;MVZ647?1Ex)^9*x z2_WW~#*ii!d1dw2Dh_>FGHRi-HoG>ildUDe9Wkbo427HY@&@&En>l@XHtSj*MsS zY{HYx#nUOMxF#EYTO>FqxE`BtvR%O=pT;XGX&wu%XPtaD!@HIl-upM4;oYS2kAI3n zL`v7n5;6jH)}6;E=!(vkAN81YQwA}ogjba_<|Iz!p@5!Z43qu2c=t+w2OF*^W@$+( zS~`*$Y|527ed|n&`myxwuo^7rE0EGjigyBqIiZCZ#aw@*4nljy_RZQXecP_N3r)JC zl>VtVsyFmQ@AKbfnd7By3$)dg0e_DH&Em~#cc41Y2S&IFhNU@3q3&tjk8Q;i#mm(y z{AJG>dG?WW@J&OgHiI~KPziijN@9MNgTm87SZ{x;vP2cDe!i`Y0=~NaZC<(kEjtF5 z3+nUVd#APT$Es|p$X7uvdGpj$l7Aw9`bn>DUW10w#A6odRjlE0=!Sm;~k+iY*(M`>&46P1&Q@+vr@snhR%gW)%BXshk z*@UI80j|`^(;^M!V|#n5jY0>YzU{L};JCk!~H1$$y_5id&4mv6~76 zSHg#O`6pYKKQ8F<#|L+59p#$^UA}p6mv4+NmvX55waHw>)gL$4W2rSLun-*&_kA~9 zvF|!MG=Lc(3|701BTF{Z@)D?o z4r|@ok)yL`v2_E#sg-y1J3L__55Q3+4 zQt$Uf+;1a*U&yP)=b4@_IIr_!5!y?$3Q=0s@3tGb&KKC8@bwl_Z?``?W7pL+bm!a1 zvi-7sK10t&`G245Gr`7Pr5DSII9R5Mzumo<>u!iU_BoRW@mux^apZ*A&c!VkwS9)P z^n+Z1lEL;9KY?JKGCx$6ATBZr?A(IczODC*oNSp(vs2i$H{|Tq)HglPuP}{RLMN>g zc~$%I)j8F-BF#CwV`_hS!Hw;)@pgjdI?0CpNB=_t+@7smRK zeT>pnUiyt<EPSYt zJz<@q2;BW;!!~m}(g1r3iGotFVXWU@Fg(i$5`P6^4=N^5&QPx&TR*ONZt60>wQ-wq z0m8}~$O&(Mq~~AgusipfhR+`py%Vg1+&3Rwu)GG&Bl7df$PaIRA7-X0`k$VUVTku; zVgt){_}Bb&Ro2aPnai+QmDx0lI^|FW_P!4JDVVbnxJl{{IuL8{`!H$a zsDIY!HV?i5n`(o-H@jx=U58<_AFs-?*)Nxxbi*h@qdRt zPDIpXD`ug(oW4f59*0A(CGT3^ut2rF>?ru%p2c4=pfc zSb_5osD3ICpMObS6z>GF=H(Yf2Y-ns45pTz91h2M{LEibDa2?8DH(?_a=wH{Cu$G! z6)z_7?p?M==s*oC|GFIUuWR)Bb@bLUPRisRoJziq!r)e|!Ij{Yc~vcX z9*L#@VA?Fx?yt{hR(V zpY`vmXb0J%JOCLoCZlRFTz~!GSI%6|!=8bb;2ISJ;0*;;V^x+(LfL$2!(vl|WU7k% zL_sb~s(!c%UD+&Fe`(bit*DNPC+XjlCPITThJK>`h@uz|D>kV9-Y z3JQum!z~#<6oqZ4ZjvCE-@<2pHYZT zoPzn*%#Q}Ru$N}x8}h0~u&!m*P)R4&r_|smsQs|=W8&4AjZSp36glUr8O;JrgGm#LJN@Oaz6cJyR z`GjT^2BeO12Ln7!Uw>MrDj{FLinCw)3C(Yfhwd!kz|dA2kv_Rm4}m#}KbZ!!WOITO z)CdU=DlC9DM>su8aC|^vIJKCT*dJmHS4@N1;PUpb=yVodeh0h}qR;4r{N-#Gv~T&c z%L|j^qiEEE%x{d!uGdTb365rIwRorg+%N5vU+bYt1Yp$(U4Qce7ZG*OPo{x;t8*M9 zsJ@Id0`(X?W|Wj&-)3P7)jQf6&8{=S# zZE_4sHnuUX*tZzjQBrSFH8n}10V_#sP)$1CU8dq>Up?OOZuG6;ds zoRb>E663<41zdaM31&rF0_2Co2;I69%GI*!b2dBnG5@9+4kH4WPK{!*P<|EU6rFs; z!GwHNCjD6MB_20Fv}+Kp-}&6>HjaCrYsW{u=E-5Jx&k}_rg_ro){c%EZSs{6d2wfS zF@KK#<4+$GMGc%CL$113M6f-5R2pjF>`yQ;4UrcZ3830wbf?K^pt`3KJRqM7MCd4khsPtk?QAX6>%B zNSLhMQl6;w4KxqT9fvNHgUiwJcbVFmWPg36dpW3KRlLie37+?8gi|>}yGBE-+y9>sk zM4-#;MLG_hOMjY!fZ{mI@M6Zcn1~t^{Iy;#maGkp@Cxbio&qN!2n;P%@nx~wV1EWi zBfRqCD19JqZfWVj9rdk)90Ryoid~(5^(!0R z2Gvybd738MIGB@{)ue%YvCI@wtS7PXW@dm=%$-hg2AdyVQHe<9(k=6$}(5+Sx#R^%GpMqn=DSHTqgg|_J8v0(~RZT zqqYt@rF9%>s#dTe@>fqnX5L<`T3M^f5n@ngRU*N%Q0)YoN5;_?^I7QO`z_~;ldRSV zepHU)E~OE)-cT&X*5t% z5ZGJX#O|%F9EcRs3`tj@4S!!KnQu+FB0yoe3BB0E!MO>lwY$y3+CjI~{*0m+sW+-M zfxGY0_R}GRD^%6tt6q%*nnqKg#Pfc_sl~((8SAk$n{Wy=ac}ge1xhFk+jZVD8^)k~ zVH)$9j!eTo)2C_U>+s*~{;BG`DewNNyrVXng&lr(S%;SVSlx1dEPwBncM$pCU1!_K zO=V9ws&`6l`3$DuE-c~S&<<65j{<8qzbc(rB!CY^d{h}p4j@d*4>GeoOHm+Fu)Av* zSL@(!PU}NxkqgU03ySO3S{-CK=mE&)!`j^jWrT`%3z+fzZx?d}IetmQgR^$Kanj}K zh{+qPc|q%sRnp0{0)I;EZLaaba{4L^!NqqfC4*^Rh7;iQf)1^qm$*HadbCKo;uBdtE)_jHHyf2K}&nEcz=|H-wEAu4tb({^U} zV|astlOHI#u_paW;F0e@N}YS}P~^i)r_J>lx6ea`NO zo)}T)&xw=I&o(TdU2)R}o?Va0=$eO}>SlG5h4#eg5_1;`&AR~DU*cPcZh-HUsH5|9 z^qqWP{eS4oM)?IHDi5}xFO&!Jj79*t#02>*R?0wpmGMW)eyWBBu!`iBRNK7M=g+B| zteVC%On-j$W9(k$D+JmY&Y?^p@S+f-zsL{`-C4o&A5mTllGXAzi;TPKv~Te1Ku6I? zsW3|)9z`RqJo*9+CeLAWy^!)O*?LrPmf*M+dLQCwHa(1^3DH8zXmX3UXB>n<1Qp{T zB&FAF^$tEZ4t}P*5BIH=XPO@oKx+FTBE0lQ34g!aM7;onFs7g!wLbI?n@5e_N$t2% zRh(P|9cZW0RQXW4y*P-h&ohWdmExV&g~=1kyuP{9ve2@#Bc}#p<>1A5P89FX0%8rb zp>d7^@wCxCZg!|Kv=9uw!2|UC9eTHwVvI^|UgK@*WoMTz+;oht$2i6;rU(x3d0HS= zKYu;JX-CA-z`?bcdDTzJV|4V1!|9-+fL<_X-z}dEzLcoA^4M{TEUYRps`KKViJcZ9 zz}OvUWL{Rt&nuQ~E(IbwKwAt<_CfI9*%`i#bAfWpNl|nR>HNW|Cmihgb z5GDDMzB24*)&+C&Gm*8*ea8?LCF|-pX`Wa&n>RZRq-pkWd6QH8(4A~5&GLg>5s%KE z`5qei9{S5){=zd!4$vm#q1@zwqV$MKBQnv9Gv8yK>*OZyM(~ctZ9|EaRMObQl7G)E z(yPlA*~V5aE2p>=^!jk-z#Y)k(KN06?=X%=73D+q@3Oo+CB;dpLZ=>&E$PrW+lKy` zWtJ@K<|cNRjmHF^;K3~R)7#3#O;Q|}FY&DvG?c`fBVib7QQ(}te4G?`P7C^~?yF(C z%pX1LcUU2D=b+uFb(<$2P`mN>vwvp0QAhP=yK&HMwVRF3Q&=)KgLril5|(X7u?3Pu z6Sdx}2T1YqB#;AO-@(|w!rs&La2>^$PXn3N>|7bPMFkoGC5yG-dVH4?N4MUjSEejC z+^WgZ^buz~wg29GF`N!o-HSbdZ8GBewA~5WecEm^g8a1I38{YCZWIw;uYV)4O^2NT z4pT_2i6-t8`3a&GY1u6WciMp0hB*V@sF!v?e&8wHfW2_4&I=x~%nJuG8=hCqCXb+G zmU5MXI<#o~4_xSVXG({$vt-1Bu&Hf4ftou?8L-g*jTb`_vo}R0YC2LR=1%1W5`V?2=T3!;+&u0e za$!G^p*X4YtWwnJPyoYO5D1CHiM}m?_<_O7)L8a`ZRk@sO>rEOH9uK4{BO)4P|5^BHh)NeED(9I8Yx!%)<%_~ zh@I4^5fA?m7`AB?%YUJOBd#ei705!%NwdI(lD=&KtzP(97QXpmPATr@HZ#N>*dnOjG=G6%1I z$f_|i8Ij7z%eZc(g;{bq&~i2Cv^MYUB=EyaMmu>2y1-sMlz;dXN@v!mg&y4Q7KuVnU=e=bzaVZ(}1Dn;M9tRSvNVAzC~=pMsKnTpFJ{BQQ&1| zYJp2$vP*|ex?XRACPP?Nr!fSHT4R5m%mEo>t!!*Z8(3~=GM3}Sg-+NdFO7~OUyC|@ z%2$*_L7km0m>Z?Ywmtzo1gDxKW#Wc6YnUA(Z?UEEhWujNDq~l&nluj>Vj&_6xE$FW zL78g(kblN;fr()E8*S-QE`?+}Cq{kC5YEgOMj&2pP!wHDMLJtQp(!G{kM5kqz2Ip@ z_}+9i$5UQCdP~9qD`INe0gBj=Ml^~sU99B@v(@D$q`ky6-(pe~(Zjj}RB_v}zggPE zuRQtW`9C%;RfWCipyl#J#iQz;7|syqk|mYxxVKr|s53qtj`ge30RssC9@=+pW_^yZad(Hjj7^2Gl-tksV4r?q2d zs@XfXoz^{nt~_JQ|Ha}ZlJ4~hf^wt3nqjfn4S*~cKco;!0p-VrxqV*f3J`+0Kvj*o z08=#unucdNAG;;ctx5)eKY=8!IDbq{4Qgltjig%JwdZ=`lBI(FZ)S$cKO+Bm6Rb`0 z#GU5PC6_R9cwL;hQ~f@%B4c(A5Qp2Up}uT_`9E`yqU@n{2Gv+I1Il`P2nkc0nEP~y zy3xO4jt4&!DOBmH{!8OG^2=E6dH9%?S}|hTvJPQ^3|za3zE~}n1@JvnB7bN6SbzuF z?Lv@57rb2-XVfH`g(=;&=ei0DD&`cD;vkhcpBL{qID#j4o4%AWetxQ6QV>DlqLeZ{ z>R=!YU!RZ|F(}LT_ZS^ol7Q|%B*ff8dB^5&QmkS@Tbk9SvWdw5ZD;7joH}x1#3Jtb zF9|TWN0DnV9P`qrm!YKk;A--Gzs9Tr+krDMo1fuC}OB^LUa(suK z=NS#TbJBc4II*gGF?l}l4aD5xTCy>*KC?p($VwNuDG_AI28)C*8eC@5qi*=}^&n*R zXkwWIb)jQaA8#h=GwaFwi5cldb7oD9j->R` zN!C8hI^no+Qukx)G1PbY=84$YQWC1D+g38R`Y!hiDkvTB1ERH&TK({TMN}kpG>%l4 zhQ*kfMneWt_6SX3WPc;t!g}y#K}q3yrr$a&Jj%M!zQ2z9LnS>}B`w-7-b*4gA!OMs z#A{3B?<@wnht4bU{wa-*J7$q&k1PV2Q)Q=k_XE#&{9w`Gr$jdKkXt{yh-m3A{=3A) zvTpIw-e>J%d%*jzUxA58MGd*!H7k$h*usw|Zub;9q;JAAqJL)&ZB;?cFv&udW%Tl5 zu?CYiyuv$~&8w%#wmrhy9zZN|sO1IcvcR+Rd_t@nw43SD76hB6(F1VFhRSL<DX*KL3?D z$p-Vd);)j%yMGGs6`5igipmX9bkI8KHcq;BjBQi>D8Tsy`r%b{i3!&n&O(5=(hLeC zV9#m<@MykurZsDOuNIoT@*FR8ofq#&|A*j&otLHNSnNBuSv0wh^jrgL={PeXZ!0mO z?yR{NF|BuL9tKth)JIbvN{U%DlvH4D=y3(5r1OZTw|`7a@k`q#)5_V*gbls zo@uFbo7^zi%~M+4USH}65Zl|;d;#_BWh04K_J0(a-BG%y_sl*tPSkri(X!byOpxMh zeitoEsC>+9ihmtADV|KVYbDCZ_LfU=;kWLNm2;dWmKEuAfmH>sb;HXVO18|5TE4a$ zh>(>%^F*3J#=E5^kkS5Lv1iAvPmOGKl6%awCTwf8JnOL`y)S8 zCyDSD`Hsx-6iS&dfvKO>$HN5(>SiZomzL9j-mUT_M_j{~u-^oG7Hmy4r4K!`Op5)j z>6&n0o64i5XF=dBZh8h@7ybeD!7TYibDTNI;V!l%pV{0|4sui<8jV$_(HE9bzJGP> zco~zWJnU=MyV~&7jd3WOE2cHwcU#SBV{5O5bkVJSew2!GMShG?GV5ze0fhh=sbm)z zVHT(1+6M1R3$}hMd%^x>F}CK#W~zwT^f<*W{&{f>^KUgpCZVV@;(xFoBQ9@I44ysI4$0US#1aZ<+iVwsNRi!!7v;u| z{h%7Pe1|M5oQ-8~M4$R70 zqNkTfKciKXkjR`hET&XUF`e8TG%6SO9)coAIVXQZi|1 zh9JLTZv<|JqlmU<_r&1|T!L4c#csdvr<32bmh|EAZ)Rhj9a?Mbxv5Kb0vX8I^YxbS zq&zT2nL*klL)D>}FZz%YTzG8g!*P_P&1n_ND-m;l)(`x_NAmD=+ka>#zSK9Lk6Ign z8`*6<4cq}nf8*rbfB82Uq1WGk`8Pfxq(PSDW7>RpI(l+KJ zasE~mA1K=>2<4HfMs#giyi;d)Zb@b!NbE?=olG;HufvaeF&!vlW<8OBfJT2Z%vD1l z>47X1N5R!g5ajgy)_?e^;%D zb*v=Tk&{V&(@j+Ysp-CQEc$G@Kx@7*o=_)aHRPt!$)jLyM}KO0NEn}mZptt(EH0J@ z<(A1HvTA}E!i6fHnM^p;@5U~{lljwY_Q+f-$$b*CC~|DuIxgH*5gfI|EJ-FS%ku5u z@ZMWNEeQ+hBR2sH^zn^yuK-cLVagSwzkh>FE85bqVs4e3&9v>>9aU4mPcoKOXX5`# z9u(DT&~u=ubAOfj^xrD)iFPO~sQO4LPyDCIc(Q@$a+-(*#0@=JoJ3hc*lwM$>(4CD z;Y!xs>NecEMx02ow~iT?yXsSZF&sMo7fiW_+bS#DDd!wo);tmJAX(VeW}HYS%-9&q zjSRB`2RRa+W^b4+mzo~t2lui<&Q9yKZiAJum1YR~rGMG|h< z_o9Kmcq|>$H*s=Xbdmf#U%US9Yl4w(iiQ-yX@ZeSF^EPTKL~D-bBxn*qM+4Sq&;=tl{CSf)_%876>F1_R7kZ|V}K1;9`{vuvLK{_+}wnqb3?1`T} z2vX;_aeq8Nu6(IfjEx%qr-OY0$0YEHHb`qlLSwj@HcwgnFN2<0q-_C~dDYdC%$vEW zPMqK&s{b`ij7QL=Rvpe@Ls52Efm%^A2CsvTT2XgBq}J)8wpvq79MEtr!{GNT5VEgZOv`=j0iO2Ag&t$(k0xP7=VatHor$z>R}zS!=^~(a&%u2Xln~9yG>TI6&YcBBUq^ zLuql}aCM=xSbT3&Qv8d>DhhpD=dix^hy!-hO>A9{RtoF3FLA5HcWVds+TlD?FGQQb zEw-nLeHQ(j8eBy}?pB@<6T_}pX@8BeL?L7%pAp=BOWin?fgwW0npYIMwRTsl5(J)q z9D2*Yl`$=3Q*zYkHtMKzc5u+>bPmssjy^jy&w z*tK@-N8zuro9R=8aN-+3MLRU-K_g6NF;36~r!MtY_zAUFyuwlIL+^d#W9?J3)$Y~m z_1akc7NfXNZIpa4nMac zU0i5WD_`=-%U0wygI!mdsfA@JA94d{#p@?ZAbX|4FIM~(>>JxnB@_i}pj$5AtJ}_lXe=3Um zAIy?8nlP)<2=TdRCHX_wXZEi;kr@vH*mWOpN`yR(22mid3a}f6QA{xWbrKx#->1B( zp`iSS)=1|#d}4~Zc+`CV>+#VZ1QDWA=>}q1H!GD6j<0YG|M3U7A}~AOEc%-lmA`R7 znj&gVGIa+m*^nJYKkB8iKLM9qwgIpQYY4+&69L4Fw`8bNx6?xcJDt;*wa~xHoO@G{*?l{J-m#jd#W7+tz2`KP* zFcsy9o{*M0y3FB00i0X{apS5@M915?zvwTY(4>AdE&Gw1d1GaTQpIkCz& zPc*Z#hkwVKS_$!Yp4;ruvXg7H$)DX!L%QyqO+A_iO1bMnFN6s4(8auv;B?`-z0e0i zJ}@Vifc>mpHCpw~&EnfSqQm^_I*NJ14<%lrfhc{t`7EWfT~ zd~sjCv;oQxa4q)CLNs|)1^xgoH4Q3_SC^Thc`R$UFwMdjMx!U;;T(H$O>WLu@sGsP zd4D7@I6Ie8%NRWG!~6320(c8(y%yu=c46|ry|)KQf@y+klCW%(kS7Ut6WNdiv(b}C zLXL&KrX*zcSC1qKc_c6-A(v9iBq8s^`%1zBcnfH~mL%kMVM>C%w}(rDG}mpSkiV^3 zA`9l0!Y&N5)l*7CW=C>AaZqUSxbl#tf`45gEYw<2BC=mTP$aAX7g78^LSgA7M=H$z z?k^UJaXUv7ipEVEy9e-oRdKMnzztcz66#~`P_54MHMw6w@3Ht^1*vnoNsDFtZM0u; zaNpqgA}cvrRC6b2EnzX?G7%Aw)uXC4r2;@JvO$D_%K!tuq8MQ9t2_yyZ=b?dqks2s zZy5;jAWGxM76X0QITB&eW>vw^KU;0!T1LNx!0eogv}}Kd9t8ebp}?!!+=h0(fMu|> z&Hviz_Hv+KsFE^}tB5V<9u5+WkP_v-&KSOAse&@&`r`aPBNJu%m_IY~k?!E+v4OfS z$(Wdmqdw!nw_Vi|{T)QJFwNvIxqlf1GY{AC(47S-^Al?~O8u%+e4Vp0S5dw;Q@9tV z(Cmjrtli?o!pB3RM7MlYMZ4%N^3h+a8vnyk<_>xF{rB73O0=+gT>@Fpq-|&5PSF?P z=~6tDOI{jGIwIu^8E#G#@9K@i+SyUJSA!Vx-f64do%7h-{MPL*Z+US6w|}N=ndc1J z-uskgjc^7<9F5h@Dc&`H?Y3*ZgVxDm^8-i}({VPX^8eZU4zMP&?r(^&i@|5b25V(D zqJTnZ3IZZQ0Y$2Sl@dcTKqSe;OlVTGtJuX=>&zjN-Ldr5;soa{!~GxZs^My2-zWaL1qPf2IDg2-HMgIAAA8V^e)jzw z@Xv2mkp=dcm<(D7g>H|@*9|=cx?>Jq5QZL@&_NjIrb8H)=0XyB4ATH4X%NHcz#~); zqrF+D!Cer9E0*)*iNp~EUlLH!e0%=&0x24Rt4&h5-@fT&iP(%pmiQpjYiLL>U1xji z3g`rsXjMmLRey*}uJ55U#H0CeEi{}@jSP&wh+0Dd9Bm!DW zP7RbJNcGW?LFlVP`JGibo*$1OC=A65JUH%3*6q1K~?&G zWe}e#H&7oQ-(Y`znk&lH@r4joc1bAFkiHG6LO3r0QvhlKjSd4l6h>tO6ruVEjn?7u zi98Y3cnm^bGPB_$5>zOFWnZgVBD4gvsbLe6rmchxI}Ph5I6Yl7wY?yTMf?Hb&^1zo zq2b&>uTkMcLVdiv!?fRluo_5Z#b?4bnX%UW2}H zNW2C$G$h&?Zk>}`V~W^7+S8kE7HLd0nyL83^Xxx8tlwwaf0^m~FP~@11sFW}K?XzW ztJiTH#8iLd18^o|ID&&H+UwoE9{$z^vYG;)8U|sF04wc0068)wQI%k&V|hXm^NYg* zA~g8cNFE1n8d^#Y?rCCc_1YY1!w$K?Hv;R*pH^@{ipRS{s2q`j#UQ!nhsQ9FPl8@r zB2bN7264=#knWrpyghmaYxQpi75xB6{Vuim_S#}?&QEmh(ahVw}B)`veK?Wfz+i$Rdj{?`3+j}-&UFA? z=hTrzK;&^8AYI6U9yL{a3k6B_CWx%b)txJpkKdg%SZ28V8HP z%Dp54FTT!Q)n6B=69>Jj^xs~`ys%ehALD;uIk@SP4`vb)i5!+mkwl+DF|}46r88gdKkl4+zs?bhWNH z4hjzUafAHgBnbHUBRn9CJhKz=FOihzec3=_L?Db!1z-_h1;Eb+lOqr;;l+qxi~t)7 zmI>p860m(0lM_2d3_g`Zc}cJU5=!u+#3FRaC5mdh!UMv%gMx#CdmcZI4H=(dx`R=P}u}DR* z2}(>Utfj-6iFlDFuT9WmW&ghDupNzNMLZP6*B_eMtR081tou!8j25TL%|lX|j(x@r z948du7Z&al{WjXDEipm?F#}^y zdx;-@(pnIrB1t}x0FNkz5@0)Y&V3s+K`50{j1Cr1(-5Tj8i#hSHsZ!KdPkxNuIP=<0hm-_S!wPOR)#C)9% zrssreVm=U7%1aUl3Gw5hXmQM!>=RJUC_27X5ftIrnsjibPLuEjO0WK$A~c-3;t$UJ zB+X<{{F`P)|3?wKT7;?thlH`3;zS{y3|8$X)OB*e*N}gt22Wmt8XVSdGOAj!*uS6e zrXLg{i2YXwhMGgCngm))r}>2ke%>Ul&202zknW_-xKuU3jGvZsB7cbrju!$|aiM|r z5$c&3G8m`B`gTDjDPQrGgr(!ts_sy*M3hP|CmZFqX6>A~QgXh)nRrq43y2IOLq>qq zC5nHz^Q?a%ncyA$XW9c~qU9OXGVvPJxJ*bor4~#-2O7bUcc}aq2!^UD5H%;JV_r!R zrF`(c@Vceb{JEy0t*K64|6FwBt!%0VEs~qiNFURFZPI+~h%bxj2pPd^E1gLfYn=h3 z)|GvH@+K)t4{0I{tgE!}>fi6Pvgt4FYM|k&vFDNTQ4n`%>P}I7kl`rui(&YXaC{ulf-p)`Sxtq_j}nX6xYuW^ z?F~KfXc`su9%2R9DaWo%mD{ zR@EvbM8Q$Ra$yn-7EM594Hs%*MbPpV(jY!EeI;PP-%_^b6ep$GQtx#_{Br2jf6D2hEf}p^_?k9S4O_`bErIB~ z>Wy-=mL_wwL#({FOq17NJfL3M1ED-gx^adzDMC%}LwdUIK9Dp|^VBl>Bf)fO-#=Cw zl4%W=%IV`;=-UDYLIQt8uW_Z!AjcwjHlI8hl`#tTsu74W)N6znH#h_fp~ISpF*2A; z<nO)VJ9KSgZ&ImO zo=^f|VqXj0>K-Bzklt!K8VQ`-z*|Hlpj!x(z{$QekHtJG`JVzc-Nsf7HC?3cRYv;q z9%{$dVA7bx5({Ec|E13JlNoO;PfRNNRkQtsUG((}@M%IaoWuni5edM51=L|>0T1A< z^^lStr_p~EIsr7B7t)FY@rZ8yf%K{Yxw9;+Fc<6>>jT^_K3z1p#wJYjq+^X|cKoHyLVixUCV70V$5K0z`M9L5~hKGXhrcgOgB!XpBgy(W=f{z88ZAU*FKxIlIg&GF3#_|PPON}gyp1U+z zFqVJSK;slb84a~h2#gXHr%?$K^_!s;r0ArHS`kFy4;yZBi(g4AnyP}$XsD_D(~{H> zb5t|!xR@u!j2ZZUipj?CADyXIfh93dN`Ds{7CVD*9%PxtJgHVbnmX)(JgHW8p~0Yv z1nV@@I%*P{hDB4QRMfPxka$i&%##wmpk{w38c6~u*F?dka55Rp$5c)x=E-d|{tXG) zusSiL3>kENgvJDiXo_Kp9Fiacm@O1&p7lt4|E(+r3!ng@1crDqND>SbMn^rOd=Mgl z{RBGqO=>*T3>Jg}e>l~n3A=KX{ls^B#PhIbm1C%5%ZD1uf1~{#|BPYnZ8%hhzrlYV zoQABX&MhU)*dRhpMWgXN87yF9>eJLzG_DTn)e~~xp>!xt21_AUv{2xtb+)7oj)POe z%75 zxr8T%Aw-5%MqxRW2sTk+g~En-h5C4NgMz(%xPIOcEJ+f|5DA$6NkpoQ4TZwfDA0-sRFLu-}HpC|&P$yjC35YIq2X++r%w!%4#laF7a|QzZ zD=I+@n{=EEPa=TgdA4BaNRNrT=VQSvZ>Z}OZ_!i^h))DC>Na1Oome5bniPfQLwG*; z=15%M8{Gi^Wky7s*G6oK=E8p*$(pd^l`Mbh$GNn`h&8N6Kg5;6Vk8NG1D&7Q#_;$F ziBbTgwZ9usuL(6I%2*|SZ9qnRDP${+RhP#8QZnXN_9qsqHg!Dk4fO#?99BXZfKas z2ab(}`B*qU9ED{bbT0pFVzRQ-m@$uSMt|HmO$LFYYPTU^cmnS~E1`i-~GYz6cXhTunZL<5*7GNCw-hsu9pnLnILUyo9RXb&Ix5EZZj%B!PU+OiZn#B^z}K2e5JkCcFFur8?_ zrT%DM6BM?yokFEgcD$>XU3?Va=> z`!8;>8NWo#R_fXRt72dsCI(g}H1JRc9DfJG6P2F^ zi#EijMq(k2vSbG05-su=&$^o+R!6j(Aea{Oq!1d9B#H!Bb)iI=_Jvb;VyOsrqa)x! z!#9C|apXZDDi0!%!5=(nCFH${{DcqzpJdsHC@~Aw?6VQbZ=F zye6J8X}l$6Fq$DN)glRXR3YjWd}3J6Bk_i*#DCg;egFNlU&H=C3~YF3qw)W!jYj>y zvx|SG|F`e!_}%~i8^7+q*u@AXb}>9Op2@_&ZvaR6Sy`tsm5VfT%$a-GGrL29yc8&0 zD290Y;Uj9PE|SVk&cm7HNcNGr)go z;1+3W(PThTQA~(*;|ge1o#h3Mu<1HF z(VmH~?U8!{sKWx~*GRF6B2g-v%@#@^KZ#Hd4MHSxo)8=df!7U_Blt(xvOehfp~pC1`*8q~H>!p#j!O)KolJ^mx>cCzWEl&0?x`%)`Qw ziK#lI!3Pn}Vmw$l5YxcJ!jq{SDB{B(;%<+gJt5mz2!mF3uuO)?$mvj=d-eGV%6s&2 zmkkRbHk(b~H!Sia22+`-r8aD@-A-NRsm)Q6egbWms|ihz}^j%`M7JOAvGoKqx9Aco(5CWUHpp-0FDODU_O~I$yJk zIB_zk&Ff=h^+}zi{jAfbPRFG>`^l$^Y6JdcebUkTU?$i@_<$t9U-X^>nhf~@F@EBk?< za+C$Jtw9ZUCWbYwEI_!D!i_~_$vl|=7T{JF@_;NY@s&vM9jJ~{kOj(*hsCfv6YDQl z4ngHyaGt4-sQ{;F$Pr^gNe1$OSXeO@L~goalG+r3z4VgepVfb1Xc6DeCgg+k5%v;K zF>ysm9G+bkA#peYA)|R);;2Qelw2qlO5)tHx&VJSRuvS&YPvIJaI#D&hoNXnq~K`* z*+DW`0J3^YAz&H;HmfW9udZvAAPB;bsZzh?BS?Y}=7O9m1ZDP$ zwqy5#?5MRN)vp1q$Mpo)c{e+|CeIJDLC`owk*qv`C3=6egP%^zMo|$G8!JM1f{cu& zm-R(td>HsRPz&!g3d_|JgGSj7^_3xUw!ulTOePe-wgY)6Y&%>g0!aka&CU+Y1b3a) zeU)?57F->KS5x)}7`ZQKTgSw2J~uiDsrbWfCW%>LEQH7)D|kF+)8vi1Rcol9y>-LI z$zyE|_33}(Wc?o!2&QY01>qks%l7re_-%&SO>Kn#(;Zam2_3Z|2r7ptKb%@q2gVn{ z9$DbUcme^1jaWpc(>Mr(C%DFMh zS`ZQ&tA4HZm?8s=MWQERa+#1Xw++XPJSK44FqnUzC=<$4A(ohzV#|wzJ)9i+Iyu|h z+q)}GW>{j3HzMW)JIUL}0Ir%;1k{nvX(%E-}m zRM~$woPQeE`!Dl@ag#yOIjK+zi-ZywRcf5>2}CoWhfCvRJONBh`$YU-I?57iXvi%=ixd>~6%>B+xHzC;UJfrv^oRkmNd(QnqKayDczGng4o z%l|Zx|HF*YKTG@(zed*o`Zzjht^YZ=eBXcn^RN86LxDV@1Pa1NT;sL^Oc$8Rh%5nv z4fxs6FbY9^+?n*b04$zkqjCW(0|@|iXa*4vwIW7PLj)(mBJKE}RK4jrKa8NAD%@*Y zgZQM83I$5D3nEEbqQs79OeoOqF=g2VjVHU^?%koVupvay1r4I62<0-ECnln4B3pk* zW4;>8t$@uJ4dqubmKruqwp23Bq#&U)sQ=hc0YPf!4{&QnYLy8m!4f$NBu8i`U*$be z2y(D@?1P2cOw81RQcA9f;f3XVB1s??iea`qRSE;FMJ!@-u?G;uQ>QHK5WXCi+v0=g z&SXNWZ}(7cJP(Zr0XvA*@wT4G1|tG;FSWS>Fe#2p>==VER|$VXmKHKt zjL2auT9HAvkn*32C@g~_EU+$f!QO}{h^0iTEXb0H)x;NFAq%{{R5^@da*{(V9O;0S z30OvyCLUsRxx6@7B4;VfX~ak@OHEes+!= zQ*|jBj7kv+3gd6c{He-P{!NqsqQ@H~4~DY6;a;S7>M@BVgx{dA#HIiqJ(7~30yxQz zc&AKtYFTbl`DVIDnnWtassqy_0Vj+pJt88(%5<0WrCbRjO+@2WPnmxZlmNq0o=BJk zt8_lMaGVg8%TnAhFYE)n=%Yx-L ze09Ac*vv;nf-pHE)V)YcZVrcv z)Rtmlo;*uH4 z09z=DQ+7^*A|f~diBs;ohLv5^ftA%YaB}2Ru~9c@03c)QO|E|uC*$$q*hCQx%@;K&LzP6F;8XD8LDWnQ1c!iV(DvT4SXiJihh}mc&ra}^494y8LbeuQ^2aQcA0&Y129mS|i@nEkh z^<-k<7uQ8mk*XSsinMzPV5^E2Hm0CG;yy>^K`$P}>b`%viiO5%S<^_sVyOrmb)_>{ zwrFHjc!~0OSR%)_Sv5@tQvB%VlwBnKgq5CoOS$zXK6h+?jl zh1uHWAi96$YN_hA=5)g%c{fP?9{<`_CUS#V;{-8$H}xAlwagBMMKIQK5KoNB(Uw$z z#2~3br)20FiidN!+u#c-fdk|>9%Jy%v> z6Q$Wi)_^Drr9@nMOa7YBPd z+N<6N@=~bpT}}CFX0min5fpTW)6HM?~~YN?3p0 zNajGyC({t~vD(zS%`_ike&eP?C?L)oRBC(UG)YyO)CD5|CyJ0)m9J zbax|Nzh~h5e7?Vb_dNG~`rOyK_uMm(ASX=P{UisUSkB92&?5;r`bJP_%-Mfczt-STZb05nad zjt-eHkOe>wnH3xxEN8J43$T*)qQ3uVc1=s0GMJvp8k5DNqp7%wkj&f zlIZDEsrbMoVcCmgE(CQU_*H)b90894R6d3_T24?ba6T$v2Ypp}HIGzlHw&gA$Ep|3 z?~K$wFPV5feNfaPik}kiZEXEs_#eoH4PWUYsClg(uaUtc6j68Iuf2R3-c;pB!39NG zE=V~PY&mdhs^?(cmlk!8ZC^&rQi0qO&$gLk^Qb4sIqK6@#fw{M_C|l#u}%pHthE?3 zfjkrIjLFxLqrz_MO>5_E4#6}ucmSxBSzS~c>-DL@Tp@nq-6lx@eUZ12(z9pWSU&C| zsCl8=UcLsTya;Q&Z+9>x@!TNF8ncQxOREiCzjLSOw)D`)F-Ebg}BY+&r{|DYpBeqC@0-O(~&!9a3x zhjU1&t!>U?4lgdOP{6yjs8&6d{zo(J+6anGXvvFncqJk_9}S z=3b7bb+rduQ9Q1mN_sOa7k9+V3D&8`n+}d$#pBblOZFY%L4gxG2Z2{`YoIuZ`9ry^$Tr=oW4=QuT>u@|+QI=ZGZe1a4n;fR8a)W7kU z;(Q{4?l`5GUy@3v@pxluSqk2zcFc$}tYA6T3=OS_EC5btt_Tjp4NlVkl z$e6<)IvK1Nb#0r2CF+0;5AY-zGOX!v*MDO4(p|{lyksK9Sc-=aVwH4p2Jsk0UDLe# z@!*k&8rL{o4mN|LiG_?j6i>0x%#%bN-QKQD%Z7gkSVC{ui@SAYrhcnBBF|3*8nbuv z3rAe%nUCTx+KDdOIO;UL+;?WD*BqgvGUaN%N`@&>149rP9EPH zllOwfG@`hLUm|XRC;7pX841Ti;cv@fm@zW{>W>!p=z9C!T^xD}0kmO-N8eu1yDq$$ zsdI#C*`5{%^;+v=v85t{npd;;89xYYU|@gpvtC!K-K(jpn>@cAQ0t_AIO4K8O}6|Q z$Hhj2zPYF{k!FtWnv>H0AW9`e00f@ZL{Rgl?%4H#w%Lmpn)WtxJ4BK3Ph52; z*R+|&Bv`|}^RPv88ZE$M%aaisy11R!LOVl_t$_mRl7>TV_8$SD9 zh7$Ci$Rxn@_UVkjTCzWOYnuVu9H>)G8krDSKqS>-K)Ue!VF53=w(2DOwAA{C*?mV z5$F1I(71MV!~5cv5>{D^iKCs^jriS}l3+UShpF6H%OCNb68la2zm`toz;1yN4`jkU zIXDb*Kz4bxUQ1_HmYoTym)-1+`15PY1D~h$lf|WeiCUltvd2fey-F*03Wc!+3)a~2 zJLyOe*aeyPv3su%dC$~H7CL{(fvRCUb6MEp8B2b)aoMc22C)&Vus3|4Z(>0XqZe#Zqc0BV+S(Ttp7+n1Z>af38GL~UJ%Q=7NYp8)_ZPmv- z(Ws9Um%S_ksLk>gnNI&)O*U@Y77iHQVDa|k*D#grkN;afI541=s|`11$q zGgS6K^W)y5Z?$IRg@J#7NQ|+7_5J~)TGjkz4*?rK>&A7ml-{u~QE?qlC=b>OjLRtX zB*e0AW4B!^Q1sM8A#HKR0K|Qek@1o8j}W;jadwz=dgXHWbeM9w?d4bc0HwXIC4EnA z0=37tLv$y{`u$&v1&fo9=l z=Sa6U7A3zZLbz?pT6_}4|H{LSHGco=U1_YXv0&wAZ-z4n>|$Wz-O(&ffo!glC(jQA z_SM#$7iZ7H)UtRky+#r^UUnl+9{m)UBb4}!quYP=bN-53bJW#ztRJi5nOBOIe`7?z zIZ;Qk)yAsz44r@d?{#jHd-&{4R?m}ZsMNY$erF#PCUVo;Y<}&gWZo}sRTM$aF5&*k z+W2oT5j+4y%9chHSIO~{0RUCQH&31i%@|Mx_uoOqQJz+0eXJrRTjsfJc)N{i9zZY5 z6R%9>byzq;v9NQf2LT5_;3lRtBC7CD>>#i#yL!c8qbPs!#S>R`_^5G}!w1+@llA2@ zKJo`?AG>c`9lpAjGHFAZgPFZ)E|*}|f&DZIww`)-H5rabYX?@sxHk;_bd%celnhgV z-5*^m|H<2xCvw>@^A_w=diK$+%klOrUHIssyM71y?QCG$|G+O;6z28(jJry$^!!|! z0~J+eNkM-LxqoHYGh*1qVL~vsdevN1in$c8U-r((JA6OkEabhF?~Gk#^Z$FICIA{G z<(+qRsF||kY0!a7G95z8K3H+1n;^Bk_Uf`T zCY^tB$i=0@cBb&zKJ(MSArM`>KylVbmSxXDX>9dEp^grTM$Ul1vW)yzhp&bxeSmT< zezTsgQ&;h~vV#}>*31Ey({Ib8Qj+7?WVB$Vx%1 zK}RLB_9M~vwsKIfF5Qa`_0O*NM*QzjZfbw8Zoj$@muzu6M0sv0*RlVvabqy3qh(PwLNk+wO2r><7X)2P9zAdWE>h;LS9V7daDUUVt}`=bmWejP?%lmW zn?o||&Q$=U$kMI%)YoPp&Et_rdKnkL%@I#aVpt3(b<<_jv?KIIxZk;zVv(C#*zJE; z_b214_9;~kng@;FT09i*1Q>xSDyJEx> z&{wwN_0-{khqkazx#?7&jX9 zNq_Mje(O&IE#lz%LGiOwUccDiBOZSsuQ=oSj~4eH^dQs$5ZL8Dg1S(`Yg{P+p^QaG z>tQ##(ksT;8uY``m37Bvt*`(= z0||tmN&tfNB0my^h%pRPU~>t2AnJhv#P`p8RnlDRLSM6HjGJ1*?N@iO`H_D^o92}g zwuXYRH@bFo)PP~H)J&%8;{~{9UOYukeiyr!phr8C=n;9Tk%#zS2^#5>J@41#sM22C z;k10X7ZR1gdL)O%++g;`WP)!>7jHbkVcHs6OJdihAbD=f}D`ave0kp8PgCshb!8fenAO5Y+4Gl70^Y z5KExF!Hp%Prz_FQZ3bS(&F-BdbLcJOF=m#*{|1PfsXA0*?Tp z2JWsj3SDg~RwtXj3k0$m`7h4aGcyNDU#3_Wh}Pg*t?7v?m3X6<)KwJQ{HUR*wgPf^ zkp~Z`B2`DX_q)(?qsYMr;#NdleIYNf=vRu(EfcQc5_#DY{t&yv>bLmqGv??u1nZ?) zjV!!Yh9)y20x*B)BmBm zy>)wYt(7p@wvMhG(a@Gp+=tBJcXWxrf-(TJH_kmtfWAd=g1+`@(8qgv_i+iW87oZA z2iq5l)!w4WOOLwL%M_rr-{qPyLdYZb#N#xt2+b;a&n18S3ThO}-Zi-R9@ztJVl8>cPnQnDY;og%r;^(m=#{ z$%L5CHiCb1Tb2z1YmEsIG$_m-F@Z2^!6mz`4pwWP=tMvqjZj-7^$Jfd7AY_LZ>um3 zB6a@bQAJdyFb}u!Qw}xyF*XD|5R1)|wC*uRh@(pMWnMq#U~Tfmmmc94cZFm$UaCM2 z*!bu-SDRzP*9_HfVyu%WMgBi;N`bSf7xalj4<4JO_04Swf|-{<~nG8EjczuQt%o8 zddQbX6nDgN>k6<$k^4+i++>P2;CR_BhgIp%8+kJY zlZ7pw&ta$Qx;Yl-Z|-1=SfS|q=(_=kVEpK*hmxMvs_=e*inDa4K5(61l)O}GIcE8u(vg;d=lri{`yoqfKtb6HQO(H>uJeDSdV`;r4*??#QG;>PzVq?JT>{|WA8VIQd|lm~ z+gkto(ADfgn!^WvpS{WJvATDYI5t;nk9`T5l#U}yF1DnrAiZHdy3g!1JE1~IZh@1N z(UZs23Uv422zVXP=CG73A;lJd2(|eR+lc!`}y?D7nx0Zh)Y&An` z=2EJjzFt`H^v*FC|40;u-C)=oS-iYl!Vt|ME1K|Y*WFbl3Ar^?2}cFa#Et7)hy2V$ zFJxdrNwK*RUEh~li510PPueu~fB&OL)r|W;b@LLXhxzv?$HJ1PWlhI9bVhzp@~uC! zDoS~awA~H*xTL&4qvHs&UOQA>i+UHU~)Z^)bc>DO7n--0ltM2T6nlNiEP} z=RM^%BSvbHOYT!Jvo}qh>g`^89@Mq*jv0Lt@P2J33j#A6BdB=~oil?#rEpjZF~5yo z!#gS+G2;EDqo2A8H%%|icL?*a-{lfBu5OY%ndqn%qK`L^27#HC5Y&IXx;yVhK&71O z<-4EW#XS-u-k<(@|5Jiev)rE^ZguuDDWU!PBtDZTbZ0*dwc~%V1TrGv-+?xV2Prpw znIcF~!`Y_mQa2V#1tn?9-|w2qm$vX<;s`s?a)PzL*dbbUkVq_C_~{8Tt=_V8l<`%? z00bi_dWuv4Dt-q(f=z!St%r_O zYW2wZdnFjAkR2dsAh;k`1U9>G(q7^!)$TRx^O9<0)1G^!N_{SiM5qFY@|Z5k2&rOP1Gk@~HN;HvGIPWM zQ^fII9p~lcDmK6r*{dq$TY;>IG!T{`g6b_o6|}6sY*$o$yJACB`zN$#V`TnTH)9Tc+pa*EWf9c83}o+VL4xd?dmkc8cv76` zo|^O%UmHML@&^%Jz7n3*G1+*XjIHJuLXw8i=iiS$J0)48RRa({K%2vsZip-#2~3F@ z*R}J7*v>^SAf`%)xUTWGYP(T$UyA)2^L%+ps>R^#f~$Y3jp>G_;~eU!PV75ZBQqlH z1y+hCssuzVeoDNzy-j;|62Xy8<07}^+mcI+<3m{6aMYi~=lX>9Oew@Kal=5!3jj^S z)X^dLbcp~@G95P)BOAi(Bmyssb>Au8PERHNnM*i-Nb;1Y>{_ZXvXe@3i%7<2)1fgk z=I{Xz6AOPLjTuY$F;z4o7JnFOORQWcmeT*QGlM9_!`D_*B{u5&(4W`9nmJ*&Zd99| zxQZF?H3^-xSX_JSh%HaF5Y)U6jY7#6ODEWN{m&qh zUclJSM2r$ivI(P6IRS1lC%PxU+o+sUt*?M#udp6R`Y4pN^*v_Z)Y`k+ zdGnpUY@Nl;hP99z7I6%JMq(Hfo_O~jYZQNplugu}luEHx_}i`%03a~A5`wx==j|{< zgga{L6M=q-9M9&-^Prs+%jV0h=)?!JBi-*q-pep@eR^JX*my~ny*@zDU`wT30UiYG z@g*%Hw!Pe$Q@v+x^_TgSg*V}zHY^|k#j7!VJm0~JoC!J zTu)-GGH!hqe#xgKUw5U=^EkYV&VYZt%=c0s7fBKJ^qKWx$D0wSdgWlRgDDC6sYLqi z(Er{C&1$9j?8syoozB*tVhfFfSyWPX+~YsWrlQC#9C1~e>lXl%Ky1G==9ZH>$&+a3 z6NmUc2)Ke$1+zE00`1vY-E?<9lqp_5*5LDX8b5o!ykuE&%=Tws)TA|Psit|(r-Xce zcauP^R@&!$#@ay!Tkc8#Z4P5UcZ>i}ssoqPiFBj=+1k^=IO*hlW+A^nlQ^VMR8-Ol zJ1m-gjYUJ-8s1Tp`yeoEF(N?FAn2WqYyhSTXh{q^B$dsyuCHM``)#H)R<&)!fKxk% z`iP57_W`|pHTQSVnSb*AVDk11l5SA(g`J3v7mj}gt z7Xto{aEV<#|FG~*iP11=w$?HHYkVdf=2*YcLQofGJgi_sOe$Z0v;idX zePg_N7T9}KUM8FhS+~TEq4|bSqfd)gU8i0mWmyPqs5#=JN(CBN5CMo9pv_^8^jE+Y z)F=GCq@nTr)t;oH#&p^GHp`c;EXt@zMBV-~@=Tnu zyJtYq=7@)cNAK40e6Wmvs~M%uy?`qyXRH9iTTnb;feK8lu&Yt|Y9M*)i@vw&s%S$E z5~svusv*5k@7)j0Nq7vM##hfz5lQ#6-K06{0{`tHyGh~UxiuqZ>?9|0;&1QOkkM-* zfn&Y}B&{dHS>3PHsqbg~ZRchpNS_3bnDmFRH=gocJBK4C?*h_)P^vM#9T6cr>m8Rq zLi5Wf=>bXG2i7q-j-mz;xzk|JvfQ@ShZvHi9d9*aw8f?1g-P;Oh#FU+lWVul{#Bi;K!+ zsCn1C4~pXF=9pThHDt-~kM5~JK2;3q6l31`#!FvaJP7fCDhhRU`&u8ZDC!#I#xqg! z6b9JO4=i0B*J#cgk7BE@b)Z6V-akh9r}XnQ@|C-_r_WhbXkECrck(pg3aS>&-Z*ET z9U6rg1YyyCrmRwIlE`s?%6G#=k_}u6aZxd83oA|H!!XjEEzpjy55MvF?<)aR7_?6~ z73@C|^0e;lf<*&Gm$V?PBWv1Fw>-Rg;|*iSTMIC(wK71^ zpqXuG6YO$-G9+|EwoK={^Cw@fde<~Rd-wTo-Zr3 z1fEqvQ1jXg+0}x?Ibmnn?kxu^={ni7wH*S9(u<2XhQj{LT~oKY=qp+XDPc$ZcV(uQ zQw#@7A%~g%b3mX@j@lZjfAu8v?apobq=BOHZoOzo4;W`XLZHJ||CG{q2v& zWRF^3RAlyLFQ>8+2@^{m80Hyt|(5KJdE?BgS`0ErvdamIlKL%0@ z{JFZUKfP+MpK5JeR|};C!&-|21P#z^gWX^kjmhs~_Zao!W-ESq=lr&NX1z4+<3Wpm zn>7ibvIm-L*v@W!ZvXV^>e_QvPpsN7GZb_R~ZPxrpZR!^%&4S-?)o5ZDZX zpyvH2>M0l$%*1bNY2HbtEJu$mSHH#6e#D88PX4ZVtv#LZh12-t^JQv{&UY0Mm_mMl zpn;yHY6FDa&}8VhzEiy(=lZ69|IYa*_lXU+e(mXlb0!EUXXmS6Uo-*(0zb{9y2GqU z_)reU0VtA?WI>+Jjx%TO-}Tz46Y24;3Een+BQ#-|^mudKcTdc+Vc+XC6EnV1H(>qo zcy+g6G)ZVKI3?T*$`|zyE|^~uy6n50J4}E3)_M1fT8(>i`OCQPC=ghG3xT021mvOt z6ab3dNTm1C;=FxYZ)^E+v>`X(tLXDT;oMD*!l&9z1&fB@{pGUYM7~m(x)8C?N<;K@UU2yR;2S zK?nf!DMOBrz3DR^Y5=EyXkUxLE!%H>%^bMZ77!ch(oSWaK7@|M!t~=*DHkvQPahV< zK;TRU6KI@#vh0Jj_dpT1cPR$dsNt`(XLfI8&H?8Nfgrz6iwvI(P=I}rU*4SOq!~`v{V7T z=VyyM5;YQuI*4#aMA|Aa;9y=9aR9!hXEHj!54|CLL)fQLKa@9q2dD5*G7tBTcGhYD z-(vOpVC56B_$#PcP{Gt&uJ`~5Irrww_c#hsOOVX+aLL5d>N;+b6q{!Bmmw6uAJzGyH-nMSU6+$!wwwGXO)swO94E12})do)*q@g22`Lxceb7Vh8(@U43$OXF2?DiocbCi5yCx$%~` zSWVz^Sm?XMT9*zQpx65~y6j1V2bhx=qq$7An`F}=H;mc!5#a>u|3P!`bIn)x~4esJskUE7UM zYm3bm1D&66=%RyM$K640&HbFI%Nu`Q`bpzRM?tbB30(ayo^>TDrJMqYhD-g}32oi% zSA+3?WZc$J`%zO<((fdW%X0kyaaT3Vb}iCmy__%A)>S|%^$LoTP^YoH@(P_=+99B{ z*U91V?v+op@k?vJeJ{qpiXP@`lA}8e?x;imI)P$;pCc(obE)yj-c5S|sCCdkT;(U( zD`T4<>(5N^B*M|l0*DV^R#vD{2>!0Goc$Sp`>3Z80()8#AZ!pqIJpH%;8s627*ax~ zYPJ%QoWL5OgR=v>qJP%D1gwd9iKUMzV$S9^&R60KdwICC?vug0_3*4K2adeOQ5J-6 ze%&g|rDi-3tB05Ws$22=6T?dQbV-MelQYH|OKKz1*;z5a9RQFXhY560(YK4W+Xt0@ zs#;Z=i^sSvR&M-q#LMj7PW!@ZoP{4j3dAb8w-1me>-+u=ruo5(RUmMg0fweY)4Q51R_t~~fd^cjdQ$JRX%KnHAbdFg08w}?_y>V?#DiCAEw27ecBRCBc2WIeGRkpzhH-NEk6S=~ZL%61G= z76iV9<$J1$4w1$TIJ>FvI=H;@>8lRpQhccS_-{CH7rShXwmO@js`y4k8orl*?}!4R zHn<5CiROHTLI{EA;!kIQ1$6QKF^QRK3$1n`oIjdVT$eOA8PX`mH2K#}TAF_>t^~uL zrUeKa{Ns32B~20tQr*}PS?$H|ggy?}tF?UO*~UqVOSC)q7VAkcH*#+NEo?BP?|58V zq3jWcrm*}hMK$dK&^XYbJJ}6?+sma_t1ozfzjGb8SQp>RtJ?mC|KlN4F{X*l<{6)L z6O_PQyfZW~$)w7#WGu7(d_85Iwf~1HPFxns#ntuew~nlTU%t_aB(5s1 z>q2QBD;ik7CxNTi1_&E$Wveo)-$s2#HjsPPb8zkpBnHKPES<(7tTM$k+c9f772U|L zJYN^(e9jBUlEu&zvaHf6rsV-5VH>obl^o3bcQU$UdR?pY%y;;40>8#rb)#!IIrZl; z>kjr&Sj#X~G%))g!kj37R|2GLicN5v@8U30N&C?koPqUtH=Elg$c3%tq;vN01bO(f zu7F`r69R+{dUK^-A*?}0x_f4`CD{?9$KeNGx56?$=B4KRIsF5*ajG91eDZ6Q)@-e$ zIE)x3XpJuaI?aek^T4yNoK{IzNRtH0i=y_Is&>^}`FK1FB!ww|Ah`IK1l0H{KcDGx z9YD&7BRJIV$lVP?A6M-Ea?$b{~(|wTNM>#Iri{F!e0ZcbE3SnjSUwn=k|tM zTBAQ?KW9LsIRXQJ4xyi11yPiQE-tB6tEc_~1AQFD9$l-HR6R40zvg{c?))%$CU(S{ zo=NkVKc19I_bCWW&T0ZhUe5{yN2{33cfPhr5XZNR_jvHDjgt;%oIFsqcXiRt<7Dw5 z-p_;5%Gp}x6hwFcl(ZOa4J$B(lo?HKRk>e(x=en(A?xFR?n`n!oPtKNF0QfAJ-0W( z?Ug$!M|l$u_y*Q#q?lPG2h7PtdtIx~U-4akx=d2()E8&{#yr2k<8SnliH>GEYL~8xD3uINizTDnB zRh*!B@^eGw?9b8|ZAlQAoYw@36wUhwQf8F)sb;2hzv24DCtBuDGXEno{3hyHrn00H zkNaRPP(F3z_+jsodn9QqhY2)}tkV|M#?5cP|9kR(p7QC^ap?y^_e$K^J1O))J^JW= zcg!(WOmXR5MnoC{&$<#2xG9&$4RrDPq@6Lqw;>{78zVa!YPxM4*KpQ>0)|qC$k0Ri zS?bcWQDfabS!fm|6CQAgUof>jQWb>|!?*gU8xy(EF9VX5e(!rqJI7s^`&Y}99^Si3 zk(RB0N6rmitDjFEJH$&E*npO)iqZKK-4IY4OR+~+!4X?PE1QAfNm7R8wYdD;bX?45 z!i&#mQ$mKj!XzIIq#h3kwrjGNUx&c{imS-Ac`}_kczzV_^Hqp&oy*n{UmiDrz6xXN zeD!_`^GesLT6-#o=j)gE0}xWcfWz)$RUBe}31nql_`6QS_~ZF5{oxB?g1DH%LI8J; zz|1&lZ_y{FfK6kviag2yXdA54IH%!EjOu}MPlz;hQu%L=*eCitdltg=H_II2ThZIP zH69Q?507@uZdlq(*rw8Z0RqpG6{Fi1=6L}Y3J=ev8Q1#VzzJ!Ko$j}>k92T;AqyIR z9;l=9$t(~${rpHORUz-?g8+mDo^|C9GdsAt1I zAwYo|d?ZQhQ(M4xs)SB;u3pjNET?3{1L~-X(JOT^T##rzlkJMg-P)zV3H=e1b&12r zd4$}eI4eB_M#Akr_%`cqGJHz;y|A8=p!Ee`DF9IfDwy*6Hp>XiQ62QxO00Nx5H#$g z;zeQ_ni!dJ+G4?C-!DNwyn6h9{eE`!a(;dafh~px2piZ(8{A7<1(L^-3=202Iyplh13#piXi_-r;ykyD1S~9=)T%(18hwv5oVjW zuts+>zS45){fm%W8mH%fQeaGKY=Yj`ZxbctepFtRAmw3YT>6bl6c3gHk!2vaiRoI@8|<0XnY6H1Dmr&6D*cBnHQAHby_+h~IOba>(PUYl1nI@PCDT0UFmiOdPfUJt?whyYXpb zHVD$Mm-{#J@!f8JjNI_qE?_%t_;_rZ?n-qV4>;Gy(5(9>9*BS*lIHq`cuMi@w(ehx z_ft`fq2$8JYK|IVGp6(0E@0a~mab%&YYR!fg7SwIqmK!$og?5ps0_<%#zW&dDREx2 z-Ys@#`g(JeJh;V%s0!Dj`zewu-%sC8EhH_8C)+K_sRm$w{ITx1L31X9Y64;5Ul*;^ zcHJr3cV4F@vT2#e;9v6|tF7nxGAqm;eo7goT8oddnZs8Nz{r4g8c+WT^2b`xo>EGy zYejOqj=1yWv0nMV#9dJmInZE9|ADCE{PtAUx}#|}#N-Ar;2@4dd<6#IoVrOB?X>QP zjhq;LeMyCXE5P7)P>)w8UR^Bv721$(j;tkb()HTVcce2|^5uMI-MrgoUwxa)b6Kz~WidL$z_%)ghu>*NqRM#o zi+!zov+J`#qu)3a-NL`BMt$7}){%TRYs=JIP?ZaRfgKRwSy%c`ZW4n?Nj=Q=!krtw z1x|=s&D20B{G^n$S58F8s$Y zOJ-^uCvHSBrf^1|i8y_C)~{p8p2fi9JsEt{7#MIkXPNScFtZddZ6>)4`W=A9uG7x{@Oh})r#if9MMkwG}S4Nn}xf}t=up`A}`>l7M&&}E> zmZ*%c;uKVdZLE`kb}KooSoaM;eviWcRyI@bV)9hCvL@o(uitINf;AV zd1|tES%}Zfk7TlkMfC@ezs|<7`Q!O9WBAP~=kvn?dKO*#f5~z>bp5t=3U`ZtwD<8` z;^P`7>0yN3#nAA|#CTw(oaFPW&%5n?u;$$hhpNXxK)(W+yi#^6)9LeL*;pfS5Mysc7`a`ldf^@sgWujs?TmE;>CxmI+ME;U2=4yP77zl!Ior&)!n#gprG(@{GL%oP}?eU&G6VtPU5Z}=mVb# z6nRS1UaSc0cUIos#zac&XL&C?%7f0{4fsoK)iBD5*dx;ZwejtsKk8pMX{l}XPZh49{=lYwJl^FKWi(sEOYNs>>3)AV;f_3r6Kj)0Ql(->AY}hX zu%zW|r|)(}B*oH|XRyDn%5ODOQx#O#DpDv_S$iR+Hf1q7-^q3E5ev%wQNs<-*48-h ziHc9xQihRPw<8FDaZcw2)P&i0h^*o_ST~_%kVVvHvT57EfI|#l35bhvb4OPODs4Hd za6S(?<$UP(FGU`Q{&)7X*Y}=JZ2vxCh_QoyC&|qY5H=8A;mlu#Tvu^=qszzbm+6%Z zXTn)<7nG79!`xC#brL&FRjFM>_8(IDaEF86UncL#*ZLemes1rI^o&-X;#31gLZzd4<|=)>%LH#FK5de*U7$d#l67Hzs4sU@^jG) zd)&oieT_F?hfN^}F^3H%<0p_ucd!5+O0A1uNbi1!!iw*2vkMVwBlO5>z_V)?n=$3x zTi+sFK?PHPA2gNXwR9$tJL|X1%qTqUrUEJKAbBJ>b+B$8j7pStdKY5hFW11;9tL$! zJnPD~IGyL(uu4ZC<9qUtP!II)YrWrd!X2+Q!94tDMsydX2C)e8e<^8Cnk{VAulW*D z>*DVv4e9pBMo|50vc&L$cJYm>v$s2Iv4u=1dEc-95H`pZj zqeK^f-$-{)_TAM;b`&RAr&0TTuv2aY#y0AznJ;yX-_BHd|2p&+$zT7UY}9djv$e0> zkbU7d6j&E_aLjsykLp|x^J|ojn(@7H7<>;uSe8TL|i*EJiEU_@x)sVzL{pooe5I1WpJx(L)}c5!`$JM?Ser6cgPMd6q&Z z!8LrXhf)0wo^>UZQd?1*^j)>BR+xz6roNH>mQ9Qof)}?%ivoPSp0^Iz6Pd6ZR?8AB zo>ajQL&LiZ4Q`kQ>~h#@UT=WB_vXbPah z)`hTJ=EGqtM~N7OPzc|=;gMMCs| zC!d%*&xoz{70!nDO@5~6ejymXvDCG1p6BRy*DRqjK-gd!pWH7Cf85~EC(PY{VOF2l zW&>N94yzRS7yUrOV8yLZDh z__ulfkxyD_e`tCd(Ry1K&$`k~iEZ-(;Xy**jljO1I_Laxs%_l8otCl5Uf}~V`7e{7 z4@&!%&rMp3NwLY2J%jlJJHKg5Zj1q$ywsyKD%0Og^74+k{5cL`;Z zsPmP~l;A3O)|IzbSeLH={)Wc{jk2uR)F6Q{R*1yu{;tVn>yBXPb6&dgtua_K!vo+o zp-v-<5n>97oUD3De4SMgA}PMGw_FkU<6o)#ZqFPkXQYJ~>+YEqP)72gZs8Y9^%NxL z{sSHrI<>+Q!frije3`g^dK=8JxxAet6#AUE(6`R!Rc8bF+@z zvUt$^TFRp_w+L#SBgSI-Y%{RT)qCD;(BWa!2cAK78nbjoG?0dqT7%z*sjgx9J8;VV zaIS;16qvbquJwXDK59QzH8uA%%1F+?4CeC|A8zVJ$+DvGm^#;g4s&yseHmnM`Hmc$ zZ9;~-I&Z#E0p2r@^nTnV`HK=zIn(8YIDCBPVkO{yP8t_k9NM3&scx_>AB>RqaH z$HO9~+rdc?W)>4@oK_WxR(^7vdJs$W$ApxWvb#d`zqUTwE5WXj5ntH5ur&vvPGgpV zR0$F}nNTtzC~ro(=~>o)`V1!=Ov_{^(iOX8LOjpE+bHsXHp&H5Fx8V6qtFO;aYEW2uApdc#8*?r9QociV8d>zkQAngo zOG!t((Pv&dNplm|kvkSRAFc%IxrJA9{U`hCE7usE0S>TEW0pFLz*RDxTFXZRgSMM1 zzA9G5^dw7ve=Za>Ky-;hq6cY-!l&f~ard8H^CVT!#L)0|RKKq70cJjzS^AD$Z8)=E zIn!ARi@1R^S}rD$%U5?fM=&>QCh+w4Am`Y%w>YnOL3y2jdQ`db@n(p>_*`4}8lxwWMOBRE z@_h||x~`UXzS%{PN++1*-}O6xX7|+_Ts@!xvP--L?{&C-^2A^E<{ZI)?4&ih7=Q1} z{h)%SL21RJ^g1{XfU{J%m4Cb7iqG}Kqxv;p@fwNWP&^=uq8QC(`wT>>Wim4*9JzqL zQFZRRM*n;-xC}=O4}{%L!$<_SdFWGyNU$k?0 zyPSF!*GC2C_fyipSk%7H(|sERj(&upDJ;t((NRZf&`yL(-kNRoZG5kN$6Ia~$KVy9 zeXpZSfKk5QysaV<@91tY=pjWhTB+P=y6{85M0J4UL<3J^Ak&u6N5b$Nqf*BX6>iCmCdB^ATfieJIMefpfYin}amNCJ&drr+Lg zbP1A$WU`q+BFn1(vLv9KTpqMDLDDVL+sn0Rs+MJfRpysLQ^~zU>Ct1-0(-B6%C~ zzrry>YD>01$ICZOQW=X+k1C8YAc5Bqoko^N{8NwgCnxiKzXYKRmefY1tA}}i7$p9> zhCm|PBAGPM*gt!vtbzfNmIMqq%oT*>K8M;!*WNy8iV!}HIi|v2qj>hO8nMZ0CHnfw zkrbO7dgaXsWZ1|_O6UJbb@J>`2#p>m92M9-zC-)3 z2busfNgj^aHD~4>TwiEpt3hu`iqT5hT0kg#^T+dJB4{0zhfU`s*pT6W{$K4KOIN<% z*G%U!Hae38JvWqg)-XWb`yS7&OGtbY4l>7aYv1iPN2g7adqY4 zP=8&VZ==TOsX-{j*s?~EE!&iAStI+J5|NA~J2Mp7sjS&5A;uD6>?6s({OnoBo_)zO z_V<}#RPW#O+&T9w_ngmvIrrT2je!dTJ&1<4w^OJ=-qKsbghl)1v$g-wkrVL9C(sE{ zt5R%(!n8EV?J!S!6z<-j$hoj{nXp9Bep|I&@sHqQvF(Mq8{W5{O=iE|2%-Q*rj&yf^vIj$X-Xld#D1+HkQwZ5^ur&Wf{obwVm6VFVh3CaB*L+n_ zv2wCLPQKdxR_R}Vv~b~g;TDyc({kK5_3KHX37&GP{pZl=Z)0Kgrn0b|U=HtR;J0_M zj5#5?)H-UYPae|`-3v}uVK%b!ukPIHA&DXuIHBatiziFq-E%&r|9zg;kd~(eDQeO{ z#aRvqL|ioL*B(N=S2WX1s52x}Z(hN1BC+CZkIN5}>*L`K6zI|0X0TBT8rMe+_v41q)k5C(wFRhn z`Am{LmJib?f;`0a_Ahxhk-@YS$?Y)0dJ1Y+DU#ylI6L2o;%nu5D;fXcZ23py$VpbD z$S3mwqaXh##j|atqQq7A#m+*yn6qnZ-~w9Z~jYlZ3DQpAfyTOwAExFYCDtCl~?gfTdw`#*O_5 zMpfl@vntk$-45U)00lSH3kGG}>gK_r6w^yJPTHRNOu|3ksF)=Rg@{`u=s9u*1P|IrKgZmbT>0QI=|AS#?0KAA6z1zvwc#nn7H9$PLi}LMVEMrdUOKQDq zD0?OGhhEu=mHSF>PX0?tYC|g08iy`nl_=CFxLz@GJ50XrE$JH+J*-A?>xGP4+fr%X zayB28-;wlefD51%3X?uz-Qv}|l8t(Qr!Wh;&1V78@AwAhkH^NJP%R*Ca4BT&gYzp- z(gr~%H7}e$+@H|k|1s6L7ffX8U>R-f_YYHxao)}o7bNT!--V;81_iYL7GLzzyJX@9 z>!ydPv*jmLN#1uE+x(8b4QuE$;_4boh+B^dUMlOAt1m=gNJ@OpPIPr$v$;%v>R+Zt z4U2Cjp$ewVcJMRU7ItTVivS@X-Frc&Zn2VNXlR}lnd{@eA7OKm-TXMHC_Nk@3VkQu zuW9WxIbcPb=g)7f@SXOqMRkD4Aydu1JO468NE231Cx~w9o}xeXg#I%v&u65li3$}5 z+6MUjT(fE0DAaS$iHbF(hi#RAJ$g_110WKGoW zmV(^bzaScj-qmv0l&D^z0dYA*fpQ{Th2S)d&fim@jV*Dq?CG_gm@_xo4gQCz^r^Ff zMD;cdOS=1$q4FsWYNE2)$0+#-IX^nLyT13}NrTY8OeGm2%!&IWr2H6v!&HrwRD#R6 z9Fq5eSZ^UpJy*bk-6)`*)Se3`DIUVuPdB<#Y5I8Ne#f-w}@uu zo_?|lzoj4-{x31hVbeom1LAVetLl^B7bwu9jRtgADbB?fdl;p;*e(Yr8{;jomm-Bp zdcLL=0qu5mX$D>M%StFLOO6Y87oe>Bd63XX3 zcKqZN^h9Jk2T5Q`e4JJxW!yajDTy6ax$Efph<&sYzpTLN?o2D zZZX!y<-6b?@K=PbDR@Fj+~VH*s4qJT8-GAZ3U<}&w+KOh_$E6B5lHo8RlyM`0t6yT z@kfo_|4T7yhrx=-*r;K#;y;WHitNqe?jzFH#90j)%FG{V{$&$r**wlpDN%ea0U%hs zQ6@EtLrpA}-=c_&IY^aW{XfPsyb$t4#+G8Y_gxzFY zGW?`H1|dj)pinNeaZLl_y%K&20{%yenCJ5@zTwqz9dZa;hh_grSriSys`7Yl2Jz%g zf6pOW)yS27-+zfMh0DqjiQT`bWVsnUPk9VN;HaAPvxkb#ygKrK#GZXGA#&#OESYln zN(c9GGdMMchI3Lqaf{eU^Pqoh5tFeukGqdp>LjFpTk{ViWl*mPTl{+EThOKUq0XV= z1@v&0o}al}!?M%g@u`~PdYP1O8#bLlKK%pPDeY;B|QvA5dpNb2ywQ~@^KFPSXtz*h9Ul{P< z54kgc3H=Oi+b!`sqW7yVmfuU&JWj9JA-fjMy#u2?b2yI{)u z_Y3bkQVPSM+m|gM`m?KG!M9k*xKRD}(@@X7u=~b0{~^|M(S({v499!Ik7$uPSjLelmL|Wp1s-e!Dpnl%fOIo(%b{Op#CB%D>_w{uT zL-U--T=Q~ky^M_rfj{TEIgyrKv&zuP$6`vIc&`7XTG$GaoF*c%kc;E&{}B6O?=7^t z_D*zDX?l>QP-j9tr3>gmd=d=D-K3P3p6fvi_15=>|CGLV*F!n_*!)xMhwLKLN6;mI za-u%UM2mb%P1@&wj*J`xbThj6`+^;1hkGKfkjZh~c)yR4Zk*^-Hyxg9nXt@B8KR z|2TYcbqr4@=KStht;fSt4(DfRdD4-hCX#dPnXdrVjaEuo{s*@Y;=hc9{dLHHZqc2W zSXGVtQHx{9mBO&wrq{oNeUn5a@D_eWtdV>_R(R$khpN^dZp;o zn)<}!w=^$l`H9yPc;8P(I;%4FXv2aG92l1#&wOLs$>)VX$+>r$|9!%LRzUkji{Wu6 zYjF%C*H_B^sVzli-QV^oO#pTc*LY4=;q`wnhV*g>UyATYikdJw(^x@K*7So6o|g4*_wPHMJoV`G zBT|asOEtJz9ZK9|Rry+f%we47w}9yPcYp;x5Zit5-y?YUq|(p)5bckTPgNKbJg7bYBT1dQ%wJ|7852R-kEZKcg*qK2o=%m8#I-(UVs3|Tv zFkK+-@#TV7se38ASkSR3BSB#<%uUz0N?jKhKSwg&$^mK92Bpj!2WouXpT9N&{u+HP6$Jyef-f`-pn$iy!<6XxK{Y54k>^~D5g>WSL>lM zAJPST$`$b%DQa@No5pUE;!IM%wM{+e75klEjV>=if4uLH$1L%WZarIx%jvB4_(y)k z($j|Ql>ac*uKf^yE5|<^4I69?47Ua3Fkkuqil-T_@s7FJ^))k`i>kNbR`lOIyS|l_@5zjp=T->F!rmTq8unG{}@J>cSROq5$ih<2_o`;h$EmrlZ#&zh-DbXfMF_= ztE=UI+u(UrQnI`U`KPY;Tm%( zKG*I5T5@kfbe6YAe)7nL; z?@6QtYL%d;Dn<6w+{;6w$yBm34{jeM&Gf#oqo(|SQ=A?Y+v*@*tDFjzl_M72Ujsb| zm1y<$ZhDO4?#t@*0JWI|i{{>}p|knNcXadwEEVTpgJU2>fXFh1MNp%V1w?gitp)_bI=K*J>w(#u72jKZK2&)j{|8ro~aTeeh?l@aWy7Zm7G8o{LYEIzHB zJdqQBw=SVtlzRTYA$cW`XL~TFcK)Ntn4lR9q2$;ln0yLNQ_B_Z`jeX2K?Zo2=B=mbg13gKBUcJ{fZ z_<+Xs`RIv%DeXR8)Ff<=rQneb@zRukkKF>IA9?fkM@vA&MT4*iKl7HKzvn~;+aEyh zcC(5=DP}&AmMF(v0^rS!1am+h%a-??k5s|Nd{W$%BneQ<6xH9Gy1}$DLs;cyXGj#@ zMRGgLY?t;WT^fmQ=X{3&!%Kl`fQOf%ZIt&(QitGo`absu@@aS4YZ=b&Jl52IbeYeI zltF<&=usM;*U>@PxHmD`nCe1))ikL3%`G$8qgcIr=egg z1o=}F=T}UMd35gCN`2_kizP-f5Dft=q$uAE;k|OdPJ+Nue~c0Hk_2Oc@o~j7<28IC zt5~=NC744gu#0#|>>tYHvw-M#H#YLSp;`iZLP z1j1WOh6rl{Xob%7aUW-Yh+T2LYC+23r5T{OF&TF-aIvL-oyOUvV!tq;UU&uH3| zIWKkFE>;G&^rHyGtdz2`$uIPH>3(KC=koXRvhioH)c#n9t835YxF2#1o2>h8=5LHD zSV0$bNyHRDPtNUgX@nPbg$w9HRPe?DX@TG`Mq^L<|6s-%C+&nt|evZ3Wt<#PE zKe#T4a`#DtkW+|HwZn?&XSyBU2#rDt^k}B3`A;sPy#HcZpJHlI?WpOLr;{;TRFN_+BC-GvpY5D~&|a z7Mw71-F~g*`VVs>CNH_!&x$ANec>Q-I}BJi`hGtk7k>>l*VMQEE*z~|Hv5755B@Lo z`64Nng_Y@l%nc*5mWLEI`BqEoPQ=K`>sIsIU)~+ir^=+NkWLt})3D`>A%%CEG!6)OOs_(#tczt7 z>~0K{3d08NQ595%nEJHzIS^a=NPToLtsP_mzhaVqbyy2u-qvzI!# zawHhkjUN5{Ic^6pVprdvzV(dIrFe@1$mt~EE9<4f$?$9Bb{HGF7>CzDcv}U>9RJVt zD#JB@t=F~m*N%-487N*yb}*oD_K+$ z+E#U4FY`=z|5{AfoS--G9|%4ALD!^G1wwFHVa55zXA=5j9#jC(Krg>gkh~iBQr>tU zeSzH>l<2AGlXK^0Xq3&cjDlM$7M8Hr}s^Ps{5cTCR2fRW5ivEl=_rS#9>+(YaiLAxZw~VaHN^ zJjY&efLBpL8Hb`@{cHFq0(ZI^4R?)GHrUXTf5Yw74xhaW6$;D#;PF(7Qjbz0j{|ex zaI|Dc)&1GcH#ucE$`^Eb{#3^9f$7}K^WRQAEq_0hzh3XZ7IfM2Piu4cspwdJCvbWG zeBgNqyDFedh_FTqlREcMSKs+hxyqMW^=*su57KFq;49~4TyXtr=v-$}xJv1wFE>;V zf6FNNc!nz}37b}9w_1_vH?rQ2Htygrdi961#rO?BT#aqz+GaxEVVm)Bx3+-R4lZP$ zqcUPi=Hgb)evRDHZ#({7Cz3p2rYPFPx*f>Qvk4VZ^TiQXFYi(X21yhS=s{F7!M6h` zPse$ySL!=2mqjnUOE&oQ^0;;byJFq@e-?M&wsMU%Zf~4yh)n_LK9}d8_0ZHxSC^36 zyj#+}xSP|fFRlMqH#N)@HF9~&YWb?D`L=v-W&D%>6f|HMq^QaA$p~*t!0EYq>t^N| z@7{$sLYnA~Z(PUN9CL4%qPnijb7m)th1^>r)xeP44$~N|XBl;l0NaUM+%8HafA*vo zy*Tbk4}{E16mj80;)V8t``cu@-Pr&8K=tnE#m!Y>W9A5z6y9`PtSaU&?lQi@cecfS z&t`0O9Q>C`9<@l-K%GI@W-XOK#Vnn)0|;X1BjMqdN;ae#6i~7hCCtp1BqPU*HJ0OQuhJGIUHa37hht zdDBYk^QPcnpMRbceZY494v5uo0u#_aUy)w|7(|ojPj&b4XFWw+pqtC2f2(!bI7TV+4q6{$lXt zX^GiiAK$Mq3WYHN)D-AZc0JQpI;iX8R-C8M63(T&(zub8O)ur(6X|s+f8O4&G#u@t z#kFKpJiOGIL6RKsVaW6Eqy)V_cmB(0Tgk{swFCM;uD{<&v1#o@e?;KMZ5G$KpIkHx zq>lih+JPR#`zE2SXB1*AW^Z=(F7E#DNHagw%Ap{YO>spHF(+A-oYD_Q122BoH{S(; z4Qgh{0TsyF+B5V0{i-SNmr9i@-!^0eG?p~;XMIVMp$R^LZL9)3w(-{WeJ)Nx8Kw^C$ z1~rc;fX|Tfs+9$Ivx&&1iX}fL?d;NHkI755`>Zi4GQHjXX>$4c-dld5=KyG52g|6z z4^`H+sQc2gUS5Cn#<7k0cH#0EX``vUc}o+E{k=U(NjIIQf6bhv5Vq!rk5La`7Ld)e z%=;*~5uhKwr?=?hw#-!L@JKVO=L6}irwU&2tTC=uY9E{#xxP|%D4|_bETaZDRM`Mm z^78DIcg(4(l~KcDPU2(4!*w+0FdqH7#AOHfe!iikH>n4KJwuS9gLNr*eULVFY^B#@ zB0V9Nji2Kef0cB2ze@ax%U$~#RwEm-#Pve>Wxp;a_&;Rp6O-S>9p>hJ3;@`;s$D(1sK}*@0XE4`DpkP;+~{_$#-z^*tI`J3qM{#_Vdz zD|j455$n+1wk#QZg?LC+N^>pU@eI93sKunke;XgG=sj`GiQXec1)iS_I`)KI|9Z^a zL5uMoq$n9#d}88(aC7HkAbAiN1&YgKI#$ey3J)7U=WY@L%BM7_vHJZ~6!jEB2;+ke zF5;EtYb#Hqb?^S+G06~7^+==`o2*|Ed&DA}1!PkWzRQ8)q}aWBe{nSJ`8XX%+uZ!3 ze{%lv@rizGfV(>#xX)3k)s{fNJKSGv{1#JwlLHP5M~a%Th;Bl8-odS1rmdEIuHs|0 zHMd#k&zl#;2L&91R`|jk#cW)=?}(Jm+LjFKdg}0rF>W}{2W(KYcNTh}4AVu`O%GFh zYJcY53wSv6+IIB#Z6Hq;*0j@GtL+8-f9~*zfO`h|B^|?G<1{{lF4#%PkN@*x6vgkqwf;qrizy=O7HkifGur9n-1?es(76HTAz1Jh-< zPpxao#(4N=2;cFjq7Oi&3_7Xgf9pn^yNBld?XPR!F2QkcDA1#_KkIzBB4V+k_p40X z_~q~wClnLrL?meDTuIS=l2*cfs=c;GmdP^@&An&Ly)I;~t8fbmzY6pqY9+3}gsXtO zizi~%%U6e_yj6`=Zu6f0L$;=;>Ru*ViIASEA5#4@XhFr|XXO@gnQJ5Vf9>pLJ<_rU zkh|<`iR13ev_^N37>kiL`KY zNyZ9I?{`TXhGVmuk_8VLf4m3uAYR_N{tB)FqW6|4NMfk+sFK=ss*?(fPL2OYqQ}XJ zz;#u6*T~u@p_MFJP@gDuO)@~>2XuMx~;8#Jg4h5)Dl|?WpfE9d`HZ7o{jpYZ$y>;@FnF3 zZxt;zR~Ebmc0jDKIkkhAwc&5mk^nY%${nBw5&GI$5Y+=|9+Z6FDv4e2F&^(-M@plx3OHS#Z4hpjkkoOn{@tbMRbZ}5P$ zXBu!xP_yrjU)#0y^n?2Ofkq=U zM^cx&kxvYTOPfBJ35 zqh~)6-PO9Ljt1u8e6bzt7yY0hh%XsxiokF0KqvU?U0jrvCZqc-!>@s1>w*75FK4G7 z4>UD`Jr14(NKNT(G=rr#wi1&N3lvK&qZFoJ?`m|&bL8s$AhcS0cYDv3o|77 z@RB-3p#lmD6gQA?eh_-p=>1|G$cP((9rhwef9*eJ=?2W5 z&0fnwyljq$sW=d_F4YjveVTNO5R%cOxAmZ-P@8i2f>R45j*WS>YnLrlD25w)>~fat z=Led-UxLf?@R2<+Iv^1Es|J=a$9W@3Ekywc-fZT3;EzyVU>GAD(+|S#$74@a0H!n9 zt62zz&`P_rf)3h0rTxiJf7%d(ny7pMU-ouoR8rDnR-JcNh6+1gQgi1eMsXvv;Aqsm zFj!i6r>6iSo=+KI}H7p$2C+mWqHN+rw{&txar+Gq3`yO4B2Ez zSDEb=sL%kt9bL?aOT+i(Mjj(dm^G2`pA_iPMl-e*YMU2Wl#Yjxf2~|uh8sIxvaqe+ z?pA05sg%s)ci3_`PZzcC65x}wVgJY~)P!n9CLYdD!SYT7L!9F3HL&ye zdU;w@;o4(T0KX5?TQFz#?BKt$XU!nn9?K^gMTU9{LXT=^MjKF`oj8ZT?LmTf@tL zxFthvm8COC2vEq)ZZyP*r`!tTgu`UW?J#S$mn;E=!#*eJyB6Cn>N_{?I6EE>1)l^8 z^EvH6VM&tD8kU`W!*5Zff5|y)uJxSj&>@{d1uC@x z*);4`Ui_Vyii6rt)AS(0$lqY7vNo1cupyr^9)1U?GFvXd>D)oh9N2Uq`;(a%bQ!0Q zEsLwYyK}Nr6_P+>ZnoW|7uDfJ3FUc*6gBzD$Ib$%(;0YKsGw;5Kc?BU4s~t2ohNOa z9JnubRk4pmxrE`6-VNcP@9JbwE zF2!Vg<#>i{5WTBS&*d}>K#^pIaQKhwDGNE!K%j1ZlM>2v%VK8Ojj$}!OJpzpgenzp zs5}V_t3wUSt)r=P3{a2&J<<0FmK#X85zvEpf7Gq|6~xGcm`mNS)nKXCEOVMoIjPt5 zABo>Fkh^ZBt_iv*n;>?-4s$(t>^bTD z(Sm+BJQ69PCKRk~zA@e1U$u6RM=WP$3Uz`!lkPTylAKw9hyLgwUb%p4qd?oCE_3O_G1PzeR z1BA}?{-i_m<62_*KwfnU0f0K_^88hcpR;pom1Ey`?g+7L{IpDuz@@LVuDsmi-aKc{ z$4x4?20{CrJ+oGF``nzdubjeNnE5|Qf3(8jDZW4tf=(dwEx3z=fD3~z-b&aYVDZnd zJpP&t?%H!XtOls@H%&`g>Bm_$fPJ$Wd-aaKzQ zO{1Nse(QcWCE120#YW=bkPm*^e=pqnWxCJi*Q}t+Z#uzH(j7hrKmk_!DVD!n@*UN7 z*)tE>e>`_ys~9`(v~=Yio!}LR&9zegaWVkJCujlDUjbCZ;j*Zlq#V0C@AT#WIPs{$ z@Oe`Ygj;10CYZGPEw-cLx38vEsB+bYA-1qeNBeGQf;GPez z*2gb3Ls&9^4An?5o67GroLae9Cn{F3tf?r^o_@5<9S8eC(l;c2B@%vm+^$T!+ovBb z^RokE0{^bB^5C)#P70Z6tdurHEcKCPW*> zW|8W)a@Rg+Vf3NJ?-)H~6vi*V2(ZO>vtLO7HpLV4oGr)qPrW03@r2Wn8>(xT^|)_S3ONADMQxUto$f zMJ)%*o%&N)e|$vOeV!-y9OcWibid{o%k4XmifTfw^WyU&9-8x+#WxqZ;V{C%E01rA zg`1(cr-rO1Y$FJ1)YRqR9g-2S3N5$J#xPwgmDt#-l{;{Kb`VWQ4kYtfKsE!V_c_Rc z19hy>chM(f^K!et%fcLX9QyW!Nb(azT~_Mp^}e6wf7q!y@n(`r!ja~$?0%75x2#E?`y6%@4^Z`he~;ns9se_Y)n^Y{J_NA4#0*kGGt9A! zIo|TzP%yXFPGc90b{HH4<^@PnBpD)nmL<{eiPqk_*qTu2$VBQI!KwAmyyIT8`}K-ucX>o; ze^0l*_4T#CicyXr+YwH!o!?m8M+5mzAE(LRj{PcW$MP=Zx+5R(1{cslVswVyH8tckS|1hcQf3sEhb}7$&fS5Zf5cUNw|Pbdnvp9#TmLMY^2d)&cwz+e|&k@ zueDsp-=Y1z=3R!DifLh_v7cn9Qc!a zzh0M6dv~Yvbk~jQx>Uty zKncB#^x%zw!rpUTQ;D`R7F(>aG(jv?Fc{{S?hcid3prx@UVFAC$ao~Rf7QwLbx0QU zy7wfBctlS(tBqV2%r@j;0ziBMDG>cp;CvDs07iYl0b#j(n~WK_fhBW~AB}seG_jYp zj}Xpn_B_|k^2~T|3UgSL)SlEO$Xe)&*J}DqY;YVExjjaRXZIx;s*MI8*Oql|Ii;ycB~b5yMwf9w6oP_IB=N7?!O zwZIdmxCXb)C=ZnfJ0}#q?NrCN%Qfj%j;z9q*1*N0R-w?E_y?a9s+NpV z^(B0S2G&l6GHn?UVsU$3qOVZoGw?#S|GKfqo%2Fmq_VKhqRnU5@P zQPr|h*_RCU3-opLe*=FY5H-~3j;;fF1tVrgENrj|ZJh^J6>Q-fA)Z&BD& zS!Q$c&gC!3$N?GZcK^A+*S%0!yAEnO`Kx}|{=Jh;9P%Lp%Xc)h#+;Abz&?d-Px+(0 zHz;g`|8K7a*g;wFY4CUc6bOTZ{VhdUI`FZ#DyACJ03F?_e;Qr9BALkf?I=pQbLDw1 zeb_IwHaK_yyW-pwYZmn*m=X$W=O?$vm^r##B?q_}2!U*CeE)*L>(h=km}K!n@uYH2 zv!gt3!Vor?J|F)@u4uIZjuU^A`%AVzak_#1IRLU`X!j4UxH-lVp@iC!O0&w-5WWR< zeYSCtKGO9ET^v3P`L9 zh$Ueq>$IM#KNw)m@!sRCT|R2#gcSo0UK_p?nqokB+r{XCN9+gy^pqn767x=*@-4g) zB*tvyb+2XSCpr!ydmCX^YJH#5bXE z%GSPX>Ks=!tFHq_O7^i^*53-hCp zT|(n(xi}Op9!J^ScU@M$Kt2xyo-lwj3D(upUPs|6z7VH9)TECf#-8FY6Py4~3Pk^=f7&~CI8FlK-xPAR8*T21+_P&lD7D^q zY{V%Y8Jz}5I-0HErOtUCbfTDePE$h{^b$G13%05Gq@^GUePsgkG!$-{yjxud*&H7j zDwkR1Ws^Q4i~)?#IxtJjmQk2DKZx7UQcb%4gl#v1j2y6{Y4_)+MYqtvmW5D$**gL} ze+4F5!&e_!7+cR(L2wR7#92V`Qt?mrF+S0D{=VVP>#@Im-B{B6&r?E`mEcT*iAIet z;LU)u-3TxKy3qJqa=edE^Y0r+m)h7EP-*ycx$8R{Aq=M}Sf6}Li!{&l^`Rkt2%;Y` zNnhjzfcAOYe-60~yikQyb~Q=toz<^;f3kWMg50QG=L#5v&G^oFVm#7LpUG}3S3B!S z1B(bjikS$1dJ9JNQGEM&A~REyAMY#Fv{v)x>O+U*^E+RmtVh0X?N@5cx}hLV2wk*_ z-B|(gbubhbQGpaQ*_cw~)G|RWtE)6s3Q(L3*^TCU=(s5!M|&u7*@2w~g+gnqf0CP5 zdQ^kGli&PgQv*QSwC(51d6=0f(D(f; zUk}=LDllO>Z?_!#JHG!cgIiBSA{hz-v8mC8jqYW_r5i~Vs$_VI?28|EMuKhx0vWJ&Q{B3z$4b@6y7~r<$}Y+$n7zGeC#a1 zOQ_A<;|j+?p?q@>I?wM2>ah=|1yomSI1l3)JAG_{R|kdY9;8yBL3VH8(iyNhD9qIi z97=||3AU-B`_TNF6Z#&@e_i&_s%lqw%*uHhErk*Owguxn#D`E6-Ht58JoM|O27{E$ zw2?-yU@GCdR23w=nZUNb(qj+}gX_~_uM4@@(+r@K;amUZOf3YqT|OL0iQ=IsVi|T- z*ejvu{7LmfYDGbk1Ehe2kAl9ALiC?f(!Nr{!RLmXl)Qv?oMWjSfA%px_Xnz7ANndE z9usQNx5nvh|762}2SQg|o88nG0)ns9O(-MbyFfo;rC0d{NSpqA*{I)qjHTjfU{k$9 zZ1BC%WC@q}AyRt-S5^9*_(rf8{P`7jEqmNDp30I9p3)BVBVGztO;Fx7N!hX4-0Sqe z`{2gI_PVLvw6e0Se=#RG*`d3cK@b3UtZjX zsiPLu-wkG9*t0sW{NfKzR(8^BI8@tr&gFUE5#3n>^JT|VVcUz9(L+~TD84{pTF=Ps zF(2B4Qn@5)}dUKMo>GxWq^hn6mGVN&Sse=^*B`F))OTE*F{A(#x6 z0I{i={EGN?DdH_0Z#yxhlOu>1{-QmjubaH_h0a!c=gkA+rrA-Yg-ds5p$>!8A`U?| zd|*N#KHyG)ShGW6(3jZgAL|U^(gW^a*a3#FwRa`eh6mo9A~(*1z)%T2IMbp5#D^Q6@)|H5GIddBdC+aZiz0U(F)Q&=;j%`4 zA;vQ}9}w))<`n1iDf-&I!O>0!LM(24{Q49*ph9U=f5WA#(EzP4!~T%B+wM&=tX43t z4q&-^u&3o)-o}0ig;0$U^l;UDN2wE}G9$xcn*f!RI-?s~$v+Yx_8jQzXr;!FHcDtl z2zErjT)W!&-TPDFTai(AR%w-Y2%CUWqFO@dax&z4gFg5IqhHResBx)4VG(IaF_U=b zC_23x3xq%hrPkvY7jtf_ zla15B;J8$vAHmJF3IL#ddIJ{=g@b+(J`7T1Et^(Qw(B`;?i*A15hk%PZ(3xk+w0)_ z;0wfr>`wL6F#VPe{H}q7Zv*{^nO*~b&??Vze|S`7{B|fL$FinOYJHp!RO&Fc2(siF`o_}rnMqS9~rE9SDZ9vgKU%JaQbQT8`4$berMUo3vc((qyU$5D`?wXt<5cj}m=KTXFFTWa-qcq+`563UZ- z6f?O&C1ek{QR8zBi}gJ3aU)NqK^`qUe{N_>pgv5|>J?K;K0fN3;LoPz6I`As-^u@s zx$`d`d3(pgrzwelwk;dKszEmD&2OKWZH4}-erQD|lrL0km*TGXLEeu6yB{{z2$u=Oqq~PXp-AY{6>3P6fna87CD8wLkSF`B|J5oD*2e_AE@DEW8SUzz;Z6e;_f`U+;mT z63(cwoUjqKO7_>a$yaV(j_+sEtUwnX!pboQiN4ZcF_tLE&T({i+&7ZXJ?P);C`W_h zS30mTZ%WcW*CTe=`e=`Vf21(ICPV2^$Cg^Dac05qfl!D?a10!G7czS|Q6BaXRiX48 za?uCn=gkS|cBi+DT)jL+$sq=SzzGI$rp52F-l|A=2+($!nYzJ9>--H^Lc+XcKF5zh z_DhFQ$rc!RCkAC&34_M9Rh)&8F@%Clb2K$t^jBoe?~yiY2iX4)(SkY z=NMZ@++FXp-k!O9xCbK?f45VfER?MXKon>|C&RW|^DC%4bZ-eAx)f@Nz@VfPw%$P!qr`Q^ zQOER!j)F2_6WK^H6NlRJBto{z61P5=LI29P#C785??DO452m+xh#Rm&%^26M=gM?m zmEl8KPsz2<-E==e{Nio9e{jBlFFQQNnqV2+42g_9mCP)B3Qo&R8~L@q zRD1Qtt;1x`oo{(`wl^zNGk77xXxa5!1+teJk51=WyAFWBw}et43|@gCWhAx%Bx|s4 zRIQ)kn0C-(e=U@Ne&&5x|M&p?wYcc+-0;ScMU#r9J9d3Z>8b+g29gX#LupeJ1@3rA z{8q;1e^0~%gPlV6=Y3B}l}Yi7i-sIxPz}NM;qHy^gJt8=uj!>T(}!plCw}+xgQ4|t zNHLRE$R!g16vmIh?6t2$FGt$~C7ud-N2VW1)l=C&IND&FI1%309|91a6Ri#C!$ez?C$lyqRx}``B_ArC7 z18p$>q;{8aWRam_>Hg$A{rHi|fvEHE`@mnx7dc=~LqJ!)xrQIr&yN^-A!_b z!J2|Xe~j5g)U`SMgUmB}>AXkAjC>N}r`3^Us8xb6+IpfXp&3Z52$esB=TAoaZT+s~ zGRu*4x>#EOLnmoY{Xh1uJRYj<|1-~cV(Jx4`Uq_z$NqwgYdRK_HQ6I|JU9KFXrX!3(KT?duI2brO<wOQ2g`| zfBq(U5tr13$2KjuOb)S6UV7vR(XMkflb`PL*98ORyx+jvzm3j$x=ps>$XO^5gA~Dk zy?8fay~r8HREL`7)VJ>qlbI%F4rc2W&pn3p5GjeGgNzKo%m+yy6RJBqSg3||yT2!xia@sVd-Zj0a8d)AQo!Yg)K(P926)$veI z&56V~nER2WfIYmF7s@0B7e9=Oe`sP%-TSDjs4?$0Plu-r0L5QPP?@*kjuD>4t634) ztb0b9Y_)j5kTA-KAp2AUAOL4X5nJX9X@s@;MqXwd`KkqHSNcOD;m?XqKMP2hUNe`2OPx7qa% zWhrE>VPEay`SDeR_Jw;YHFpXUf>fuME(#Fhkwl{T!PzxlDD*yQ?Sf6^_tq!x^4Fi6 zD$FeozslWsO={^{cwo9h!83N6Ps|(27gVg}l+5QoryghBekUdYNQ4F8oj5bcNeH7r z$yXfrOQTNTO`a`2&VG4Bf5SVs4yh>Oyex|=E8*aOS>*ME+Kix%!$AsRz^LqqgzG*Y z`)X~LL2>le0DQp?zb}V`c&E^=^kWjM^j;>#OL%*~ykz-t+y0~e`=%qGR-Cdmr*`SS0~M$ib9LlIT2)|%IsI02M{o*LbHSp?doYtC5fV2 z9Pe5d;_zFKD8hSKvqMsvc4yOn4-HM*u)TJx^|Uz|CRdh2j>VWDiV&;l(RLtBksp z1lgzzPSK(OLNGLJraotG3O;mcLEDqJN}gJbj8Dg{6Z`PKZBGqPjH!C~YL zg7>?9o4u1J{0{l9TNHMbTUd#h1Yjd6OpDv6nkOoZiXVSCnY7B=-XY2Usz^x8g)_zZ z=`Q6}RgX%yNWsT6k6$A+Eq>#%Bm&#?Ox5eXi=FSwYqv&Q$2Z}TqZHBnW#|5!A}UdS zoluzP#fm*7f6a`${`mIGHb3l5EqGvAi8M^h@#6=z zveNatf0f?xPA2J+Dw?K+=8c9|XIdyJ{O&0>jWn~HdYCbkruXfK^y7~p?c1aBm@Ca? zw+ZpeMP5!_v7S;m-^c0w&iSH=H4hcq)Ad`zECQf-<$TKC;4^2-3`>MUZFxrZWpw?- z6KC%>KtaHGbAoKtqU=LT!YDr69uDeJ9gvbLcX)>?T zf4+Twa=OdjQ9OPY;}x5HKj%PB2+YSa-bc%0)kkozc=K{l^%d>Y-q%S^fWl{q%)p?N zWB&vQ0FEakqkpu*^h!w@TXq&BDulX93{DkFvR_jfD6Y4AdiA#IWUOzM$LLBKX#Cee z<6{;|7=;d2kVqO|TV9!8n>2YshZMV-e@@l)K8|XIvxQ9*n33G-wCi;C<3|Z8>a}uA z+wJZRb9oYg#N`0IQ{s(rlY~(RrA0#ur{GI3BwpTe*UF}6x zSCz~gRE+k&wbyp6(y1nk|f6J$b zRAgQy!&5K|^<=7L2DT||vmFW*Yn`ZJcb#SX@BuW0_Xpse!Y|CUN1?CDym}U0`a!L1 z<`t7esk`57y2{D;Qh9dIIGET)6kn&bV)9cj>SDs0MQPTJH!9oL?P!A!9|kK$V!WIt z7(z>rWHf$?6Sw(X^~@_ahZZcaf2XMUwQM^S{LX)QPB9#&I`Ymm+$)<9%v`kCxa7jK zj;psQwlV4t+#zOS1Mp5s6^a{B=xgIvBr%=^>@8kQx;rnZrlTRq^T{LEcN5`{iUms_ zzV6401J5cfos8n2cv~SJr5BUb4ja%{lJTCE*6*qh+ZUOWDg=SiW18GHhKf$r3l{oojy1bnh=|q^%ZlJ&y-4{qs|$6rJmUf9#zFJ2@v9~#cRD;e9R}(wg~aoihbecvUez-KP`oxb{J2tf8g)Fd-TyY$Rqf` zyC;nYE>3lNF4eRq+oSXdE@p9n5bsY>_cZC#&p*!~L`I`j=53g9>&A^;*bDj-Mt97}rACr$r0)$8lR2~!j8Q7ovj~xPsHx6VL!KrnJUkt9>W`G`qc@d3!<^1rN!=#Idn=N9a^jb2yHvA` z8ts!ca(fsQlaHxs@bAXH zs5-2{Y)&#gtFa{U2z-orUXgzFAu*Zx%Vx~0d38Kt!CB2`%}UBBw5T0{ARC1$51c^& z)OeXYWDVK^f478|EW3EY;G^~EO|nU>gRfp394m}Mx6Hdz_8?gCUC~FOy)h{#uQSAB zOUi3!e1cO~nl1$33mh!JY=Lu#+~4c5ieGqStqw}Bx^|Pj?q%ZpEvIr4wrNQKdkzue zrl%R&C*jt-jh!O%e1ZUcp;)fYg`B}>R37u-WuB<0f5al=$kQjAtL8YIy(J&&ED_iI zrahu6eE+>u2SuR8ppq)%{tMarZK-^~hd0V?3`VB4(Q|`0oPl-Rjv!lg%49XX$A;0k zA(!OwIr44jJIOI9{_6Sp$ME$WSMRC9DD-EQEAi`{H zm&dF2CySyQt!~XKSa(uUD$N6qi^WlSOg}>H1cyRAf7UztpBQLWp(t_qnor%Hz*@ftGG-;=b2cBVFR{9^ zq)A#Bg8661g|g^VY~0X;c?Y|;GppI`q~2u?;`+(S)+1*puJ`5Hp^1#8B8ejYJ6s3I`P>SivWTZ`e>eP*uC)|rp%h*xZE!@e}YeI?PxRavBuBFu9#jtPMxu?g8C%gL|PGg zjg7>FHez;02=QKtq`tXV6@fn$kiKD#3-0qVf5(PIv-9_of_8=w_#&xo7B0@8-rleh`W5i$2{$eErgRWxkX`mZz1`3T)l1M85RI%36ks-=tKM2Rf2QS{ zHPOp|ht&3!KtV`HSgDHh2cvV@#k1cDS2SL)t}I^{;=(0@8-I^G%ER^RWQB_f*WoP$5;aOq;-s$tqLx3=9oY zRYoN(ff2j&YxcT2nfRWu5gl&~Xp}6xtz|;!WztQ_zZd9k zzJI(-A(SBD?b{r5-iqr3e?|W)s63|6`2*Tw5{oQLs+HPv<_PmLM4~*j{U5h9rscBM zU3W4-i`rQMdXwGX=EZ83OmR1mId{Q8`SG?#Ps|jc#bOG)pE4I3n`=>gr3K41+m26{ zXmSFMEBi;c?0y$~X6KVdzexdmwh&19x05e>mp*!CSo3~y37a?Cf5R%`3WY3r-BA(> z32p`8ovN117Dl02%BmE$--;=^)ma z){mCQ)Wd>)S0Dhfe<;VvIfk)Jla4&~khU40yig|;6TBM>3Z8oKqyPe7ButA#2hE*{ zLbDXFkTbq4sd&0RhETEuCG(=L^-ZGh%c)LB66PqcL7{O{E8_hhR=X8Xs`^rYT3n)I zhU&TYg5|BDu6SvTNkESj5JF@n>SCix!k_I7Zi zr6%gM|M9G%{aIeFWCwY|bYWDfl&CoCymRf#dW>P~%?ym8qAF)&cGNps>9H*_Fb?N8 zAv3T-Jq%i2e?``zT32}%-1F3Rs`_+Qcb{+Xwf6ef zx)Vy@d^nP^OIvF%Fz+HL)fk7(I0JB(%awCyZ{CfS3TCSb zKfjmfFdDoU_ebQg>HHDbP`U3T?1aR{U&-4VS(NCNfr_PtG7buVnr1< z+5Nvy8990pT6};dD8IAvVfO5;I7qL02zfRVNG*7l->+ivtH`}m4=;pM2sE9U^(6w2 z5Bqkr%H4n*TFeubvl-LDr_If&S?0wdp2oMj3p*#tC3uaUT;Tt)19ze?UsE96o? zTcU5@45f-B&tkxOa;DSg2QjtX_Ii10-zsmcNucz6gkBQd!Sax)r|eMs`7?Wei?D54 z&&IOFzQvwK_e|kQ4i`OJn}YhI#5`_+AsDR z6OEW77BFzV<0-K*UdE*tDO!`WfH|GwG!kLZ6W~-FqEk4s8!3AKwYRkRLNmNT?A6${ zx0YBQIuD;1RUXx#LO7tL`tCE}|B~(iU7|hS@!!y=&P#$mKYnlSYz$d6vSAA`f;AR( z3x|Vuk9QT;aOzCjVTFAU?jkxqaN84kk!-CJ39MR@D-AGCjcU0*=B#_ys5lpi>KUz# z*u@{Rlnvni6bL#8f_fOSmZzkTPQJTaX(=&ehqdY=V9i*^aYj!w%*E$Ae?b5EPmDim z=OFb?I>Y4*pL;30R%Ix(~?e)H@29yp{nT4}%SjZ%J8t2T8NeEn9#nk=@P?@Yb! zTrl^5mHFB1q28H>;h3D5Xmedp+&jna4~T(keH*?VGIF)}VqDIga|isSdUB;|S_A5Wy9Uqx+hg3ijtbKJGDoH^yJY=FB|pO@eKD5aC(~d$&I(Vw3S2o@6AVLy@u1P?t1J!hZj0(H^Bgy zUWRF8#iKpP9{a_f^z{y>WZ!)};6A9}gdiE0YJ)kqJip4-CJnXjTDDhOu}J9#F+Li% zdVV=(j&+qGsWUS<%gxO2W))xAus2MT+tP5)0s}1Jsk+Fs8A-Eqb*PzM%XK2NS+6yH zx~|&Iu5nM`R#2IY?~k_vG|;wTNGN1|(f8iJDDQV;hknkPAEu^sdPTYpG{6hl3Xz^B zJmj}(56B!B#!qdn9uPNW{|!E^jmM46<@%P=XdaIASeSRTfn%@*-aMs{3%|GU`^*1(MV_ ztC(gD9n-j1K}*rI8+gHIHr-craq#Npku;ngOCM?2RvsOKUz>uLn-IpVf1|_k4;&d@ zf)T#7r#P=rf5}fMzbm8758c9ee3RLsD{gzX2V8Upwj9^2Uu&rY z%9HJbO~P(doK|{TU>nPxN%88GwJ>vA_E*Byzqk}%ukKwnJn>?ZL6lnQ=BkIGAC>r7 z_c9&`n61F%P=FWQRQIrdTYZ_Ati~SEPK!A_nfe`bv&Nql>(c4^%Q)FYtjMa_@^5$0 zOAC*p9`4z-Vb$MGAc4_|nY~0}(s6O|w0cOcc$tH&i5u@CPZRi8QJn!rmY9I?5SK=D zses;)$3NU?2A{)H{}GX9=sIx;Dy@XIcv@0ms8{3vMRzndJWprUGy3C*bGlZNhAtM3 z4jK&;nVQoO)B8RrGSQ)OeMl_`+MSknkt;u)Q!vu4S3TDPqc5!&R;Cr-i1S$6Jgc33 zi~Z&%V;2D!X|4o8H}{OcDgCA24P0zk+@DpEV{@|Um~|WNM&KzZcA3{I>}}2|%)SCB z+EOV%^jy5amWaO-xN-nP@4a{JlVZKE@UZNa3$^fBW8d=TJy~7MTGwh5GN-T-+t11^ zi>w{*D&97rSft87b+Cq2(d~j1?{8Yduc}5ERO@Jq0oZ|Y2dJG~X5s(-!NV8(4)dr~ zVY|4u)ZjJ@$2Y`mgjwH>%G^H_ov;o#ZZw}Vw9%gb{Gn@YPp@@q17U;WfXsDYWy^}{ z);iYNZTFSzs=`l5ThQ|hkgVf6zjVi2Gz3wqJ_{58Ax!v_T6rkD#`=Z*dK_08{dNw8 zfuTjg{F<^_hC!2g_HXrxjrdAL4?d|phpHFK{{*>~8VC0eN@fz$7M(3o{)lasJNl{Z zMi)iKYBsqhByWIwmZdUS!G?sWoU!S}^=y?k+Iw|xR^aIT?9tTU^qPfufBhe3=N;k> zVe}JovLbiOhrDL_PMnR6E$6! zDOi~xZf&Td^{3ubh(Kf=oW4VOlHdEOdJ1s=+>RXYr%2=y)%S1=UX4#G!vQj)!Wypq zv9G+|Z`%_Bzs-9?Yb^BolyF$0fa@%fCK2%`inmd|mV>2L%i9>6fRS=vN%eGnfJgU{ zct>2X_XbCXHgmTbU4v+VEY@3(ur`vvs_^tb>1w9W&i zW9jlk?m1|bkd212RC36K%O7m_PXxeUTFda?{6}#hgmT!D}szv4Ze5T{; zC`K!jhw&tt#hIpse9o*5F6S%D@)>GOcbFtd$Nu)at`m9`fa;)CGzHDzXytn_->J3s zj}n>6-xeK%&uyPDLTk{wDIRR(ylO!1mXhi8xi-hP3x{ydlwltY1NX|Z@H+K|oD@{HC7EJnX+;``nlb;IGTEkiXT3* z6Ut;Af+hh5-(NXAz*Z_K|5uS)hu&Kn(JW1urcl^e*=qI%2Q=*9qA1ih#^U?QRp3E( z*E}amT*TLd5Sx3rkN;F5vhAKysAwa;8e-&7*L4D-hhaAEwK4&d>bA%qon9;!P4dp? zo291NEG-0md!$7Q+q#1_eO|?UH%sYV_Uwk0&kUxJ47WJ?Hg}_d=Z-Iw9Xeh7UY!== z?KqsS11%+VzMQsXLaaA)jWt<}SY4`bs>@_sMXgxmgKrJ;I1z`1oS(MExI*7@*-IeN zXJ~fjD&dkazt``Y241oAl}=9Bi)J|V&8GD&UQXFrCB+qy?(J`>(!G5@HK-}m4bVC@ zm>t*WVr6-JU8kP`>K4DG2bnDGwngtOJSTZ^0%%GH%>84D1;ac@k*`f+^UIa}Ms$oPfYMnf? zhyYH-o4(`F=yQ=?AyLgh91b2pZ7ow|n}am2jQpWXr-mB{JtSa@CDes^X0O?Z2**sk z!s#`Du=P!Id^PKCOB$tG!D5M)xZ~089TZXA1tVut?pC&`jb68Se&yIQy}ue@a&>r5 z!o>97hW#?(XXa4W((NON;x8)}Z08jZO<$;SEg$Gj zY+gcEqaDv;5aqd+-^RGN8q}R5qyi85Gq2&Q7-e|nyZZ@4>N(xH9@99=emnQ@dY-xT zxauZqSeoHODJ$ck!p;)k*5|6W>)2iR zc3Xs#HSJDme48xw)Vw)kGTmkh_E2Xly8Jm2itr>70+#?xSEjCSiEtYRE3XEr4%kNs z8Faz5#InIo#e#tlnF|9RQ8^^ha&x!-)iuR$wifqswtsndB_@ie$WJysjB|wfH9=Pz z$-oM6fXCc-CUS(;A)-TUd(cdR?|Bg1WpPR#l3l%2z(>XP`~KvE%k|W>7>Gd~igwJJ z)vMyVG8rNgJ2cEwDGis7d-?l^J$F7!+DFh&f7B41#!qGlG%?tUl2HuyRv^DiwVb4? zD~P^8ywX*|4(#A#K+QKypkE>MEj5x00N~Eavq#KfO7#rc0|kEN9q;>cO$LRUyDNQQ zCb}%RM;GF&b;uY>zV{XrY4EcixYWx zrPk{TJErOP4|nK$DRIX|^^NsE6Y87=3E#WoR}~aQ9jE=>s=N_G(9~8Uom4eIosWg1 ztv>UMT-sdqE_p=h3nK=MK_vC%H*hAdxLy+y5)BfVmJAf1bMs}zH(2(jhdh;G#0{U? zERZDe3H9m_$f{7K_(mz&w5#B2T+?A@D&>a!bgDC~^@nA#Q9EVsc@Z@09X~v~i%Bdd zp*Jz5Rk*?|vzRpJs+g9l6_Gq(n}!?wR+IB$qBak*eIC9QNv)@2awPc&4%mn|(0mP_ z#8^!=hckt5Cd?!LH2{BxXo z^~&d`*iF81Ba6FdEn^NK?E9mM**W6VNX|^&ZFD@2ilYVc;nLOfmqlafPp92DAr51b z|CXQ2!n+jkijxHC?kOqY`vK0z|I*m5dEFi;jSsXf1(N>Gvr}Ru7e`o*Qa<5MGAPnI zu%hNpOFD;6nPlR_+WSOIt5s~jm4oxbIFan<;kc7GqAxvp-A`u2SD>t`nz!n;6p|_h zbO^;b!(~p(2sS-6$5}Hw;}4(9gCJX!kR8v4+eBb&_iarnxt{v3Ux3Z*#7=yxh4syv zL%Sb1>6%ccwU_%~%5-+Z5JNXB)8$%|!1M9H8(}zw_7a8YbR6LxNYZAptDKU~256W6 zxM>(uJ-5-v60!ouO>1+Qy6|xv(j?}hZe*)2ik`9u^Fyf2BA`HA9F+zqwE}b^x5U^T zNJ3-n<;g@1QZZU$DW}ym6?lp-94? z%c02M;fFk<@-sBtr4mY7oHLbfA<|ik=2Ej;qM(mD)B7H z1Y!`4B*X1^frs#t?X&V#xkT-kJ3r*uR~d+gVO;MJko~tXF)N^hJA$Jqo#&m#`~}tx z--FJ;c9FRNG~=qyJ6kIe!ah?9NuloNX?nX6=O9kr;a2HuagujOjPX|;ARiG4r89cm z7#s_7Go2_KOHbrV{cG+g8MTj}SmA_!?2nJ_`8V=2BPth$BSOls-RNS1{&qq*EFXa7 z-E@Q{o=jKg8wso)%=M^u3-TQLCcLmZYL0d#k?0-|q^Hvl6(25cwm!#f7!d||bb9UY zP>t#z%7x_L)AOeatvz{^JS3pDG0^elbFc{Sj(5DRl*XP-A;RNBXJ_NBzVsgo5FgwI2+-!{p?lPBMRQvLVF=-w(4R zsUhhHB=x{4xahgX5$C`vfN^9v?CDoKKiIc+`rHib4&?tmD3t0dlO5q=Qlo-GTbS{J z_d;Z8+v*d&-S%j7Uxk-_mIyijXl*gPB=*yNaJ@(&V`tB8n~of6S4b9lUg*fQbYB(T z-+2v3uS+`*9MJt)iY4S@5Osg%Ozq-XH!Yt5;2dph7ef%P(2ML(_Vm5;+rupNvNflK zRo`}~tGTOL1e|;A&UE%XGOpN-A^0R-aGbIjq9t7^j42Pf2(ghSr7GG zqd@d8?h|rJ&ir%@Yo8M`D_tbb$Jb{25TPYvsTNmo%afa1+tW4rkU~uf6S#4SjO3FA z&hqkg_%glxD`n|ycl?tw1F=;{nup1yF%<*a4a>TUSkbOhSfurm{XaQ5EG{rMbyC%+D84`sHBkgxw<7ZjAa_y{(4>zv^4V z(}yEv!7!G0FQVRU%=g$dPCA|(*wLS~qT&vx-Aog9UzkHg#`~D;fr+~@hK@Hv-08tV zSp#VWllXz>+)(*c=6uIkj!9|%X$`8-}fb~s!7?mFd-kNG;2|5-* z>0@A971lS^T&&CPHfzCj$B&c%=x~4@bkUySu&rGMBxfTkFBr8 zPEr-F_I34nT-iH2;&5kCZc2_pbDMN)j)()ZzozuA!liDO#w34ErG8Z;mb>YCF)b0x_DTz~o zm>?60)OzR|WvV3zRxO+Hmq|MiQSvlFb%bzSu8BYX#7K&_*~w*KK^3)uRn750LVj-&7zBK@llob_`z-E(T(j{SZxsCRigdP=xcF4>pF7QW^MMKraoa&PhSUB0+xuL@W=Rma49 zDqIZ*m1!vHWa}I5c3m#w5HX{N?2b6tCtI5+rZsu<@bNoM3*^mP(QLvD#toIC2|4{J z1a!o_P4BW?MN!@K_~gL2wMj73yBl7WmD=L1&qfVxBT zH&Nwfh}02vJ^PrAO8g2~CVnwZp0F4-%Z+>gY=*-UCu?%D;Jxhaa3Vz_smo&NT9+hMhz{B{!r^)SBfp}No$G(Jt>qo>imXwb!SE*S8 z_wd1#*v8rtSU2Y5Y>Xt_KB4D?o_MmoUq?2pyzfmW&HPAVZPcpyGWE02_@(yjd%1bj zT$0(1uUBAg3#e6zoM6I0A2uv#J&0|g9qNhRu1v_PaY?OmAHjnYJ+Sht*svA^E=Dyj zrPeh+8&4C9vo9UF1bZFd9-w2c z#C=o0r!=>E8075Ab{Q4Pb@~bgF>>>gFAHd*lnn5VD|4vv?v%0L!J)BTc%7>Zbf<9i zsK)q>&nr>v-e_AkY3IK76Q!an3{3`*QiEQWmGWbNY{z;vqC}_6K)Gl@F-Iz>=?^^y z)4n3DpZC!c8yDSz&B;ir^{o6Q8pQ=Dz1X`~!3{fs802($boQND_R^F37@bhiiYx2F z74;;MtsHYntA`Mi@FH1voZ*fopChKFiz-prZ~7z!qouLt>Bug|el>Tt{=`eA)o*8^ zs|gCG1uTlj!GDz|LZ1qd{1Y6v4MoZTEg9i0IM|+2X1LzxP9kS0#mU*A+;<-=UY=KK zD5<}j{C;n>sg>&?pH?6GbL~`Mpv5j7-iiCZOLmEbH*yjr7iRXcqDiNd|B+|RfrFr6 zD186(Z|}b&&25xhk6$TaEq`47A4Pn~MO&@+@PGj~+<5i>>Ft1^idM-u5KtmbW-#Mf zg(ZZVrE0lYPT?_VH{@YsB;B2`0a=^M~G=my&9m zt>RFHy|F%gcMRa=)zRW6JM)d#n*=0_bMyOq%=1vHih1!~wbodq8(nko592x8A%^cC zVNXBDu1Q(CL9CNfRk|-@JA9-xwk)!NaYj{z`n9s`zQH2;27+ozmrIOXg{^0ngY>;C z_E(zLQTt*bcGDlzN_GL{Z&#Pyf@la+$+vz`ZJR4G`*uL@0$*W`sfX`b=3?(dxJ~kp zoUatouxan*?&;N1^$hw(2iZZ>!8WCnxsDQ397JZw9})?%?5n{%IQm4U*j}%{l-4hT zPaFKzj(-wYM3L)V3I?X(;K<cpm@QK|m6}PT=H^l2dVBPa_q|c7uuo877hx2nV4Y&($ zpPLxon^$JcB7Yubs}+@r4~YD4YeJR_-J@KBPCFoEY0ABCdn0jWa-@`>?QV1HclBLy zD)QjJbx}jIPJ{A39jK48m~yR*V%(;CTd~YP49FY9|6VuCI3OA(#w~FOe7!H7`ZjB< zA<>6CS>&2(g{FSix)TSp-FVnLR(>VC`?yZ-Gi9it++-7(@b^kyRWxOh$q~?;=2=(==P}o> zgqsje#h$uJvORDGX7cFuYNscEtYPPFnCSxD&-l#Y_rLUVp7C{BoQs+#96)-pY*O>_ z-0JpGswo7+c+67x#wL3zvImvOVptA4?^(2rkPLQYH42SX*o^3o$zv zihW~{T2s(6?DF4Wc~Ts78+Vfn*X0{RkVhG?@xF?*aax zOR)`+u;P=>bH4UQ;`N0)pgEAPX^~t}hlm)00`lhHx_|OvvzFfM#%AyPxS%EMMf}G8 z-dbt$+N**^KB6OSo_|?2M=>V&Ues-u((8b92=>#zT3&m8iwKM>9AcQWm$RPr@@-h( zQ+3fF)n2%VAo|)qsKD(IF*}BFQ z|C{@e3~sFJY`n*8)&7+jGEYlie5%bE9j8*d*2Yb!9-(ObrkNDny<4*E%GW_`At3F4 zTT0NB=UO>)VfW`#v`x+t_0kzE-~`+ z4mo~QhwEyhLzs&XY2@fb_(c{oEReFgh`Hl3JSJFUe53`E%mb>}4u|__WRsS*`8aXr z5ofS5V-<>``v|A94g@|}WKb4k4Iwnf2A;n!-k!bJy;(=qVdjRY_8^Btekh{<#*TOr z-=yCOgyXd@9=-B;HoG9{Li2X)1zgET9p#6IpCrtbnXFP{}M z9<{C?6@+I8(oiJmo)SCgf&bURCraSDo}4(vyPi6UL!|<{Bd^B?DZHT65q;N zC4`8(cp{Q@UY^LrFn-3sEa3-L9ASQt$5Cc0R@9nDPWFlsPVByE$ zbKnTM3gp3H>*15&KkFYrzvGdh2$8ran0;5IT!H3*q?GWdC}E6@_}Urv1WbmewnXfl z{Gys_u6#)b9qlgKZQ4SWjz}-zK$bkvmOqRwc#2&0m@K*m(otIb$=#@R3@Q5g|3tWEMh~g}S8q)b- z(5dg~(!(WRe1o3z^?2n2BMBCzWfu2@w$i5$|NSc{0GM5l0!tRPfFy$H-$9Gov0sc{!31E~=`SDek+tz?EShnWdP8y29DU4b~i12%|TTbOi7h}B$4GU`BE z#pP`<5ff)OFfZhnT2d=AYw~A6@Dba{JN^gvsOv5v_ z@0Voq04(cqWl^_DXdG|u??SYSi5NyZ5ghnmE)n(xy}e8Lc~>o+Jv=A1E)?tG ziocWa7-zx;3f5G)175!`I^bHcMQmxW zFxOMT_4@-B34S{rAQAx@Y$^6v1Q0+_N?36mrRp$?<6kh~J?iQ+>+dCHP7 zm}cm_NH}TxQxK76t-Tfw=6XRy6xp&=l}skA{ZkW+-Rb_IWWlV7%0I~6wYXn=x~EZZfl6K$9CcM<&ah$vasGR zfVPC`%H|1JRDxm#k*DX$MAfdk#sT<08W_Y12G7uBSQ#XOWa!3KEEogBJdj}@s;@>q zbm%2asU1b4)>BBi$; zhiK=`@?~IHGQB#Q#dgZ2tTEeh!g3o8&`7IX9Hdf&G!x0G3|r6HSYOZW&ZcW&ZT`>X zXR{btH%;PrXjnVJk6vk~b?QTIW)!JIcV@(z^p1?$dJY2%bF*EO#sV$D(Zz97*7}kG z@5=)+?=IK@)+?d|15^DSeDBv##kzu}GJ$gpeH|oEdt!E-maR#v%1T}T=z5DQ0Kpn< zQ}J#Y-q{+EaeC0c)&coBKG_s>4R^y*2h>_A$QJf(3xfVyL>m4DHEZY7sj$` zc@W;dLu-;-EkraPy*Y`DZ}EH$}*@8bMkTxVG;^oFXo?js=f>c*yE+nN|gls7!Z# zkTIhFosmMBE_{-(N`24-u?a894vO1yy%wRb1%r{VeV8N<{;_=%1r}h*t3NSC)(f-6 zg^~7PjO$Uu3Lg!Caywx#^iKy7yEpJs&OTYqCQ@W_*FHql_sIM3SXgTmToj^=$1fHE zv?rJAejyloRQYQriFy{Y^2g!;GJhykEY9ovVaOYTo}K=w&FD{9`L;a+4tRSgq<075 z;C+BI;lJHEe<&~_&M(xmEt)SSH>j^Sy*xt*Hu7>(lkbo~zyDdgElR*y*e1CVQMS}C z)m<^|eiBgWrJ~T7fl!C?rwgbp&^$&$5pkxO+Z4I|;_l@MI^F$jDbLe2^i(u;RIbYx z5S|3Ivhs(Y%wGI>N}qyx{kJps${uro5B=Z8>HXVgd$3c|l| zA-%JN(WimIk62|D;r1b;pSFH;ZwtM!^rsG??{;Pd`2rGVPC^3pGA^StyVngxpBfzc z)iMfI4q>1Rp?#Jm6kP?Rw0s&%cpYob>dk6TI*5*2Yfo%Xsx_#FU5L^Jf4+t*_SLAe)tZw81U5Q4wc z5{d*sC*g6fEpv+Cq$`%qYBFp=K&+f={}ru`~gdVL}+~)-gX0k&nE`|3}bXN9RDKNP`daczAWPodV?o; zR@(Qs?ooR5zcZ@S6+ZITkY<1R#LXYL=_Bp^bZ~rEYP$LWtSc#>x3@t823LfEFR9uu zLU5QbetFVEzr5vSIbx_-d%V z@4fC9to36uaXGJyv(a`mxKoyJ4pBL8&?oqp-+dj=HP5 za*#2bk&GxXO*x7;AoNLrtqsGDZ;PP+CrO5;GmtT0(wrTi5s!{SwE`@*J#GLZLm~-w z7Ws8Db)X2H_uLEU*JTbxm_$dSCk)GvncVgH5Wyjg9-yTqYX-wi5dWpTiigKcH6T3j zoe2M>JDed{&fHUUo&kmfmlZryJxmEzbc4?RQa#_k03&kT+(iTjNBJygF##Xm1RVCI zzl97DJgnYOkIFrNjW77XzV0Ld8w&*vDr3?}3==Jp4;BY7<0W)GPz9$Cy&M>3EMVVo z1sjnhi$+>$0IfaC6J4lmZ;S>PonhzCPLn42WxkRls6K^R7Kx zCHwukrSPw@suNwk18z7e{z;_=C@;vt7)0vROT#7@J_pAOY!GA*74adebz zT*lgj4%BouaO0!%;2~oF%j(V)I%SORSD=BCIR0x!l-SMIQdeH-=i~_%3I%Dmg>=;Y zX(n@r5BB9BeUGLvL}MKT`836iNfC-@k-Pzx zt(JPv>erd?`)HeCpl#B#HMoYXVz)gAp$rVDqBSx*T=LzDSJ~!E-P`BQ$53hJfV>#_xxnI@H2F@%;876CLT`^HMr&K-+{uSt}*xE z=GWaOdA(da@x$ntLVqS~{NWE*%f7o(I(?K1D(wNks=@GpsuRIrtCy`>NV?AeYpI=F~=M_~q;AMA)C}UkRf9 zY2yEsWdb^oK?XbUqGXL2;n*DYkm9YA(5GipRA6dftuSxyt^QBn{MG;XwG0(DCU%Ij zvY^QFw&DcALmv~}3KtaB)qI$|oiYeSP8_mYZ59!Pu*p-nL}Zbro#JX&b&<}H#%bo4x+Vk44E;c-S$MS;3nGC(8VOiq>606R1{H<$)%XOP zUEFOP%nE^s0vYkI_VR>Y1={L7F>eeVdZcetb9_Zue!;oD7uKVX3Bo_` z1`oWu{BMmU>kW`3>I>j>XG;G!tHil7Kb*QLvwds}&XJ4x8=_qj&-0`z?MDpdLKj(X zesS>@$NF`u7sbX0G_VITVNBL=V%HtGcX_atbSquiei1`NS~ zHON2L8VjrlcU|-2O&?j`ErL1h_yIX(W^@3jUgN9P20i74nz@97o}_bzaRzxHHJts2 zcT^ivEnh|eTp*x2Wb7>q{w9Ue$}BZJT-w^oEdQTYXGS&>m1Ta=*RSe9_rT4NdM>UX z7S_uOY2ZLt>-OB&uNC2eNq7muzdi6#AM|ZYbbM0aabwFEtB{PkeTS1MxVoQN0t=+| z0%+%JWzIZ_Z;tfBqc{U?1)ugoejjzniIa~e^`kwGU!Q1Smp}W0bT=FPES}O}h9A3H z=7Jx2Brmrn{NAKVdDq$MoBd`h>>Kygp(E=d<@o_JZ;oqCeGfKRv2y<+^-;%w_;ln# znn?q2=3Jys5O;)cR1e0NE|&}IxJu@%*v8S-->O%&QX2w+0?E9s=G#tI5#22U|Lubl zYA{_aGkNBEkHq}Dcjk3LBRR5fo9C$LpdlG~Qo)u|Uj;G+-hz(f0r>=kTOzdj{!!Dm z!&7-C@)B>5B7PoTP%T~>l^}Z@)Q?t{(vKEzLz(q#$i`y{Yc0DBHKWI6`a7^J)OXZi zNh%f5(YBeD^M?gq@vRWfi(j)`sBk-%17*Na;IFGSh!f<5CoJqwc#FO>7Kx}jmeuPC z|A$fYhgd`(OU(PFty^;(?fwnyDLvC*qLn9Sodqv2Y%1@dYYX0Pc@XaY4klyj>+7ojLH7!@XIko`<}~qXITc~#C;`+6*_xPJoPsW`7Ow_x)Bgv zs;3U1M{Z+DLp*)&?0qIcMQIZ53@!Exnho?hu*oD%vz$rM{YSk@;3OJ+M6%k+(cNkN z&|Mg^Y4?Q4Y_Xv8w_UoS@SES`7RiOJLVwje*+djc74yIHKQlq>li)#(B!p#~)AmUG z5@!vG^dJjM>x!>{3;tgOQ#r!r`hx)cGz@l)QayKuYc3DEQP0nwWi)I1!6aB~cx_Qr zRO3v}0=#Jcb+qc#uq_@)nB==wlKqllU@12vVxiM_zG)&tFA zf&n(;3hGP6Wsz@L_9^p#a*ui)$%{YkV(!<~weqGHX;koO{?*lk;%4TT(9uHf7x`=H z7B`3Q$U^)i(lX0~DF;Mbhzc0!Z#>$(J*tu*hurx0q&DW#skkV)E?FS?51ETBkxOH8 zH_8qb%Xi&QxLu3HkETbV*;|4bI2O{``l_Aa^j!DU`A|8 zQ&V#d4W=BJE!nNH>9N%@C8r&M8X;$}CIvjO+yQVaB=$dxmQ$lGMPL8sCrEb0muyq( zA{S|!XpDSr zOadpf=0B5}40VKNl3|k#{DkUUla3EkA7FcFbdnKnzpw8FOE=jEwsEEVub%xEw`Dq5 zrXw^S4o9*JHx26OW@8Pl zHQDnRWrUe-41RT~ws1`8gk7&Vk01wnKM5L9^w$awhEF~_L$cz?RK7v^%;x&~OZU^Q z1`EQ7P5$V4niFa1&LtfSYAEPo^0@6RGZiAU{8#TQax#hyX{(c=5q@e`|QoF zExx*3!Qqj08@l<6`yY?)!1MA-8o~HifLDInrD@#zwwDgYm&cRt%NO1Oxa3pS)>f9% zh{UK624?rutHSov=+uPP*ZXdGdKbUL*u<=r$I-#@q80E|^Mw)r!x%{4N#9xrKJO0B zmb(TG9InDix4J)fC-uIly*}oEmnFTXxOX9e?MqPJ#1D7FOa06DroOkvz9;dkgY4XN zA0OfK&%3WyYx81ukB>7jizijEv*oX|HlI4f!~MRiZol=~uMyg_vtxt)uR(d zW!8eD$m1@9m_gZXo!OzMdgZo(3la2E3lJzH4@en08{R*F*>MIvxDAcbrVhzHu(4sK zU@@yPwo#Kr+i_Wdm6&R-6=t@k98md1`bQ-U?Mv)my_(55t!!?d+_rVuz}3_hi%JOa zWs&D39pykTWS`{6E~18*01c!Ydzqer$F#^<0p%hr5R8!tEUjqpWX@Q4I6dDT%d_ z4bN1q$&2%`8QTgA*LGYOg6;N0SBfq?37apO_6owKs4ZKB`w`fryE|TO+6Ze>!!Nn#>0Luan zMb{j54JQ}mq@_w=jT7ZVKOezj|C96k8!x1)Xy^>*3EgfYIy?_6%<1 z72-rQnU<%;r2L@G8k65&C>}?8-@e5}%6@x2&U!5;G<@3ayP6@sHHqx>hjml-X!fWX zunEEH9Wo;Li?yIa)L*mu77-E*Pb}-oU$_OOP%rZP?w%_{SgmRalChj+CXC`=97Jh( zWN^SH1~6}{@ikz=`G>WsJhB$G_h~Mcw!kYF$ z<=|&Zn7YjGu0x?ood|AA1)dY>Dx0&PBi7U>2FE!se-2!(79HRr^MO%I_>XjDhE<{% zyM)#M$d*29QPy>PMoD2N{v8eYy5?3WqAfv9wv%s>&fj>6)HLs2yqRx`29b#1wE=cDSG7BUF9LC9Qa}wd)KXP0 z+_Y5ZgL4Is53yGQ(7DSlrE?6-plV$idr4~{XudiIgK7>gG(4dr{TsDd%tCL%S;%4? zKa#@BCg~#y}fgg;&5la39Olx8#x9876-kFoCfRfJ{)v^+dt`^9DO`GQ8F(@?f`J>qZe#6GKZ+2Ih$&IB8j$k?T+&r zS$7af-L;k2Bd7J{gb8ye0DB(90Cq{@z9(qTet&t?SBC))-ky}2^KTjIt|0|~kitmG zzN0~0q%e^Q4}}zU?Kj28e?1#|S+NNWL#>b`LzsgW0>-52dC&m`q8CkPSI#%HK$0^$ zi1&nZ&_*nXuam=Fr_*Q6WUuXJn*4UXSy)+%ylQL@12(h|OBYA`ho?uqslTolb~x_h zu6^R^f`ZG!?uc?ByF3ojhB~%|Op^b$xwh`yg_zL^YYf}Zk6D32e;c4X#1Y}}r_vm0 zK*mHUE#2A@tMNYYSdH&-S58QmGbX!sE;54!xl@>r^nocq#ANM%Q;ajSQZ*Y1?`e(E z*(e~M*7|z#Nvr1l^cMgH@mpMg`e%d%!a8jt4TU7wS1Vq12EC+-R1(9KwRJV`7SEq) zP$JwBP9Gf~iSg@*e|nf?Kgto+MqqgWB(tn|s6)&2QQ7sK!a|2(x93N7X7GJ;*-|zM zaAo^5X+04g8Vjy7~G2d;KBJZ!^TaqluKZN&ba?{{yf|)DtH{nNff= z2O8uQQX&iOBj)1Z)ZD6=!?C?NqS7BC%*Wz^i0)4OG4!_2f5#$=3$}6E)V>V+gW5sV zuIDg^ZmXZ3-B@3^aL;Q?KH|y zfr?cckf)yof3^jLXUr)>pa|uum+^rpkdK&DNJTpC3##M!h99d;qHU{_9X2s@E6B6I*L zjPj(+=8ZC^J`?QFOpG-fc_9+}Z4+)yGNtdSA%iu|e=zxhhC0n*lA=QazD!=3U_i*@ zObA;eqr!nhoQ=kqsWqYFiG4o;a~^>FA1u~@L!w`x(twT8_?IEh@Jn*zZ7w?WIl?3r z)+Unr`UEujQvA%(qX&{-lVo2E6bXKsl^+3Zms%p|t&0 z3I;ete}~$TDjHu+hPY=Y+upnOk+1DE^`RjbMly<8Tb zKPzO7=FGwn*VeP36Ab!u5ULVY$q%Y&4w~6lf3*>vaJfJUe#%0%K-tJvFpE^353od_ zIH4h9vdOGl5cwH5B8r5P&)`|2tC8yY8ELm>ksW9_`eAiCyHa1TH1M97^wFJpaYgI@ zZF@+=Z85PQh&N%VbI6cjQz9oc9QQh9G;J{0%DK+qn8!Isd=nwJNuOv#Z6=Ce)4ODW ze*_5yRRnim);4qasY$qH{1d_fgJ(1cA;fqEhI!P@=^&#H*v6Yb4;W#<&2W)H|iZfSm z&?e1?Xt#V}!+zVwnVgBa_C6I32&d6uf98iwyZ4Y$P{|H>9#C}As#!GFRNAniIOqrj zv>o(AD|~(l)2f19C(3Rf?u!G8FyiDGg7F5O_>P z9}&)(c|^Ii&#tPa>OP<$K@le-`nh%(0UMvtkeox!>`0WV_ZJI^Q}xn(f{MVrf1t0b zj+D}a0y^ZFQ#ctp2QOXgTvs}=b^53ije+bM)+zo>r}5O}taoQK1MD=LJTueOIbuv!~OGup0pPDI4Zw$=RqI6cmeNmN|2gABBj%sf0{+F zE5)Pq52ec;p4mTW=x6>&ZtMF19CL4Sg{9<4ckpEdOz2ml`<}5W*f67zDC4yQ*)cT{b zP%WMn62(G>gLZeAW^Xan$!4HP6caaTjCHB3AAf@vXU`)-be%_wl9y+l{?VM8d3QuP zxy3=Sz0Qe{oO-frz>FA&G1(#6XU+1V4PXoxI2;pWUe-msJ3ak>>eyvvIG4%ofMQ!t zu)iTTmBds3QF4{Ve^UE`8fIZ35|gR%5(UBc(=3Tgrj9u|)fyaM2flKS(O=nbyn520)9aYt+rdsou zB{#a;&_Zm#+I_PLs9Jyy%GE&AO71@m$d_+kzuMeV!1S8(f51L(2=6^e2JFv49^O7fV%zrgkkVHA>EB&X5H8iYVf zaM%JhFt|TSr#D&K`FH!&{4nj~8tgpK+qZ8~I$w}$JdnG5zdHU;_tU?b z`o;*U)9sWs>2$Wt>?iO4)}#m4xf-E99idFLzYQnA-zI|Oib+VnEflgsS6~H&sL+oG zg2*l8O&Bwo6w_dVNw0+W5ZV|=duo>Ue;aHi?sQ0Bf2<(%?$@I&2n+h(wULs<-Y2a_ zr7;c5>^juxF}0M_zJ7iQ`6176fa%m&r8&eu|9Ftt+*u_$fjA^1#))NOxCs~%G-Pq; zTlOMmPo@Hqa>>1g1lxi=+q}wbC5C)(c-PkPia5OsV~@%>>gk-ziJZ01crXmR%c|}n zKB@wkf5Q^iqQV94_u)MpUZ(XiFv-iGs4yA3bCE1b0CSGzr1X)>!9&EujXkN}1Svt^Zke>Em7J?%egUtLqq(~OsGZzL(w4Nvfp z1S0tcwg-JQovE!GbXL>I>ZNhyV@Z;f&{;QFeJ~ouffASC`XB{ttHS-I__o&Uz@T;< z&H^w{69$|ha(i9lWb;UyQ+5{7exd3&VTd4+Qrf7WB@_P5J*vMf8~t*`KUKeEWbaISs!(>q#%2wi~s7TDN&96 zwys;&i^i+ABZIpAQ%Ocu&q z$I{vm-3G5JtkSK_PX*52GEb4ZN|9BPo!Kg(upLwR0_@9^j74S-f|S>Tz(&9HX^;EuyjvEume*q=bbOSCp)`f}_ZOwujiTH^Suo4i=1>VS5?LBm+g zhU|0tJxNsl`wO4F*g`LowlDrjf6h@Qy*g_!y^krGxsQ$;k;nklzktP<2GAcLR$5eil?(C*&LSBctc5l<)Nz3WI@dg-Vi>orYNrv`lqY4lXRk>oDpf3kOtB}x|u z)4mYW=EIFqPW{T*J{LCawDsRt3(Jl1@2-cX!)-OOe@RU&!|&f(7jwl=nL<|hy+SL? z)O#G&ET!Yq(a!FR(<&6Sht$vh)(RR-p4p|vB5K;AK&!R2R$W^_T~nmJkFExN{Wc-+ zaFyDgR!&~-=^@$uvsdVafAi-YWSy@5StJ_%qrQ54L2+V|Z&0YQ^3M;A!!Wjw|?@yw=S zjO(-tlX?Q0p_&``R8H`WyAiwtD(*}~D+>)4iaod7y4oDvQ#(-B28y#O7#e6YX)qgk zWRXvVv|i>4jm^}ge?aMlHXnquF4hqpA@W(VJB6SI5O_h@Jy#(QZ-?R7c{uy>)sW24 z<(z>%50(gXl0*pgD5(5^f4Iz{m^Yt`r!BYh% z*wZ99{K-6r{uE1X9ZRP~Y@u6XEZd=uF;3MJM#0XVS))Hqe`JkFf-D4i(vP_wxQ*QE z7cCnOK=%ooc|tRY;Gq7kIu#@X%ndSCLn9<6@W71&0SMa}3c%Ea_&sMQI?C)q)pI%L zZH6=OXX9Wx?`P^BeO5=KB#eq~&_!QB%~$QV5CgMLrKBB)Td5{)!Ts?y-Eb@Mv(G@O zxYV9gCWjF$e|#Y^mvOX(0(wJ2wd$8pNDjPB;-07WjR%y91Ag zz}@cWwKY46B0lDwxj8yJjnR2ig-t@r#n5^EFxWsSe>O029Oy~YTm*X@d>^=TnQ*CY za$@-~*XATc@LZz{;<9Vn=psuO(+ccKPX@?!3D#pyQ0mz@QZgR(#dT!G znc-cK8|xE#LLn!G$Ac6|Rx(eC zC^jv^dSY;{WT^?Dg*4vQejDxX7%jpGhx23v7qM1QhQu24q{{7*CONuUBb%H`pI?u0 zf5OCeO6jiM{nxb^vs0}DQ)HY&qRikHr?x}u7fI-|OHi|v?)hJ_kaoZV@i-XzlwU0{2jTs)rQPXr0Y-P&bRNRFvd9RZZp@U0_gx+X0br4h?i{mdOE4v6B# zU*a)wB7B_SaaswqBi={R_{wpwybAF6e~W?tW9Q44G~8dOG(2Ai_0ZR^U9%cHU(q&t z`DSMa{TteKyBaBtYj^5?3KE^rxjGQ!hH$tNRN6V!(Oi<>3yU;qmx*iddW$NaY&wek zIST7Qio=smoFF3Q(xV*(fOR{e1{-6UHg3F&0>~Ey`;P4EG8l(K{8QS zcB0l5$Z*^Z$*o;#-5ZL#*hVc<+Je6Df(}p9%~7Epjj#M9Hi!O%(4R3;%+VPfog5up zBERQjxXDBN10eerI@li^q2GQve>zhDjMrOlf<*%CDv49Gku={zFAmKb;(19UlF>|Ka5F>h$RH;`m@-$z-Fi*!0pF zsOhs#X#l_#h?rt(7ct{5+y)d6;5A3^ z@@;~gpeEGV~|L_>)f6E#|o$vJQcKWDe zUe?q+cg>tLKHk>hD>T(tlRaa-+(%&MkPPmd1(dnZvAR;=&XnVxzBBgUs4N6LNO%6DbMN-We>(rA81=|gH+4-QFm^&(l|J0yy9zf@zH z!?fsCra)yXJGf`Qe{&N0ghLkr^&PTml;GPb4Lg0bTlNe7+=0!#l_k^X&~l{E2CakErB6p0l{H~$TCkaPREtvDy74g zy8u(Oy($c7D9Jc4(Gm>6VkH65NLEg$?~|~&dO;oPn3GSAl_M-Cy1Qhg+m><*%yz*g z`92%XGBa02f0O)-LSBc|b}b7eFP2x5w`AV#VsCUGnP90nseJuu!1CD~8l#ZxC zLt=8cX*0L)zh7H(!z5~-Xl*n^d~`#xHuB26VmJ@9f9gl`exY-tIMJ65&-7icwl=z@ zazaD>7-bA2!7LIrej(|7`I0=~^%A2R^@F(adN$sujy0(r!?(dR+%Yd(`|_oW=m_;b z>eH|z;cdJK^!4jPSixP^;;0#`vMgpKgzhbtA(Lo>{E0Sn^1UqOZ2-Rx>TRY!bT$z{ zxMXN-f1>Cl!Tm;zI~5U+Lo)$dwxRR)gUfdpM}w>V!_(ultMmQA;J5b|hd+L|@hKh> zPX++egcR>~Qwy%D!B=vPNS-bX?{pbbCsnoa{@D4_^RdK^89)*kFbS{zwS(>;iqxA| zsN404$9%GxAz>u}s&I1X2~T0woRJ&CUo@jpS3i|i(h7dHWa^Ue=Eu(YyL4^})73#{ zS>ez}8&(2j>=LaE*hCv@?UmQb*q3e^anbGS7v%+Btu?KFgogr@vM%a&%|xJX7e(UF ze*ks65C^yMOLF%EP}N{xyuE=sKFZ0$VkmvXskIdV@j%zL72Lr@4Ym#?@E&AhwA;~u zh91dTVN+qR+6yMp+Vpw3X|$e4*xK8ss}BroGjX0xeC(Xms$8(zW;d3?n)ciH)NaJq z!fF=%WIIW$&^k7(nz`G=!oB1i_8VB+n-KLf)~maZUS#&{8?PY`-8SZm7n#K~ zIJ)?Fd{DA}8h71X?03_8U%se~h{IWZgN8o&jO=oA?E-t$c%STS!VLG+|Ckx=e6@?P zF^4b9;3<(Tk2g4PJbm6zj6CPae_6{GYH-~V~>fBFgROgiYh z&FY~+gmKY}qcO)mNxSTkX(VS!$2YmbbkIgXmE5QrZAt&%`hMo2%L4a*R}&IMgo~bx zf`$61zyI5N_4d{4-2LC)-pg0*{oiAhFJHF5M>llZ2hYV34T#!eew-?@coWh`-*4+x zxc&Xw(dWp7tkw5(<_ce1fAXwBMYUSBA$~?|>Gj=BD?o5iZAlM1Q+hOW(xjt1BzYWq z=*>&`C!G!;8aV27Q(#JBb`uoS6mzBzovr=P7zcDjiH~p;>GY6mzYz@`>Pyv!8i+tc z;^9~j@Q_5mCPr)KN|KVm7hp&aF3CCuB%IMUhB{2QxxR$_dlB(Af9ok@BZZ|>&<^Z7 z=&=7|b8;mulQi^B>y3!hP>xXNAENsYwR5=)pMaio2WqS3`brV%nDKU+ z&8^x~i!fb)i?B$MT`q`>xxUZR??Htas4(rdGidIUbO(j4$^=q1mCVKje_b#p^BZz! z9Izn{($D&DxRh8Be|!4?)o>K1`ie%Afp+gvr9(g)_BfZzx2~TLyHWaRs#|T=4!Rip zc{RhYfO~I39O|g!#{kTl^jnWXQ1It-s^i!&Z=8`^)g5i3NoBdWeD>!MWpg>IxKZZZ zOe$&G)>^4+{^=Y#N+g`dqgT?dm?1ZP{^>5)J|pu=Pb8&HfAW1c?ZIu!ap>;{(FC8x zQ?$F&TyBO(^Sb#tTx}X}TDh%NkbExeUKjN>^5V zFk0IOyjuFEuY3z~u5;b65a%ZCD#u=PwQ*mTYeW)vrWPcR(^TQSFuOscxrE6hxSU*L zT+Z1_xNJCVe{+ckxp^85*=;a)xb})L%G6$9sMd=y$+m0i=%WP6mcSgUN{nIm%Se%n{?we<|F6oPhDd}tt|j!W~e>id^kw*jmSF|f* z<=j<9rsb-b+7hcgvF}1ub5j7%qh4vc;Q7QWZ`Oczw&BANv9NMybPJ7W7u{LDE8PuK1-EC6^~h)9 zp=f+Xg#tS>FU)gnY|$P1GiK60H+TG@`r*flhEAvY-8nHlwDT&1Btu>1TQ;B-yzz@b zeXQzA2I7Duuk%6#pU7-Z-CxN}SO1c{RCiE!;y$`Jdg=a>K9{e8toNdlnv(~lROgFh ze+IB5rL;-Fm%K5a4Um(2L+GVJbToZ`N8J(HW|1t(+_o*J(RMG9P{c=npu0QMq@;Sb z>Kp-hz@`xm2*k*>#t4Xpk}1uWhT}j997r0j!?Sy;;t12AUf|?SAd*)v0}N~y;C8z7 z7aT!Quk>5#>d$u43w#`9_SjPhCzp|0fA{Yt648nw%0N^@v>8^@kDVelRmJ~eG(?^M z3_DKMuH$cz)Xi@I{~3PVKlyMpKp*!{jt}=QkKdp5|1*RKJlo_BX1u|6YHT%#&5yv6 zRHk75gN{c?%~CJs0iwdB=bI4l96>${Ur2~$rajOgSfe0P0#8Cv5FbYiuw6Are>nP# zE0F*u6LzH(!G1FQE#%Y#RqsDoj7-{cK_nV7 zIYHO9`ZeHr?XMHv5qv3LXpktjN*{~854A?1N5Y!cuo%Y&vlbi2H}oJ zOo5yitkDFG(*YX+ABfg?Z@xGmf1Epp+vwt_{R8J%U_VR77GVHbtQC_TiO8J4vv%iv zh&}T|VfNBCTga&gRII?57KTPc#JEqmPWdnZE7x1bKh;b|U4n-QjNZ2+=690-LFU`K znk2^QUo;%+Z z0;P&|?@1uzy--7()b1wh=+KBp|MufYV=8s+k_;cB}u1nWJnGScZq6Kf2B~X>SFsJqRK$6 zZls?~+~r22g(F7&fZaIGix1if`TTI>KG$(?F%hNp{I0MCj4wfAx_@BhE|dT+0_|9y<&cu+ox%j=w{ zX89F1?18B^(?>VEe`}v;==al`%e5(y*vAt0*N}n$`(#b+u#K`B5zIdv_PZeA5eX8-lA<>_-P3+(^7C){ph zePGW1zx(>le{OF7fBpLHZoB_KMsd!hRSJNb=bpd)ef8#kUb?1GW8`NU*pW|*ogbCOUVi<*e*LSc|N82vhZzgB zoK{$FsrSIwIN9M4S~I^S6`5TcqZ9-4O33B^d>t0b}3&X#92W4$V9z*f?MqZ z@;_^Ly1))F&;IAt+r7N}f4$S*|2|5|iOSOG>55&o*v|w*&+H6UDt)yDGHCeA?;q{q zpjP5X%pW*13?WBU^(!~Od1CF2M#Myaf3_*c_k)1lA{q`^9Qsaf5@95=Bc9U_)hy%q zzv%{F(!d1`BKW86Mx0Hv-R5XwOMy0NSumczlfgNKP`AQ9M*;PyOy*En4AWt#l1zO% zfZ|E7G3ewDYIiT&O-@BwmTb$g*6yn;D(U9Q3HH}Y@td!_x(pg|HYIXGVu6Ime{n?S z@MoeaeDh1X2P_Nh|3#;p72W?8-+x}cY~Oz$r{tE9)fw;slIRyX~vu{{k{U?uWA%a27kEVf?xJ z_c)3IG9{tJ0c433nX0F;kWgKt4bM@DwC|!R4f|+!E#1>T_tBfzuV21iL&#*ETopLA zk-ZOt*#%?ZQY2PQ!7}W1KLMh1+vvOheD z2l+=X3&?+U8e7!$56V3Gf4{evm;bx3_uBj)k5Y27!-+hRYVj8=AeCnUBRo9Ug&&}_ zcYP17`fI6H99s4er^F?WsL_nk?@pA4&L3KbC4$A=Bk*hfY)6T$c0>3Os>FiiS^`?B z61>2?{;SE`r&bn}|3e!3N~K%r`~SvJswi1brQEXHCQqj2fn(i!#O_`<}<*P_Ehc3g|mYKQ%Z0C)&)1 zvNr&GZvKR(R?O{2(yS~X|Mgv=So{buPyW9x`2X(hy=?7&AEkV$34xmpA>XTZ&(Kv` z$-ZjIuFbaINBGZ}e)$-=tZx19?Y%9W|KGf7&;O57mU#a&d~j;`V7d3dK?X><>Nx+% zShk!i(5!#{Td3`d-wB(Jp!}V%i6cQJgbG}q<;!AX2}433+i7TGJFj2m{Xbs6ZvDR>r!+fHt6JZwIlnM-eqo;s zQU6EeF&q%#5eG_IvzNRsIzQVl-5Mj)vKIx11*=R>e+Q<@p!7nj)7YM0jiHn7@Auf< zaTN8kSr$AtjaUfo0$|);Fo($yFi3X$(l`?NSeQ$CLw>^(PW|w48Kw;_qZTosHQVIb zVqUBhg8hI90p^_=&BYzmfi0y9{oNgRIRLLo`XCrO zaPNs_$b8ZmMOC>GjxJPX72B2t2XkOi>H&9J;OKwFTr^-)s9hx%hB`~Z!%?m@@TTT>0TST2>#G z5|Ztr4(g8fuHE@`k%tuP=Q;P&tCHU;C~CovUX&|8#ocr4Y@} zzbeyDKIJC$?>v~>YF(i^^PKW*^kAmbr~{9MqiS4Xaih08JI?{x5e@w&U{H6e)@&pv zXcu*-__O`DYgDr+`d!tk3r30^e;p(Li<9)(`LQQL9Ek~&oy_Ne3Hd)pEDmA&{?Bi+ zXc791`B<2h)V-LyejBgN0_2BTrcIY%?GL$ac;WrCY87=a(A&3f(Y8-+w#8(+AW9{B za!E%Kf@I!0QQY^H3BUF4-*97Sz*~zWU$-qh7|ShzX0@PwqXliL&Xi9of8PZ1!%yk_ zQ_9>`x6R46vXphh6tzB#__y4h-L@JOU*DImu-vmv}X;ZQ%= zg|27Hj=tH3%RcJN?WXFDe`nr>L*ubUoHAId0Tv>A8D97)_v+FNN&y?Ma6GT@HlP1G zg61EF8y3yc#}V( z>)HS5`0{N3^!V!V=%)|wa?2hTj+tg2Vm>ob;7|ubyL581=aXSPe=fhGNc%f~Ke&8% zaWwcpCs!ZepL{qyx;i|*DDSt^U}xye>*W0X#pTu6htr>qYI>Oyudh=ltsCuG31aUK z_b>M=$(+DUusFmLA5vaL)rt)zPlv0Eqw|yFgZ<0n_h(n9?+=eENFf4zL+VMI0rg=} zMb!pw2#zyH-S8Yzf5(EH8|Q$ZX)u??GN_*K4+g)zzc{@5`S_%2_*HwG@etdd<9tzM zlQR9i5td~L{+tQ>Xp6?}S28oMlKD~}QAD>sE0giId?K^{Jhzjjc=F)n!{GAh;%fi= z{OWA~v|>^!Yz}ag{5!k#RdjZ`4}JaeesEbM+iktzUN zc5S8qSna|pIBqSustWi$;U!fVbJIh^{IkTu)4Jl>yyql7gSoqh2>d@MS4U_2Kb;(1 zU7if)>s~V=I;az}PU%Up*%sR1Z0TTkc`~>n;7ne26Ip?mafebhO{Ce~s%Xd?LrByN&>ePI&uU2N* zU>CC_*k2N}{R$E^zn1WLuqd(@Cre^`ak3by%@*9r`*)xPG}1V5>{Hnb*!UMFBn-Ma zv#O2`-k+Tv9VpGNdc_p_I!FVJFWBAT@nESQ52^6xe|MT=hotoh4aqqtBl@`^nkUC+ zM<7;?|GhbmPA`j{g{siLq*L-23!BpR^7!=V|Gqz4fM=2pR)B9b-dF!UVdDnqp1<4w zaCm&F&~AvW1O9#o&1~;i2m6=%%V2SUB@U{QDe3j#X?CSJrFxY>0)Rg~q zfcsM#f1VN{@R%&J=mx*+UmP7OUOsuhe|U9r{L{t$#c_kKQ&BgVU{3sVP6CGg6FTIW z(`p>@cMl1eQ~cSL8}m^=-T(K6#8YKg_u}pp>bdz>3i^ajscgb<$$>H%&eh5B>2dWz zuX*n)8rer*A z^Zk?Kk4J;6#Y@#{a#d5bM}Cg?6|giE23mkz=0184h~6x zXJwuR*%ez-I}b3bbW+HIippISbdh@NnGYpfF%L$>o=1eZoNyv0Ebwap<*VjFe~mB= zVosWX%2%zh6mnmoAp!D<3#Cpd-pj3}tgkAUswE!O)k36mM0~c+XLH7U;57J zB6*-u$R!4Qv*>EdQRiOEDFWFTf=hn7&Bzr9J`e{(c;tQi*LXrl+IntJa7Hbk9~`ZqWu^!)AbdtmOr zS#pRt`YaVI`v)vci9?)^2R0Q~175Q2x;lSX&BT@28?ta@5nf*tu?-y z3~@DeGL97K@8e&FxcnzC<9V?F>6WF~lFvSKSpETxNYm_MeI$8ImKZ?Mf8O(>BOl8e z7kuMt0aOd;VxWQwtOWImhq*xcfCl%nZB3xo8nhOOn8~hJLeH8_quGX`cY^H3l)k(# z@j73^2hV7^*VC`C(vxI|SpwD@Hh2XWuY1w&p*D(zHi@MO;i9k%LFS3Y1_t^K*q@U> zW6Ft-I{W4)dyhuLF}ge%e;^k&q9Sc^J0T&|paLaU!}+TS+fv6ynKJ_w!?0O1`{+v( z!Zp(6D)hLds|?Mi#HaDJk50*y@mZFf^e+PM9DTI=jyB|THwtixzL$#=oHfNSt6ny) ztZuj@39I%ak#Ey*9}`u1qAt6(RZK7Yzut{n?CCQSiCQX8tCg+m=sg5POcR`xZ#@+!f*&b zWqw{A(CCy7ENzlSe@=(gEPHb(;rP^vBXj2dGbSQ`q!p5J>y1)+KB9k>KBkq#_c$c@jt`po4Ze4345HlpDA-c@z9@nbd5^YY~hwGkcN3P5kS+rCG0!TWw{Fc+<>5q zclgW$gC>IGfntId;6JHwpe}3F#Mm^|9zPgphIQ-XfG&pP%0hu!44Xv(t*Koa0JNt5 z{bGN*fFCmKe`jew_m7on!9HxT&$3?HIG_J|aX#lG>V~;AOw-2rY-4=3F+STEpUxv; z8{>0c2W^bcHpXXK+8Cd0jL$a4XB*?Qjq$l^1L5ar_xe06ivLxEeAXR%vu&1rF{y5FGD*OS>5A`7&Dt2iOHNsB=-}$pt!*^LLK0rm zYQd1kZ5-k@4)G#!Q=K5XWlK)XLT|!Z$YOE8rb@Nzu+gZqIgf&a|2RAN<>LL>`wxSw zgZHPW$Cow1wkrY~7yE9gi7COA!NAPte9gq|e?E+-gA{`#tMfg>k3VFn=VsH~#!6lU zzbCxxR~ak0K!}C+k%!+q+JMXt7?8Q{)vqpYQ-LmrKUK2LeBk#|uHlyJD7-1Hwxm5% z0<&QdipC2p4S0QWxPLzXnxYs)I`DC{V94uCllcfL%uKZFx!V+g77)$4DP(oVXa+Z` ze>q81;(hSTQG@MOX*c>zs{UT^93$?~HjpVG@>dFe&&AB2mgV{6K zHq;nv_m0MrWB=mdm#h8D%f{jZb8m8mf2C|JLrfn!2INA$j3Dx3(|$f&%#X+l&|_W* z`r4RkZ3cuk145etq0NBMW6cc@}Ajt z8rnP!-!e}_X1(4ku6zd4vQgzTK<*J!J_F}o5#=+GmW?MraDdcE!+Sm5mGrd2{0wad zo>sEAlKnYJ_BI2LE^P*$W)i;5z|&^nX*2LVdJlyRnC zFmFz=QN2@Ff5d4hN2oJlLjIWEgG7wWAOGQ_SP=f>KLj#EHUETb8U4>s zWcK4f1W>Rd#F@D_+1za4=arDOv}$_eQvoY;%~LvOb&fPH%|2Wl&m~zxt^Pm!?SF{> zmC~pgInp(e411}E+lDHiJG}G#!Qh)ZE#;9Z!{dnY^jk2}DlaT6VBVLNR(F9}0dXbFwD^VQ-#sb!v%g-k zQ>$+jPtBR2{@Sr@e=TN5P&jATV2b%z^wIB~uDQne%R*+Lz2PnYQ#GL&`x zm_OVaE3itbQm_I0=k5=0s|8}ZHkJt9R&3npioGz_s%*v2;i{+Po}kVA`&2XkHW1HG zF8%crd1h+%#I)3`W>H#vHs`!NE%fTN?8KJf(B`AAUQ(;ge@9(%Nj)!*a%qz$DU>q^ zxEFEx3*^Qs%3SqisYPYLEU}!PNOtcM64#}J&iwY73Q5B;qMKKsy>w3Q#zbkTjl zpfdS$WgjqO>f>3)7XW=%^8R2gCg!9qZ!m<+%5g!@qhEE(ba#N}lC;gH-DcCiZ#Hdr zjL$!r_P5TFfBbhYi{$^l@sfnQ3nc)r&;Px%xA$hZnE(4#oB#VUiktuYX4hu%)+zhu zW$A~YoGsKJTa zQYG<`U@Sz#SgIBocvPSvXP*clCfJmq8_fs;u@U+;Nxj@RttL`NqQAX8rg9Pw6^*vj z{LJn@sSq&{+pm9k^JZ@irP(BVIEW@Vm(u2$12olA;7>#mPW_w73E}8G5u&;aazjEiBqPQF=IjM0aOFAMLV~ItU3~<2x?6^Z z1c4of;uaiy0?Q2SO$>bM7;FrQr)Chq_0~`&e@7)(tIoA&tPBUoqH1d;kA${{H{+?c2TF{=c{P_HDcWKSn9M-4+At8)f`E zo5|`gkb)RiU&J-8FZ&(oB{8hT5w^uxzz2f?5W%GGG-K0%a&IuKs=b zl9eFt$Z+UaiGdCBXSQ9(tZw3y?1*Rvk{S)C6BpLPt)CjGwZ z-Sx~@`hIrW>9>3g#HlXK@TMeQs*sJu=UCcDeT}x>oO6n%I0f~YQux@O9J+`>V>o}7 z$N3L@!HKej1|dA>)EUA9UsuHanF#orb-{DHxS%RC^fYKAZkbEo-J-Ii`Vqb@`lm&= z&c81S4=Vo3h9oe#P$wd`L z!N4+ru3U3hrhM5_GrSB44!1_MoxE>by!PlKQsPgXpnuq~hZ04~_L+c&-Z%XI^+e2uYS-fwuNgO5FIRhe5V3yF%R4H| z!a_}Ltso8pPJHu4{0eeDX)uU1eQ0F$SLg;u?plq#IsrmN&?iN}GScP;fDScJZRA8G zH~E5BP+waea2dip9nbcKYB)gz@8_iM$l(OgF;pRV=7{-7g|LKV5fy@9_ViXO3T|!X zK)uh@ni8*9$qddyyM1P%N-M!Dy8;iG%{h>TQhPbtpV5kQE3)ivp&`RZhO8my`4UM| zgn&o<0?_RX(02gl*+^reSX0U(Cs9}eA!MeOf-oX_r8*IDF)^x$c)reFrkz|)6w8a) zsW)Qcut*6LM5NXZi_dyiSOZkaLbtw?%gKM>BLWhrqpI+KUkJbY_$LsH4y11s+W9uZ z-uOdT;j_TCYHjVf7hY)hA!G6F=*n(p=6eUiDJC7t_&6~gJWXu7&?*~6 zS=Dbu`_YHRu0Q0PHZ=Xm-Kiaf{Q|R2U*d6L31YP2tF;bRAK~{>0pB$iJsiYKaPhJH za0bU59g*l~w=A?&f|S*984!D)Gnwj;t@H$wXiu(bY^j7In3V8D^?H~FgRdF_p)4Nq zcTo=oZ!a**UJxSZw>$hj2{W{$iALyHFO&(k4T9avmAPR2 zUVDvATS7;soH}+T7Y*ky<+CViHX$?hpZ@yK$WwTS4@f2Y8SWXE5hcgr&=4IVC|9gx zea7X%lwqQwJ<$qIX#&)xEvyX!_DWB-ww~5KWwLGJVG{gnbj*%pjm$ReT*8dY{%18E z+B!g!zg~`0+lASB^;*6yW<5NlnSMMQcH&e`B|ex~Lpm(apultEoWohICtfT;i7fioI0PjXZ&4jZLS$jk`%ACNKztG=md2b{Y)`WDJ zgObJnd|L8}l1_MUcG&$qT=(&YbBunOHp0Ie8$%U^3vg17J(YrjP2;pT?HCn zFqu6MpYDZgWJgN#RuSe%(;n|nezy-OjOle2rf6Ru^~4KU2V5@bx=aE~lR4?ED(*OTL$Bp- z06yHSi&GAD4mx8nC*>2H9$G>nXESCCjus1Kv=y06k!JEd^a+MK5cQh@z>%QjWd8a12Mrc^$`0W=(OlFU_1P|8F1_s`lwv& zxknBmTf&$~#a6@kTneqKR-Xu;&lT^MIz3rh9x|mS%smS6kwg$@5&J~Bv0Xjwio2$` zgen3%8{{wsWvMrR^S)didL0ZGC zO7W9@nDr4wo$?fr91CVNj#lQkwVAg%3YUBJgbL9tyOi)OD=@^Ki; zn1D9bS!R8l>r0#XanIoc+9@ZN^lq#XAsUofu%J^=aQJzo=ejf%k;0)2#gM832rDdC z%!AN@yjcHaoCP2zl3%b^R*BO(pT3(E{3YF}%&c!H*vWqz6%n2L&qmxq8I1!@oDusn zon{HP@aYm}&1hZ7MqscZG01mwr|i6fcCoPSo|pHZuyP(oxFULw}q?)5nc|0?qZLafbYx^_uNLpw<$|Xd{ayW2R4s+k}Q1}tbe&k^5-?oq9 z&d6~FP=R;D9@8(p#6yWMSI_TH`d*BIK(rMZ{QFLv@Y}B`gMTT5qsL3qX#Ry7JCA}S zxvO>}a)>A~jB2Na_Z*-8>Jb#T2pH%PKTA;i>+cDQT~4#+puo2V#1eI@`lb!{#e3Cd zH}8E(#RlX^9mPtJ-OBjQ)RIIwob5G921SFk_62Gi_`nIGjg`a6AP>xiHUWGx3?ILR zp%@v3fo*##kXjO(?P;;PPT=a7L$Rm*_^Wc{KOSB%o->@csZz~Ml`IVZI=5%~ zPJ;?ld~@7&_C^ptW*mxR=Uq>c@6Sf`>OTkLNCRHc)^ujEQRB`A;sykREdoY5yzhsI zX#_t!(MT6AgKO;F$>RC=_DkPhfgitn>$BojR;Cf`z4^@DPdNqxKW09byO{BkJcKEB zs-o*J%E*GO#u$`4tKvachuo&Q{U3C;4ACkn%JlAM>yMAK(mc14j#QaSQb-numpkdta^!pi%XFKM~WN}@)Kk1|SEbu(O z)_qLzw|OredrSy9IprGc4R)S;PlfCdx7=@Y`)plxMnf|TH0`DTvag&Z`p zq{Jdx)|^yUjSvAb0C=}DpjAT%>9 zSf{GtH)IsSZOd5eKpEM7=Z+D`1Ro+d|GTsheTxR&R88kB#X)*MH-tnq zizY#0)MVchf>++0r0QB6@pKIZ`2BlWm6s~BNR9p8I-CrrZ!eRk1sqraiGIhD#WT2c zNZU34{hh~+?VY}e+Uc>BMl@iuqr9Zn&7?fmVsDZX8Cg?+QRXSg9A_S<<|PnQEPCDK7H0#FI=zX*G@klFLN!_g9GSQwPoc)Fq{Hfe zRyI;B)kuOt{{5oOkjcimKhhD+S;=;Iu$F>)pcY`td|}K7PYVIh!nDPIMjQE2Jjrfy zrwi_}yg3-MD=I=$wN+`LSRr|gr-5%-s$W2M#RNht(>X>@TA)bla6IL{ufEK2EsG%= z4OO9_p&le>s9sZ_;6Lt9_+S2qb1KC%vBp}tK8v&(lFnp_K#}O zS4@^l|0X ztRP6urARZQjtcqfR2@(}KZrhWzbTA~H%vi9$+H*ZEDfvApYzAI_$4)%fbnG*N0NiD z6P4hj#$sJP>B@tPYTSFsoAy?JYUXzSf3-0bPH3?InV65RZ7qObf{}WQ9Iw~)M*#3) zY`oJMZ!-+Og|_!bFsDA<85Cu2YudXSLs&it zUXPE5V+#Z1mS|a-^xnK3%tKCHMeCFgZj|AlomLz6axJyA`h+eIXZ}}+-X#(yb_)Z8 zNWCF6+iwOq(H68>jdrwJy@kvHYGZ#4aQgjv$n_~^opWkvxTHkS-&VWZ&QY7?gLni- z5?Jh|utBvZq+5ZHmT3ttD&KSJ5;;fHNwA;ImL==u80{FHpx_dw5`znnjqiiU5ZpoB6|*!2dyp$AXQ3-~YU(#w#0S(eeR1o3EIp<)i`*TwT8Z%a`Aw zH~sf5$9Q-$4kqi$I#E6s1yrxyc`&nTapHzBT)pDD0#Uj$B6lgoa%=CLYMyVWgx8iSWB#2GmU;cZ1G08 znZXC4#1F=ySA4m(n2PcX{%;bhHzc2(9^E0IzK>@=N5+|aUN6ub5*{uso%|3Uis)jd z*R84Zy5F39oVD5;S>fUV*xhZ?s%cFL-E3E`lk1D$D8)LGbpwqtsq`{*WkuYP9a7QC z%_#QTWBtY$Bv@pc>})$s_ktQa@Xv64j%Ct5E;tjw>o4)1A@M$OiU<*?HH`8U`JIoX z;3ITthMxoZb-P($jR~Re++=HVR5aSu6K#L=s1IH0@xOC%w7yC*&W(TE^&>(2DMz~M zqQkjzZ9ggpaI?s81)NNP$Xblmm`KIfMRW%%c%ge}|$+!#;c(D4)&wm+PQYe(U^b^4sJ*0g-5*iTn3@7^ZQoP8w#g zA_ff{1&f2gpeQp`wMm}6cen{f<+}Ql7v}ab#MYiH;C3yz@~0yH&n|30;=a|#uStrv zW~Ya%mqN;IRZ>q1C8RoN$|Em+zm%lDv&Zb7#=E#d*^45tfJY`;(iFDh!sov1b9nuB znnQI6S9F{7yUT46i1`@0sN+&wbS4@TxYOBta?(GRo9@GKKtbmeaop%`Hu+*)g#6!Z zPWNmAP{UQ4jVV+tMz_X)CSIIOv(-RyXpL=#9(^q_jYBZBd~I!LZ-qUP$9%f-z80_A`t1gcC)y!c{(FrmGHBn84)UZiAJ%`w@jf{&P1>7ct@ zmHm_wf7rD7KD+|1Qf?6c&<5^!?;;PGrTHV*`srw}bQ6wNI$jIDS37M)osX|rWS_eE z82O&=5d&X0cz*7J4ilgoIkAgTUn>pJr{r=GuPqgV?Q{;kQ~2Fp`}y1DjyNtJn;rl+ z1QPzU^jBV-8@G19=fGQX2)aS8gs(6qkxIT4U9$UdxjCB&LVbnEy!j^QM_pZVngz)^Bs8d&1|nBY6hH$9l|^Dt)UHHH4W!aW5h%a$P3NBo+& zGV2}a`bK&$V2J}mD5opDdE6VC=t1f+;0X@HM*3XqpcnL5=@GXBr_qYVM+ztjre7Zcr0ctOd8_)Q1Am zc2+tEys1A=9$lv*k#|wUSAdVuSX_teJ!>3W^zORUJo`XXYfLzcU1<*~$G&pB`C_`Q z+#<)5TD*Q5)%~+CCQoQ9(XGO$O^z4xd;C=5&QAO&)S13v%7~4f~7fx=Sxz`Cg8tHc2F~KhQsQtvtp%1+-Kp1ONFQ#=cf7 z#65S{{u;bi@pxu`X%vvM_a1N1eY3_e+gIx8gxf(6Oi-4l?31Ny^k1k+)z!FnERX!T(vo%q7=H z_h$3NKMaw1t%Sw^5DuMSItaI(_H{e1Gmpcau^J>vj#nEh8WN*Lpx@X?{f!Ga6CQ%e z?vhNPcRWy4ahay3(V7mC;+n>hRJmAlu-y2S z%iNy#@G&`Tr+~h4VF;$0iBK@}`jdsIE4x>6db0&y=qm6_M{wmjc}&j5Q>L$!Gk7@K zlgM{B%W3ol;!67vx5-XMpvGSY>nI-0&~^a^Mbs490t;5uR`0!NEHYT-^%+z%OcVEL z?~McOp&Uh=7M^r1Iw~FArDW(o*p_l$DR*}%bsr60j`xbm3XQGb-{tGC_oI~T#@fRj z_+IN|{Al334P1iVDPhK5&>$d?7|`3hTy^PbNThB2NYG?`Ew59sy-Jy;YN}{#O=3Y( z+r*a68iPWYlzc)8Vh63<5mU_{GP{PcT?FE#d5Q+HyBI8fak& z_5^}Pr!FjRDz-kxl%$5liONKf7twhXYT#vbX7bbKx&9Z@k2LV-PdkJC74MB3ZKBDU zEd6<%9)IICB>OBj*(WO_AwsLekJ=X40v{vTbfiRE4HdQ*$vf-EJv=eC6I}0j41iEK zh+CuxytA593tbhfe_PwZ&%qdwvTn!|plsf2OaA$tFjQil{q5)jY+e~Ryd{^*(K3?4@p+TXs8u@mBb;P59_M%a(}#<3p8kdQ4R#o zsf=5clgO3gb0Sfk)L(IuC@9if${pX0lrWacHusc|LO$Pto%Y*O&e=@Gq*CP`I$fY) z2>Ss_c&LUN5mDl&snMK(3$&NuP_qFZQHU<`{OuDs7#MCXgdg7%3ooX}fNbhvHMYF6 zb4fy#K;+mwbXJKWPNb+uM5{ z8ti<*pN5+w+uwERi%6@>t4lkd{(^kOnNv}%i^h_28%j1uKVfDIgVg5o~1rxLXiVj;lp|UMWP}y zWg9+#)ERaXksHfEA|`wF{?s3h(^6GuEuWI^jA{G*LRvF!u$Ue5j!TGED`dgaD}vJN zcJO{(8~?t`iA%0IwWVBw-#!t&r(a3c7%1gu7OLcRx@+)a)+VDq;b?bP1BQ3GjF9Wc z63{8B3wuIimLE8nvr=HhGSw(})@D3&1(I32Nd0_uo_1=jhE{?1 z&p(ze$C|U94K23wCxna)2_U&$4!t{FTLYk=0b`<4La0%*wt4@Kmfo`$WKn5gJ8`1? z|Hm~ibiPTLQ)i?`5By`Wu#_nD*6(ir^MWLiK^0vK#ML@hSM6FDCMfsG=3XMoD7#A} z8Jn-zXvCt&BNPNtigye{TG9X5m${eO{sA{o@4z;&pO4`6P0+ws4Yb#gvH2 z9_g79BsvW%EUM2J#z3W0}DDN|nYz-(NBVECIx)z5lZeLzc0h?0UA8AoGXxDYX-O+wo_BVo2z zE#QxIaceE)~*`?PmhWhzX8Mu zYUC`hg6KkS#s!Gju+L%W=zZPG`nD&iZB@6I4Q9cXrGqYz_Z{W+SXXH|*Zp?H97m$5 zQ$X4C2)dZxsE6clXEDCAFWzz){Kbaz4IU~t&)}$o36?dR>j*p)!)>@>QFFhY?Wu4u zIxRUi{u!27je0DO()%5w94kSA6{+-sxgkx~4ZN;{*8hv++mb zmTMD|b}4AF^YVT%JZe8oX8sW!>v796~wV z5+3u0FJ@}P!X88N&(8B2^4wEZ;E3&ZbpTCS3u0zT;IY5oUW^vv@sDPYw2bgO| zx=W+6-S(z4FWyZ$u{yBUxHl@L<6{_E=BT zfop`{vBbf5lWr0Wp$V4;BytlgQ}gA+{FCdtQOh%iNa+C`O~&}7N_UTE{M~+RHjU03 zA5`I&7T#ob^z5~@0)2L~0H^Mi7@&?y>>P&id`@(iYo6lLZ1C8ds6rDZ6T72&>=MXa2%fp z2|e3DdqhPhF=7TyWxK7-#TYBYn{!=rwOEcPeVva!GyIEczAMhd$huyo_j4iP4F0 z^6cC}%14v70cLe40zMxyWfB$xo!GlT8{pohxww}}$KZjRnftLaq6JA87X--EoZ9=A z@sSZr-=A3X?L)jUG`lnWY{ZCaObOp2M&(QhwUU$-!~5Os`h`_>WbOKGuFK(_f5qBA z(9$zR*lQ~aagn1e)+4`vC|x}=(|;#=<7kJWt%H|Wm8ga5+k3dqFjF{LRdm!pZ>gf< z(f|14%7{#?D?04z9hnUSzovRNelR zjUt5~M8h6BK~wVfy4G_`TZ|{1F*yg|pEoKj;+(RS&;cU_+%UPHm+X#8wrFP7OM+Uw z3!bzkuI2GVyTyx5&B08&Uq}PSH3Un)sgt>WupbKVFy_D$+zR=^bN1&Ne#Z6^7r1Lw zTJ+w)zrKytA$zrY4i{A`bvYNGVa1b;Vx=V_n#x(x+f^5DJ|`STDxhqx9Xw|Pr#p`N z!H;CnahfA4o-zgK1@-Yfk?lfH0nX1nZlyDa9vyO_7UG?Nxc zR^!GqqD_T%Mkz4F+1C~R5Hn;_Wf+hIrZm z-ngGvb-!wzUgRM?Yhy11KIdixw#pgZ~syRzP0Gy?X3ea$TXlsuRaV|BBB zbkM4J5LYc_Y6?Z%rrZs20z>dBDMM9~prXk=?MsPO=!tvi#wkbr5Gg0%w4NTuh}I3U zh`MCO?Fxa_A?|S|+mbm%z9qKJbNlOjS{>x2G*Kqa*Ae^b;+89*e3mm zz!4FN{Pfo+*!oi*Kl80nyGBR&0HlH#W=cC%WWz9dXrD9vGMqfYVfN}&?uER-k2H}y z?BFM-0lFg4p9iYDF^rxWuX6OfLhmK6K{}en(9V3=5IK6ubL#SP?24FqE#rE=jQK-K zg`LiI6bv^s@dw~#NsP%T>~j|6KdUS$wyj^%l=RqK;-ln-wL9nn^7-$oJG5D`ths5g zq4V!Xj(b3U4)-M|^)7eDb zr4cDXxYbDG_z;E!Qw3*+aoq}k3L-{#RU?=_(#mv_bJYaeb25x%hB+1HZ3yLhRR?MU zRIuvn*|xGm2h8DzOocK%yek5&{ndDDj7h34;$sPiHAOV@YC6Sm0Np%&tj72(Hs+8x z6v|$9Sbs@NgkxJtym?m(3oAH0+vnw(YOJ;)2}d?jH`HYnGHi=YJOP!&9mpJPU^C-@ySBYR^py+oF@`Vb)XJsF&EnyB%}me&5*Y z)|kFcyBNq>mMUFQIUf_fjJ>zM8|uq<%J!uvZvFt`p&N&b(b4fb(E?h;_z{)#}gq7b!yN(SI!HCTPY-8t!^q z<}m+A6q+HN+3NaaW>E&;f1V;@Tj3sXELW}h2k{GSfZ4xuGb_g+`k3UuCD^G*Ug76^ z?G-1$gtYkr!h#_i#s?^dDoo3b;rVJhm+is)KBi$B~#&C-` zzAiSRHCgpvoK5tI*g7))@CPVTkWD1{K9yg31u|m>@n63cn=b$5mjWIc2#b*)Ydaz= zDT$|EAB>VM&4W591JMkXf>Y}(#Z@w0v;i`JzRal(YFDf%mJI5*gcQ3fLs6Vg^K+P{ z2IaUK!&Ta##7IJ*OuBLHG{s4CUF|xnEMree=sec1?#>0jSa!Gzn9IyHm$FF&{sPt) zE)#88dXR&RcA9;vd0^AwX$oEYs#O*kf{i?iRKEXDIyPtanYz5V$aQ3>4v``|4mKK4 zbGVvcN8#SbOcC0t@(s$=XzD)qX#v&K@~mTMFCN}Rm$~DnFMcRUKjg`ldL)gys<10d z@A$xuRBW^Z25b7$=I)H;<0y{R5RGAAbaR)vw+Rgj)GuGh}NWx zD}aYgSKQw*WF_kZb?G2d%y;k?!ZHrvzNsyp5@M0wN#UiA64R=nPK~~b`Y|U#O43Ba zNt!(rex*`sPE9HsFPD^|*$TVm6ev|5@3Y*DCcgzaHN*YPsFv-uCU!HF`|%>M1`|~p zWg)BulE1Is4c2uOzCBVNLmq_0y=@BXH@4kQ#P(&3@K=BPU@A)%IzJv$$cx)sjka6s zmXpvmrCwbly|QEYFaE@D!iGcw6!|7HW(1oB)-4hIr=PBAlpaL-qnjRbol?+96p>h2 z^AIZ#^-5 z_Hcjs^l!-=W3wXmy~chevwm(BA|TdyLnNe>;IdfM=OaQ6r-t+7?m-;7eBzij8joi zW3$wJb`gDvj$IeUxJ)FIb4-uIgx!->k`!byVV4;q<#FZa&h>NqV)7}2{+rqCcxk23 zi?}O?41sj zZ>+uzb#gT|ZvsQ}wyA${Du$;*ptslcH=J#=AH4tY_B9cvD@H1)wd5zB$W(13&I-Uf zbr$tbrvxyw*}5Jwk_9m?;nDE9fAS&4B9ilr664_Esopr4O-wyApxqsvmLwQ6B>J24DMsXUw~|51?6TKKptrEB9-`$(B94s$J@SoXX+V64 z90_!_A?jv>gW%&M|RFXt_Zx~3>=7d~69&BA&t z@S%tzNM<_S`fvDO`Hii=mF0wB2sh6J?yu5ti>&x3*J0Gr0kgtE=R0tH-&lgV*a;pb zKFM50#99T5Le7sSU<~-QgS@DH!&6wZ0j5>9Wvo$h3g2B*Fwm3eINU&W6WmP>r-QpI94fx1k%FhzpZnRmigy@U z>lS}c#B5~?+fE9GYokhgOOUHj<@x-^x+P73lZoavH-Xj12w%yL>SbH_1j}=9h zjI^>2N8hqbL-J~5LK&f#NmF>(w?rg9j#IPGw0eBkRRhlmFE?WZc*M*2mN*l7jgZ)h z`F_Ey19)g4v_BJ%8wJ$JVqwRSljUgKSw6Q%F1tU{F+ahw7A5_k`=S*$>LWIx`7ieuQlSjlK_k@OYp0O+&InKcRoM(qktXs^16*phY*g|v<_^x!f z=8zTZ6WsBlQK5M=-Y}z0tP>cXEq4zYbM<8^82AwrZZEe5BUtn*ggh(rbjT{j!>yFw zTDa1&w*J&sg2{xGJcT0Z?vrrVeoPQW0cf3Be-FSvCQ$Y$$!nz9A0LBqs4DzpkZno$ zDOSwQ0;F<2Uh{=&$7~ctH-MqHMV1=&nVmwnbz8SC3WG7sgCt$zn47?|jWR|{Y(_Qs z2#}HYaxyFibCXA;EEpaB;2!Br{EPzYS0!$SX=_xIj8^~`4F_yf`uw}_d9Gi?`RD|*=YiRQ=X-)Z_ zE#`+Bt+Ns54$rCLX3okkc{s)8Zh_jVHoyzl0!<5=Al!gutXfDtsA))%Hx$_LU7aPbjvhE|up9`L;QU+6GA_yhO8@5>G{K3K!+_Z?Ym%eWorXS^CL zp@g+LErwpTIYOWobL3q7q=f}+8cuc(l>(3~88?#3B%ITwbP@IsbUL^d_{zgTAnb{= z#Kfw}`ZiRic}bTmP~NGqWcUg8IT}JBjDvns8_g%qUeV}EmG6_{CwXXbi66g5N9~Li zRIF~OopwDlbHz-YsXeOIakp|*l{~~TJ{WIPpEj*kxNlF)9sAU3rC~VNXa836=yH36 zO_I)7%BsecPeOS71LqdJ*SvY}1550`3z*eOD(5;~z)o+eVJ4^6 zz!!gekH4gW){b?Cnc0S!lb!zd{*SYNPakpJ;jpia<2#d zZ3Y<(Nrl=IlXAaATv9Fm(4kM#2y9*fVOfBq*4>7Od}+w!IFlEH2dtiM>laO(!`Dfn zo<$w8A-}}hbjb;s4TtiE5CF)i3UI|JC1E338&$>|?LH7O1P;kYCey9%8`TDEN&Un3nT8=ZyFs!-C|+;sglKimr>EB!;TYTLvDoDXMd&pBX-5ZJ1bLnE>|VwxXFu9 zAIae7V$-ww2{Zb~{S)Ls^=cGG_GLid*f9T14m)Us{=Z-htQPZ(bdx zTvwwu56b&JTcGvDGNypuP2sV(2GX1xzYHL#IYg?5!2(KmLBu6OSCF-}g}RLzJOqgigLoa%Ek2X>upGtC?a>R{X%|VBjTR zU^K9^Kal^4W|TwnecywuaH$`XhEaf1MAQO#d2Iv;r_O##aDMH`P(G3#2Im!L$xo`e zv!mdv1G|e>SR;YBhbDh&?rb@Tth@6M0^ts#D=As}q_j9K_uuc|UwRW0@qJC zs|THD$ck$cx$oFTqXH5@DVtyKlaGs#S#v(Xrn9cky!cK;eN{?(rU51ep9QOwU{bun zR`1L^weh^!cn3(B#IXN3Op?Wbfi}1a5M+}TkpD|*s|zBgxsq+z`H$4h0J>#ky`P(s z)aP+IcPIPCT~Hth%y0$HUwkTVA<7h=gTvxP!BfU-Rbqc^3=-pMh_kF6uTj=W!f30wBCJ97;|=J_`CnlCx2vR zT@(1JsH}re&{Qu|XZFP&**cbAZ<_x|SXg#%^2A_1DJ>r5Id?>TJ&R=Eyrp^C0}wb@ zf{xDg{_zu+|7?B{ZBWbgodb!@fu;vPO1^Ko!TbVhOx-M@LvMW;6-s)bb2~bW-|$>X z*H6h3TUs7wV9QG{BUaw;`k(E82ES#*$ofa|d}#`?&(ClmWS^B9gC~((P5IFR0sGVu z8ivw)J~)T>m&^X$#b?p#_?*m+5RhX0wX%RRr}4`Jg2`8J_R}JIm$166(S$*L+90zS zJTam96uAMHW4?JLk88dwZN9Opnpqs0Yr42taG_baFO_d14rO$x-2m2r&84lt2xD%| zTlyS){B1&no@r4U`+-HquWCzaZ-?xx1oq6w|{l;nB^w2%E9GV|jK%yXMTtU>ROi-)* zI_Y)M@AcjvjuP$Ho&dU46YczjNL_ZsWa1wQn{j$ZL?ylhYIYGuoK>d8nt6~r3@OL9 zHKWHb<-zF4g2Rb{hvV?Qde~Grw7lf>&9*WzI8No;Sj(CpX4){_LZ5L~X0#Utg#eBIP11?*UrCogI(&G# zC}vb9;mP@i{Qbm1p}kps%bN}xk(D^GnmZribq0U%{Jf`cqY8-&5ly^k4tkF}N zqN7NaU9xy~WE?6g`j6|kb%l~esT2jR56=|ntW&2mbb?4D_($qudfU*bNqojngl{;f z4(`_nhn6Z#t_Fi;VL_Fgs->C!sH zd&eIQWP86ICI&`RT+RU1AbPW3LDHEE(L!Vx=1ProC2rCNcX&_wbv&vBUN=d#WX=xk zf(!xv)&)~C`3K9GyWZsR+A=K_`QDxykW~1vf1;9aH5&e{TB5-|{@~A&+!dK~#{Wt` zBGc*kx*r!l@Tn|gIdwlJB`EGr{`Gq91D9bU6e#nnF>hZ#%^5M(f(?oTC`34}YbIWe znlIS|H!mHoCBQsxfY;TYRoSA#0@?SUoElg+pmtXVasSYb6U{F(Yy8w!urSrX(~YpK zE$)2@7+U#M606hS8QgXjv)R^SLC?u7D8O-Ej!vk+6dWS@8Y+i1Ay&^LtGo!wea1es z+B%7--gWUDTa4Un;HIk#@Sh33AKf3`PM|uX^YJ`{)#s#W$@1&t$-xD@mQDTz(jU(f z^DmkcJqU1%QHH&t7!+Y>rF=Z`_`*LuO_p2nZ3MV`10JzpJ>N4)1u0nq$&L#4Uq4ME zq&{a#V@8G;nvw**lcr#8Ln~Abe3fAvuBR%}8kzcB5lCTHWm_i*uvuCY@fplv?_SJ4 zSdgzMBsAyw2g7D~R9Q=S;yNsg)AGkv_B-NnH2YfwCA1XLu|A`BgjzBVX~&ut#o~9E zWU{;fkMAoP*j=V1rS+1S}Xa++nrKO~WgO^TSb% z{e?+AIk}zuT|6M*dB0XTmzdNbhv?hmUE5wQZfaPnup!y)Vj)11V~7Lr3-F6yC{1bt zlPHgsf^XA?_F02H=gnYUgOiX&pTL#p?AHr^<=lrU!&71JnHAqFB+#Nwv+m)+SYgk~ zA1=V%D^7IKaMk`jWQCS@tWKaW-KH%omx$z@kld^^zZ+=#B_PnFk&6llSpU7`4_5L0 z76dp@VFhQ!4^QR7cYIBqR5kq@04Lu}_*0?vQE*x!fZ-#haPTAIQsS}f4DuIxzIwTQ z`26bR^>n=Z%k+W-gE5HQMoX!(oOkx;Y#+QiK1r5+Kt`$p>DfT27T<{iqUgEnGUKfqCkFFbDkpTa~GrnQ7+wUfxpx8)|%3@2! zW|m_tdP1}N#XhOPi0@%)T$L>C(EWDb(tmJq@p}CFYF0bvv(*aLeP+q&_PR2ge0y7W zeD?M5Busu4`|)Zpb4uRCmAs*HnNI0AndtY|aUNheYrbyw$FnPc?J}0vLXK(-YqmMD z^k^yPz>E(68a`SQr5SP-9HLkE&i(@ay%!KOk(nrVGyXidKRN_e+;6V6P$Pe;PpHI% z8D?PRd>EZ1^lJ5ro(wMhDY%NPsmqeYXq4dJ(7Snu_2NfQ1udLiuZGu5V+}=vwN`cG zZx2*KikfAd=Am~}$ZRyT_Aj$b^C>-{^L$EYn8oYk#lOsbwugQ*#0ZX?Fgg`^v=yC*<6kJjwd)ta(QhuhMxgG5y-j)1hJ0^?|m!L4?;*?n9=(eEe+Um8#IR9>) zXWe4J#6avkp{z@!5tsygj)5vbmWf;v=_KieN1%=LSM($(T>eI;>){~0;X9M_)l?6F zHMNv?rqCXRk{nm}c$7GeUDdC9Dq$PpMpimBbsFFM9Ef?EriTTGiu0{H2!-8)7lb$` zFC7tsEG5B(Eqs&vG*#-Bm>nqXjnxY+sk1f02z5C+-Wf3EBAc0~?C_V$$~`658$9e` z3)_6>8w}1V5Li9QdH*Y2%Fg{(zAO$<-9?5$pWjf%uLaoN&UW9-AQF&R1zj48q)iLI zX4 zOi{;5;b1mUd>qSfpPp9k(k%Vz5Mg%L(!NKnfuk;RPxlG7Ab++tWp4e$*>}VniloUnvEKV9FPv6odCff zSv;t6<~Vi|RfZ5eQP`^059@_oq?+e)*S5sg+G16&N1gm*3%hUU>ha{O#?B~ z3oPbOi`O4==-3m!*CPSIecdQ^!e8)0c-;|HLn*%I1)S4Zd@vmrm9n*w6j8C!b-6av zqddqJW|Q@N7Pld5@IpYp+X}lUNt`Uuy&_S|+fnppAy)kUbU2dKr?L!6qKh=oIPsM1 zGf%8gA6iL>MFP|jG;)moATapl`f|b?*1Y%7s9&KC!t9l=@pme)H{xlWJ@{M7Wtk_+Pz|OaGHg@w2Ebj}gyVQ5l83F$rWo}SqN=~d zmpnbBX?P(*t=Yh>?l)tvida_$@o;Iou{)s%Icn(6T{;sX?rq>8Vq7bgT0~ZW->IE|1quji*&!)ef#P-=VM!T13PBCTqM@_FH@RO!B>AlGUExUtu}TX^Y@##mnRp# z{3v&(0ku(-tsiA?QgdxmxYHj8))IMYE^aopi!I=HD?6dG_`uV~Tuu^eIGPc{y)I_E zr{p~Q%FK?Iun=upWyQeXt2$;Gt1DTk??v$fFX0trGlj?Uj>KKO;66fOz2^ z>gp^9NjHBZv+Ov@4t%W$t5Zl-@9r2Q7MAFmSh5R>Gt4?E6Gs_#(<$!`tLXfbk%7HRzy{j;ziaGqB|ybW{$diDO?eGv*1*! z1;>mLY~==DD~(tL6tHNo5^}T#Lo&kodq?0{Sso47kH1g3kS^;4rs>G!fJ%Q3Dl z!3KW{IZJX#3ON2BZauRT7q6KL!qLwcC+E{O;AIQqEFyUY8!W=0|MAPy%QuIwPvI$L zM!}B@-3++GGF)(TI;IgB5C~sa4WmNeEO2UCv1=njIc*iC{bgO2EJ_9HW6+AdAyXv6kug!!L&xkBL>Rj`T`15PC zS*q?`6FL~m4r+Ss=6XLq__E0UHx?Do31hd^7<2dk(^h%^x1K*)@BjNK8|W3$(xQJ& zexdCi?gK+C(b1_Aj&<&=Q0QxXp{*g+!S-qFCZzJ$oZx|$MfSfKj`S;d&xB$MmtzI< z_J3>tNooH-+1p>A|Mya|a1iR;H?bf(NW0K~ZP?c3?#tZ$uM=FHpBx^)KJiE4O~#me z{_pLV_J3>d>3aX)OF3f61VK7H{d#|_bODazaF_9ld|o!EqSSdzObGK4PS!{LH&qtg z|0a*q&Dg-a{r~()S^jT7*>A7+|9zANlZV|lV^~mRGu3&+{Kh5ZB`T%@qw()L{(Oni zXlEuIhFSVAGD9Evdda_D+aL#75Tja;Dx~+<%>s>X6eg_0yU%CF3-15=1Xq6x@Pv8$ z|4D1FwEv$z*?YF$|MyY;UA;k#msl}V72|oLD$N6IZZph@O{Ny2Fwc0YZ$^5IWg%W{ z#^WV+#SOOubv*w(40#ajThT2iN}T_-SK#ZG`TD>9M%#&Z^Uxzpj4@~bw_8u2m+gN~ zT5J2?y%b!u{0CT)0ppVnI*ot(9A`0X8pSX{4V)y|pKbq%zt_;WohG>DA$mo79P^1A zd8p60ex#!F0YKO`?Kx*-+nUXcZ2WbE93Ma9gQ=eq+2}+%GLzpJvJvUb@?xGF4X;51 z1-b3QjgS(u>sW~ZVmD#~CNiEg!u-ll;z8qzOu%ES)9}p18lLfJYIuK{PgBD~{WJzQ zmWF5N+8AMKy`)uhGQu?K)VWS0lY7YcH8%8LW=Qjrq~k!b^p7+mjCXhY;2$sAKchLrxNAiqrp+`=ydn! z7)i!V#GQn*fC!;&s*&H<+{kpFVH3 zr}V$4>-~Q(<r%(2YCu(Z4@f})$v@5>qp@}v_8c5WMC@KKhz^BY*h8LM`S1fLcCE2kGt&x!UC`2&|uK?6R2dbBX^ zoo?+^9%UV)&Ox5lr-}Uhhnq9V92aVf*=+z3$n4(B_XcddSL!#>0hDi+F$4{T?^Fah z0S{Z~Gm5Fsbk}YbH;*B+m`Sv$U6=A{ZvP$BC+%rTPmkP-c@1I1R0G9KVqW< zbAp2&W=yX5pg5?zr6}61SU0td{DVa9PcjA<7N$xEQ92 zK_TxCRc7j614$6bffGUmYrs z&W{&GV8FvA@OOLC!wJpFeks(|v|Jo(0!g02rh+KMf`le@Ode2t-{1evZyZ93APAaA zf~%U^*I2t`IC-wpSkgRfJVGT<3T4`3`5WsafF(mzI}RHa7T&axx$vUqjqZC)%RYI{Wo7pf6ugj|4asME49)XH2ZsE26zv zY+V|ozwi6|Cg`Tn$(5QAhp?~IYK4(LOXIMqqYwFw?d{7#m|O^s57B_I#1R`j7LYQ3 zl*U!7vAw;Z_@N`r3J2B#EjyTLe%c(rZ+YMUwzd8K|N8QO+xC|)kD6$+fFAT2Np>N@ zlg}6~R6jsIv#9NB>oe=JT)DaauOnm5yZimieEomF{iJ;V`{db^_4}WDDU0dH}_fYIQzbAXz^#x_#V1v)uDe|z465Nog>?Mt6R9yB)v@l+Zgn^vw_Kj?vxR|{V@ ze~nRpxxC3z5|A;85!OMth?~cDM+XUY626V_L=iwtNN7_-l8OEHiX;&JDNDK~Z36co z!gM570`?Cnhvz?T_R(^1IVYy_?Gj1vZse2qH2Hau8QRO%#niXK9&a$kzm*!9tEob@ zX@dL=>1*!xeqvqjw%lC)gO@z3kpK3cw#)Z_KTn=MU(0{@QkIba9K(waHLM?VA#*c4 zC4LwJfq$B46KQD!{yb+)HZzm0m4D=r=-Q~MZ*QzfQFj^_N66$fP6kv+K2b80IsyG` zOyB#z_G0Y&k zB#k0nRg_3C6K`v&uf0?M+ul}Ao5h>PMF_P3l_M%T-z(Ar@Dygs6yws7@iKG?{+SkE&t*edG%@(Xj87-X>q=kWD z6wkb{a=kPQo;Ky%k#AhgxyAW!F52b`0CUg(=TrXwt@b+p!@ZPc{69ETY!Srba!^zF7*<7BOOPh;t$Gn-8gZ8DDHbkrj}i+ygILE?OEMr+rjBEUIM>c`X>t&#pX~)l7IUAB zag>^TbhRg0d2uqi$hm;)9L%|YA*=EP$bBmv*@bbmN&YBwUUg@@HC?Ozf>#gVti0y- zE-B={tA#B54l7%hgUoN8?164YhX_&(c3eSFZM9Q@ntCalQ)5`a&Po6Zo=i?>G?q0z z0Ts^fYx-cD7S+!5Wu)XOY1*qqwYinBbU8_g&hDy{Z^4xm2^ZMy_WJ67%=FUK=kGi^ zoNne&&t}E_?Te-FZ>KJBAv)gdn_7*WZ{VWr=I6ihkG{SB-)^_d@n4=iefE5P{@+Jg zLjQNpav^aXVjgPWdQrPe4UO39dPh`e=Lf!2-j?9h4*xgoz=oRE*&ynh!0)Eb^msN7 zjOK-|XMA=25svYIgkEodQfzTyWP(aIsoxCPKzS@Su9rTf5*6k-g8RHcC({`R#TxT) zTX>&vTl6lAst3r>)@7-3bN;W1X94y)SZkV_=nH*DlBaQip6$a&I=V>vefkkKy=mcpN&|VM^PK3AK>((L z*?LrAq21Ve6DJiNFw%JwFbB%C(bM~zXci};KU+oQGoak{H8(G z@tX@(7c}971Y3T8%iyWp5_WJ;2m7_6z~Sx**Ld(-zvSt%Sqv;9Kok5 zEQEJgmVXqR8-a!C)qyiZhwi0#bY`#?zFiu?G8Gnj)8j5fY!6&jS(iH~H$VTiU()UE zzwN!fy=nVztF=D=@1rbn{%7W2z4C^I&oJ#+3eh~=SaGcbLG{9y{YnEI0VkIlO9M77 zPzN1`2UXHEV%nf*ed9KNPffZN2l-pjq=$p6Ys|yk08b+-Cq6*%9gX|!c9(ri0~#xu z=O9M3;6`3r= zb|7NFEx3-3IB-S4kD<9{E%(D(uE>`WOamAJ(3dJq10sJwG!DtfS*;#rK-c?7%-wFb zi}|}PbK+(kaUwY-W1SzC_7RRa!QsRt7Y!HMmAT8bwyHYUy9I}LPxIH(E<{b^u^hfD z#sDe%(6=l6>i3#ks2P!XAcsxVZowKdwT5t^gPK6EW|+d~nHR`r^G{JM@(LvTSg+*c z?0qM?hH!uD#VpnWL2BC2pS2fCJy&c79l_2;6!O5Q{_nc8e-wKwvnA{48xJ%ms$fey zi1>q!62rtFQHJ5BkA6}5enNvQ6Z}2P24Ry8>-`z(&NQfcO^}CZEFFHSVAj8wnGOtX zhKS=hWTOmmx*NTm$U`p~iwKU3UHvj7I%xw#I1GQ0n2dT%+vgYPIwOZFL8r7H+WrBo z8T{u3rePk#S_?PU79!G~v7gO*qkz1HA1TytwTu}&%1EwrfSOIqFdkWWN?+&GFGpv} zey~+-$7bc6`t26-%}QsL4UL|eMn7q`T;?6Aq>=CkME*o3KX5iul}S+__JqWN8Zw^hER}mmL*S5u;rXTIzmPne%Vxx`2}hfX zuOCLq5To|imj3fiIwG6~sJ(?Y()R>u9IvUz@)+-DgPdX+N*67B7FQA*-Fu zn#Di7a&!JKlgMZu^G<*{{J-_ARnGtRbpPo(|J%KkrTBjw5-t!nMnQP@lexKA%J65% z*g45?=qo=E;Opw6KpP9n5_jDxFg-+xgHPG-$GPDm=X^4W>@S&~UK2gmZL(?FC_U@n zcoc0_w|+p+2XDUrwQXz#{-1wOT2uS~$&=^%>-~QpWr_W--~G^7$KsLH}nteZ8*|tHC%(u|SvtKUA7itD}Vmonr=U)vojCaXS;_*AoZCG~l zF=1SyPoMG*zI-`&w0V{GNFXCXjx`5(S%m(D&1|HpTYIM;Z#v9TGtq9$ zoQx^C){Ysmq}*JJvOaqJH}Tlm5D|@$%|D^9;%mbo<$=Ibf)0PsroCy&fhkgm^TUB{ zR%I&7jHP3>e)(;jZUQ}`8Fo{BFKcnAW>9rAix@XGjkOZC5M264C}y!|YA-@^_1AW# z+xsgrX&31b?(CPP#KL%GpAR#AHGcjZCu70|`AL1*IY8!5Q>#d2Zq@Q4-|Py}ue=u8 zD%|KfgufPX49dEA%CvR*DW`f|F1ERX*^g!1(<99ZB5yK+w1(F_fwXT z|9{a7j&P5q(gb|vEQ%m9Bp%EX`!iZG^)K;YM)fhC zkrU8s%Y+UpNALQqZ)ks@afq58|Ml1KyGNhJ5br&G)@gt0s6xLSp1(PL^J52{=d(v4 zktC2LM4L(^cLIlf`O;C}s5)Q1Z0#T;Y{XgYOC8WdUt~uU#EF_Fi9`Jg3y&EMr?G9~ zjwcZO93oJXcuYBqwc9i}XVyoc%+t%;Ae1TCb|j8N&y3=!Q9P$+!?9hg505`dxiSBD zJWzH&1XzCv9ETiN9tQL5KhK|*^S?iTwzrP|bT4HI{%<^V2s*N>ou`P;Lk456Qy@%* z_+G?^DW{{X!#d-T4g8cjSy?CEqOP!*V@A=t+|<*~82MyPFmp9Q?U7-9U^A#Q!-wz; zw0d0Y{Z-|e>SvCr`AGKB3>JA?#uZw9bgcEgj-G#S?HRc!avklny~;5dp`@p&p7GRj|+8kMJB#(RO-ok^0_{KxmJ|>FPFwF#u@~%0?Y|z zpMQuxW#_Ad{}dkrOgYqvvQ=ssXT+)5)OsRT&}MTUV7Y;7`nPJg!rGLU6;wD%BT18~ zaQJ_=-wMw~*A=kz+-hF>=EL+OjVR_5)K6nACK;18gfgdtp|rL%#!C-+X7aANJ0Z)L zThEQY0#}#|cENJy=ODTX7E2zud7pagl%LR8b9TwmU{;q4ceJX7H#;|<%5rOOnOF6q zFTNPIp9NOIwE@YUKYB<`jp$8dK05`TzOTDgVFcd+Yqa_fu{n|G!qNef)Ce z(!Zsm2P6C2iPlQ~@cQj`QokMJZe;!nlrAOl+xDhAoXX*ih5k}s)gnLU!H`MrshiGpjp<&$j!INsgj%(zP_bS$G9=)s6F?(}thXqUM(9a3-3Ktj!)si(e*`U?ZMPNu-9(XJu$vXD@pckRtosdB__|(hsleNA zAGrGNveRYuSV3mHL*@Mrw_9Nfx7laziL}hds%f`k!;IR$ORQ`zMSqj4nuC8oXGL8u zS+!#B_YeGu>x%3@EFQ2M+JD-u{ptJP{q_6L`zbfF|ESeIez|$RwR#hfA*6?60?OxF zZw1mo-_j104|GSSplQ@DX9_ayS7Hh(_ElpFnr*--c2Q%%nQKleO{(6U^w3N}$5`SX z7UlM#`TD+0Nf*TW_D>s;^lyK2dpb>a1=cS@l+;FfIW1O{@W?+i!o{>SiXf#JbQ>XsZLMym+XrrSTi7;JCm`RYZDyrPOzjNjnP;5sbOZCuoti>t=^NjO(QMWfZo~ZU z-W2+^jV%wbtj7MA*RL`G&awZspFV$9wEyj^`fK~&eUzKn|IYMkU;OV^E4TkSg!Euc zfH_R{CcwPT?W};#Sa*M81*}H!QiedMi(5VespzTF5|}BsS$$U*p5LpI9PGCux6sWQ z!*QCJVPmQutrlBa#&Y;LysinxwU8bBIidKq> zA<7Ff1KG>(L9ZYJM{B%mS5LQ9(rQUrgQXrKnpS%=y*v896e zHiT&*+|Cu5c&zW)-)0?LwOMf>Xpl+lW1$TppbL!+)b@-jap~GqWA+S+5`IY*uL+u3 zS<4y6Ax}*ykOF6JD-(UkHEa}D{j1rx<{y#ESv=V)tj-BqPY7FQ*q^dR>$33L@&wd%Dv9%YQ_A{9W8&kIm@e5UcX^-sh!gtlBpqy zq_oXldYD!3K^ml2WB=tOr1gQ|=Dh!Xy7y$iaD);^X&}2dW5^N^-dvkDKoEbeM<{(v6I=x-C9@ALMK;bBK!pE)_G{E z>ecRz5CDI*G+?uAC7bT-XF-)Yc5nAEFsbpG`V+yj6eB^z0YLG+1fO5qF1r*UDs%`UARt znROd?%^t3j^d~N>(f`~tZdwbR7ytFyeyi;N(|-1Rt^eIexrzE`^dVITG`${~R`#?A z4fTQ19!i!4W zR84>O791dpOZoU8n8SdBuRpD?uw zb@jMT;^y7O18kZ6Kl8@d)y&)sFd>$>dWiaK@df1g{It`#Lj24;Qc#kX&E-Bt!+y5V z^(pA}v}YtypDpQRRA!S6&D`zt=TtkFW;uUE+1xEzNHFCJM$X6ern%ouQ<6HTdM{-@ z&#FQK1ZRGZB~i^V0b#UXpT~Ue-YJ`OZknPe-*I(M{9uf1Vx0pd36GVk&}^bj=TU1j zmxH%#L@wu>>C4dZ)HSG^txjzRbCeaT42V2RIVZ8q?sA(`$uMyoW}KstPi<T#d}k3jg^7ppRAEW^-yf1_Ly}f7c_5IJilnr!-rQ&~7kjzuBtU7jL)Jtg;DlVSj;0h0j@EaTGa!3Ud zX_7E51rkFNMc@*FMpyv(RdmM+24HT1scB0+XCIaKEi2mD_kKV@7 z1hE)eDv(e@IErXY{Dyyg@!>)pa*Yji1f+=G9bKT1a?$VyRPMt6HTVs`_aA@03;(xY z3o z>-~Q(Wdps`d7ZwxH?%%EHj9k6w7~nR)fKaJuHTe zM@WH>vVkWkO_ZFP)dIM1Lf_eO$L+=yjl&L78ay4m#z|vDBv#V10$BrGp)x^wV85S6 zk#?1XypFcjXre_U)chxjB8GlpJPMl{h>Z<&8p|vZSfc?ixXJZ-?fw0B7wcx3F zC4>ufJ*2@91vpkB5LkbL!4G~+B9@?-T$}YUDae2trq+MU9ujDTV>}?-Z!}&Oma{Mb zaQyc_r9Fa^MC83)nQve@G|7Y{iP&VMghM5)>h9rB)<-~@N4kF+qaaF!BwYCVn1Ww5 z8irV)o}w{HgwipPA zK%&05Z%7gr8k9`oBw?WiWLJHTC9MK1gCt<1gvBJ589cdxs9U6yI!H81rf_n1kicd- z!U^utXu<6?wGcPjU5l5heK>!s-F6EtTdD?d7dmio>?{6mj`D7Cs%G64UzC_D7Vq$2 zBXyuneM)}@5mlVgzDc3|EZM2kKcWIG4}!=oUr~tP)rT3fDWGei%uhu>7d^TqC~=Z- zmT(GI+^kmVRP~o#@g<-X5=+ugBSDH_E()dF{_dcpF+7GSBC@>VGoZBXmqX;TQ1TuQ zra)W*%JY|pM~D-_QXY_HfL#j8fQMB;E)3<-`SE`Z5V*9gZ500hklz86YQgKelGMi`V0KbNS=1C;OB?GMfkd-hd(3l#-0 zvl~xGcrah!x-aeJ#%10fxi6*X#>J&vNDVLaKiv$K&bgva6{1O}pGFaanqY64UI9Zp zS-SZd+2~QzzHWTf-9ds7^#0T~`)zXr>TrL?nMCXl32q+L8kw{il< zEVv?E(;zx011cn+AmbJK29Jm+O&}xr+Bvj$>VMnY%4b-7Yv0ZFkW($>%-j(xI*{&f zd#tR?+-ED=E#J%;mX`wY8!5SmgDVn)ap6bfzJRc$d_!f(R6-R@4dpB#Ul zoo2FFwi5*5V;T_aE5cYa`|gZ^Pe>2*rrE)&m-lwGkR+U#&@M5PG$@(-w2J3bw%d!? zV0W+8{(iUhe7F5#7bnznS__ZHUfXLw+jV%sB$oJNW-SZU=G9y374DJfKF^qYJPq8H zk6@lk8H_`1Lvi@Fqe9yXNH?gS)ogz$-wXwJjvBqU;jne0I#igoZnt8D#g)@bH*4i? z#TVwSoxYP$|L^9&sRLFaGj<$$GPm(m5Y)BDh^p~+OgN_@>CRof;suDRf5eIA57v;P zew?5`QXDCKhf0+MFs60J#Rdbg4Mpm@FsUR;bCb?OYgS7v=A@s!&2M>feuIA}=8|PL z>$>I9_zj+{OBR>#wa-*>Q)ee}d0QI?tJ!N~PvEIHq7@Qn>5WsIYvGoEx;WU~0Ew?bbQM-0TCuZg2s zGj!0G+Z*&vZnpw`nFA=}h!cOwDH#)R3!;65BTjHQK>?f$q3_Cn1)pKh#G5H%2sO=x zc=)b(A7znj$8I;LzuRkWp=LzlfgCnbyM>xUauyHr$LEUwn_&u{b0XeBy8RB)Z9!Sp zbq`0z>sH%WSmu^nMSZZMqFb<8+L(1pnkHlLR>~ZNEov?LbbBAoMPGmQ<^UqM z#{;suE9tLu=&tS}`|F%~tGoWrC^u1ZnvhDjs%Dx_g$i5FS^n#9n;TBu8hZ{<(`BNA zQ>{v_d%zOhibSsmEM&PfDU&D*o)L$HYkE|lMD`K}-Yh7_h-oZxe=64?oz{?|_WUlM z2-$Ov>=_9XLzYG%(jb56r@3~RolL>#bMi+@Vi`?rL{GBlHEXf9{avR@Svkdle5V^a=kmrBR4TJf@t*BmGW-Q*hJ*VO`(FU@bl@Bg5=gTkR0rf?7pK>9cDJY1+KNFj`R-u_HZ&jN3W?~>>Wa@j&L z0Q~qiFOIwBG}|$+XqAqiy$mes_NOrlumHQ;BzLRWalEp%7sMdHDjTwEdu%#KsgEv{ zGV{)jAn@DfhJ95F^V~vn*01=Sy<4?@wn}{r2y`kDH)nqfMq-GS0IQukAW;_hdSvgV zms!8n1X1WNhN=fy=B;1pCA}~=8}J&!C8s*q9#|opDy$gC@#f1qEu9%Sy?ocr2yWEP z_l|j#F0u`z&1ax1^@w)bQ}BQ}j!JWJED(#$L;49zH3CvWY!ZK?HQ7rojix)|M|0M% zDmPDe>GgkevNNq@38&Z3$u{zJm(U3ud@rjwXZ=d?HnaPI<<<{k-Thsp$v-Y%|9w~e zd%Ja5-h8Z09wT|=7 zKa)xFmgk?&Vfx=W8T^mCJOoYN>92eUYAEja5Cng<{uj(+8V}~{^b6?kw^Fg^Dy0i3 zfVXqPaj5#*V8!PwifBBz!~-P(7-S*bW2rPRaZL>*@c@{7=9Jo9-buBwv4JiU5>Ol= z3K$f@kc_Z!+@V!h`HN?6yrw!0gybY49n~R?2MvV6e7QQ-)yh_PA{uGuS}IV)0xU_` zK!|^Q#G@o49TY+-KCOdGE#p8h>mwBp%G`R`(1#|AxskP6UbNV-0_gaX`ue!kptt10gDj4uLfrQX9c3njm3; zj&&!9^>gseIDVrMl3waywWlMHPGLZUY+u>{R{|y$(JP{UQAA8WN{$hY`;3pYgQkue zAl$Xku}KvPAI6{)<2WFuvmWU*HZtmS32UPe7NAHL8w$-ei@brHRYoqt8yiSZ-9Ue2 z9_Mc$=ZZ1kKql_&Ts+xet-3yPE+~B$2?~#mhS0a%M-dic9tr+9WI_k^$^n$<9gETt zvDf7YdfGv!eM@3ZuepQVVctMIgJ#n_5)Y|Ra0(Po6hZ?Xfk05E_*Vhqrq^sVa1an7 zUbB$Q1NRVWq7e)8oF}z#7X~^fIQ)NwQ%T;!0tot_xr1HM|Bxj{c=01`G!iU_^ZGqA zk>q;$LWYH&vLF<*$k~#PUi6K^a%jA6AP2D}1Z*I{006xKmlMvg8O;`*h;p)p@D+1r z=|+09zDL%T%$@9LVtT(Klh-&&Xbfzl(3kl>3lgIHG>82andlp^DwkCCBaMISfY9*k z`;^)a@Te-;aP2>lNrp;=OPH_%ELEK97xQWgqV^#{VnHEb66{3~ zB;BwVG|ukFc938rf+QUgp*pz6F>K0#YAu=So$IUMgb6|6!Q(^^3zEW+P%g^YP%PW7 z?gq*O{2E-r_nzlf$2c)qxp^#r6J*@I^+AM_WbvkY3Ew98kCbGAQu5XK^vSt`H}d^w z)O+@X#6cEZ{mYk^KWYOO2o5hUetCO-+?QQy111I{lbIu4IhToQ10*Qr$%QXJYCaXP zSg?rPCJ1q>b-!VVTkRVJB3>{O@!Vt+w+=}RYjfI{)oKGYf11sm5;hN-O`!S5rK$_Y zdn^y|$Zquys*6$#$oe>zjcnhMj|t-vIo%vQ+Pq48B#;p_wsB#>WkDo3AlaLToEnhF z(C>o93cQ2nCtvYA-=V`VO~v4_AkUs4&kISwLbBDULPYyUh-kZrXs-ql?K?n3yBZNT zSk}+>p|bJie`BcaxBOP4@&0X$-mozl^$17p{T;N|+H38YxBq)K>>;|w0)axJbfP|u zsTh(le=;_pa+vlYgq0#>6KQNiNGwy~vA!pV#4`~Dw|0;mQh`SJcgEfL0+3FE2sLdZ z&Sb`_V2I-Z5jseej@G4q&tqYk9S7=yWQs*Fp0Lcne?Jf0)F)V`Ty+(OI;fML>b{nz zpcl0(jQ|W@A=yx728c>QqP|kGFb|ECk)iD5?JK|0*xvpHT;>em*Mt*9O~f){q70Xj z@ISY=8x0SgSyyKBwb1CW(Y^G1;muJ|Jn5oM!$1HHy$R|uIn<5zV0!x&_fAJ@Wp#Kr zn0~R(f4%8Gb;K>i;%Ktvqxb(LoXif5XG7vC62J*HlhVFOTc866I948s(PpA{CgOl0DAb*Eq&@B0O?`uLCC4O80#;5rQi z7q*i3HXs(x{%we`nTKLy+&DKz$q@I5qycOwe-L+vY{Uj6CTiuc@uXl~wE^3l;BSh= zjufGWgzLjkM<&Qa62K;*85wEfIyGhoBSqT?1Esp!$IJ1mho=ad5Ns?vvu}2wKkdC4 zKbEMk*cXl`hzgMsvG|Z$UN^hQ(5VPWj5%dl*dV=7#y!++9Wo-+@RniO-H79fTE`rW ze|#4Dab$6?xl_}7Mu9D6!Cgd#IZrt06K>hmd)Dt$urornkOisIa$wbF@1619hw=W0 z5gq6y-rdMPdZ{2@fCUj-W|x?s;(=Z~I|s?ocwZ6A&w}*R2t_o$5}Fq5$ZEtwR7jah z#wm`(E+>LTW8x{`_Tc~fUc!X*azOMtGVeP5Ei#je>hRK7*C3vn1B~a5_dLnW$Ng_9 a%kjIToM3uzaRzx delta 212000 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õy3tJtEt)+&=W0U$aY~)c^x0q}>hKE9AW_Hs^ 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⪚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 diff --git a/assets/jfrog/artifactory-jcr-107.104.10.tgz b/assets/jfrog/artifactory-jcr-107.104.10.tgz new file mode 100644 index 0000000000000000000000000000000000000000..2d16ab46b38200fcad9de878a8e400f46de29a4e GIT binary patch literal 470909 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