From eef03c16a747116837aaab06bcb17e867c3fae2d Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Thu, 19 Sep 2024 00:56:12 +0000 Subject: [PATCH] Added chart versions: cockroach-labs/cockroachdb: - 14.0.2 codefresh/cf-runtime: - 6.4.0 jenkins/jenkins: - 5.6.2 traefik/traefik: - 31.1.0 trilio/k8s-triliovault-operator: - 4.0.5 --- assets/cockroach-labs/cockroachdb-14.0.2.tgz | Bin 0 -> 31915 bytes assets/codefresh/cf-runtime-6.4.0.tgz | Bin 0 -> 43748 bytes assets/jenkins/jenkins-5.6.2.tgz | Bin 0 -> 78180 bytes assets/traefik/traefik-31.1.0.tgz | Bin 0 -> 241743 bytes .../trilio/k8s-triliovault-operator-4.0.5.tgz | Bin 0 -> 109155 bytes .../cockroachdb/14.0.2/CONTRIBUTING.md | 14 + .../cockroachdb/14.0.2/Chart.yaml | 18 + .../cockroachdb/14.0.2/README.md | 586 + .../cockroachdb/14.0.2/app-readme.md | 9 + .../cockroachdb/14.0.2/templates/NOTES.txt | 50 + .../cockroachdb/14.0.2/templates/_helpers.tpl | 291 + .../14.0.2/templates/backendconfig.yaml | 21 + .../14.0.2/templates/certificate.ca.yaml | 33 + .../14.0.2/templates/certificate.client.yaml | 40 + .../14.0.2/templates/certificate.issuer.yaml | 20 + .../14.0.2/templates/certificate.node.yaml | 50 + .../14.0.2/templates/clusterrole.yaml | 19 + .../14.0.2/templates/clusterrolebinding.yaml | 23 + .../templates/cronjob-ca-certSelfSigner.yaml | 62 + .../cronjob-client-node-certSelfSigner.yaml | 69 + .../cockroachdb/14.0.2/templates/ingress.yaml | 90 + .../14.0.2/templates/job-certSelfSigner.yaml | 83 + .../14.0.2/templates/job-cleaner.yaml | 70 + .../14.0.2/templates/job.init.yaml | 303 + .../14.0.2/templates/networkpolicy.yaml | 59 + .../14.0.2/templates/poddisruptionbudget.yaml | 26 + .../templates/role-certRotateSelfSigner.yaml | 27 + .../14.0.2/templates/role-certSelfSigner.yaml | 33 + .../cockroachdb/14.0.2/templates/role.yaml | 23 + .../rolebinding-certRotateSelfSigner.yaml | 23 + .../templates/rolebinding-certSelfSigner.yaml | 29 + .../14.0.2/templates/rolebinding.yaml | 23 + .../templates/secret.backendconfig.yaml | 25 + .../14.0.2/templates/secret.logconfig.yaml | 19 + .../14.0.2/templates/secret.registry.yaml | 23 + .../14.0.2/templates/secrets.init.yaml | 20 + .../14.0.2/templates/service.discovery.yaml | 64 + .../14.0.2/templates/service.public.yaml | 55 + .../14.0.2/templates/serviceMonitor.yaml | 54 + .../serviceaccount-certRotateSelfSigner.yaml | 22 + .../serviceaccount-certSelfSigner.yaml | 25 + .../14.0.2/templates/serviceaccount.yaml | 21 + .../14.0.2/templates/statefulset.yaml | 402 + .../14.0.2/templates/tests/client.yaml | 65 + .../cockroachdb/14.0.2/values.schema.json | 97 + .../cockroachdb/14.0.2/values.yaml | 600 + charts/codefresh/cf-runtime/6.4.0/.helmignore | 3 + charts/codefresh/cf-runtime/6.4.0/Chart.yaml | 28 + charts/codefresh/cf-runtime/6.4.0/README.md | 1230 ++ .../cf-runtime/6.4.0/README.md.gotmpl | 1007 ++ .../cf-runtime/6.4.0/files/cleanup-runtime.sh | 37 + .../6.4.0/files/configure-dind-certs.sh | 132 + .../cf-runtime/6.4.0/files/init-runtime.sh | 80 + .../6.4.0/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 | 28 + .../_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/6.4.0/templates/_helpers.tpl | 51 + .../6.4.0/templates/app-proxy/deployment.yaml | 9 + .../6.4.0/templates/app-proxy/ingress.yaml | 9 + .../6.4.0/templates/app-proxy/rbac.yaml | 9 + .../6.4.0/templates/app-proxy/service.yaml | 9 + .../templates/event-exporter/deployment.yaml | 9 + .../6.4.0/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 + .../6.4.0/templates/monitor/deployment.yaml | 9 + .../6.4.0/templates/monitor/rbac.yaml | 9 + .../6.4.0/templates/monitor/service.yaml | 9 + .../templates/other/external-secrets.yaml | 2 + .../6.4.0/templates/other/podMonitor.yaml | 2 + .../6.4.0/templates/other/serviceMonitor.yaml | 2 + .../6.4.0/templates/runner/deployment.yaml | 9 + .../6.4.0/templates/runner/rbac.yaml | 9 + .../6.4.0/templates/runtime/_helpers.tpl | 123 + .../templates/runtime/cm-dind-daemon.yaml | 10 + .../6.4.0/templates/runtime/rbac.yaml | 48 + .../runtime/runtime-env-spec-tmpl.yaml | 214 + .../6.4.0/templates/runtime/secret.yaml | 11 + .../6.4.0/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 + charts/codefresh/cf-runtime/6.4.0/values.yaml | 951 ++ charts/jenkins/jenkins/5.6.2/CHANGELOG.md | 3118 ++++ charts/jenkins/jenkins/5.6.2/Chart.yaml | 54 + charts/jenkins/jenkins/5.6.2/README.md | 711 + charts/jenkins/jenkins/5.6.2/UPGRADING.md | 148 + charts/jenkins/jenkins/5.6.2/VALUES.md | 317 + charts/jenkins/jenkins/5.6.2/VALUES.md.gotmpl | 28 + .../jenkins/jenkins/5.6.2/templates/NOTES.txt | 68 + .../jenkins/5.6.2/templates/_helpers.tpl | 684 + .../5.6.2/templates/auto-reload-config.yaml | 60 + .../5.6.2/templates/config-init-scripts.yaml | 18 + .../jenkins/5.6.2/templates/config.yaml | 92 + .../jenkins/5.6.2/templates/deprecation.yaml | 151 + .../jenkins/5.6.2/templates/home-pvc.yaml | 41 + .../jenkins/5.6.2/templates/jcasc-config.yaml | 53 + .../5.6.2/templates/jenkins-agent-svc.yaml | 43 + .../jenkins-aws-security-group-policies.yaml | 16 + .../jenkins-controller-alerting-rules.yaml | 26 + .../jenkins-controller-backendconfig.yaml | 24 + .../templates/jenkins-controller-ingress.yaml | 77 + .../jenkins-controller-networkpolicy.yaml | 76 + .../templates/jenkins-controller-pdb.yaml | 34 + .../jenkins-controller-podmonitor.yaml | 30 + .../templates/jenkins-controller-route.yaml | 34 + .../jenkins-controller-secondary-ingress.yaml | 56 + .../jenkins-controller-servicemonitor.yaml | 45 + .../jenkins-controller-statefulset.yaml | 427 + .../templates/jenkins-controller-svc.yaml | 56 + .../jenkins/jenkins/5.6.2/templates/rbac.yaml | 149 + .../5.6.2/templates/secret-additional.yaml | 21 + .../5.6.2/templates/secret-claims.yaml | 29 + .../5.6.2/templates/secret-https-jks.yaml | 20 + .../jenkins/5.6.2/templates/secret.yaml | 20 + .../templates/service-account-agent.yaml | 26 + .../5.6.2/templates/service-account.yaml | 26 + .../5.6.2/templates/tests/jenkins-test.yaml | 49 + .../5.6.2/templates/tests/test-config.yaml | 14 + charts/jenkins/jenkins/5.6.2/values.yaml | 1363 ++ charts/traefik/traefik/31.1.0/.helmignore | 2 + charts/traefik/traefik/31.1.0/Changelog.md | 10084 ++++++++++++ charts/traefik/traefik/31.1.0/Chart.yaml | 34 + charts/traefik/traefik/31.1.0/EXAMPLES.md | 1005 ++ charts/traefik/traefik/31.1.0/Guidelines.md | 34 + charts/traefik/traefik/31.1.0/LICENSE | 202 + charts/traefik/traefik/31.1.0/README.md | 158 + charts/traefik/traefik/31.1.0/VALUES.md | 316 + charts/traefik/traefik/31.1.0/app-readme.md | 5 + .../crds/gateway-standard-install-v1.1.0.yaml | 13478 ++++++++++++++++ .../hub.traefik.io_accesscontrolpolicies.yaml | 368 + .../crds/hub.traefik.io_apiaccesses.yaml | 153 + .../crds/hub.traefik.io_apiportals.yaml | 139 + .../crds/hub.traefik.io_apiratelimits.yaml | 166 + .../31.1.0/crds/hub.traefik.io_apis.yaml | 190 + .../crds/hub.traefik.io_apiversions.yaml | 194 + .../31.1.0/crds/traefik.io_ingressroutes.yaml | 366 + .../crds/traefik.io_ingressroutetcps.yaml | 247 + .../crds/traefik.io_ingressrouteudps.yaml | 111 + .../31.1.0/crds/traefik.io_middlewares.yaml | 1098 ++ .../crds/traefik.io_middlewaretcps.yaml | 87 + .../crds/traefik.io_serverstransports.yaml | 139 + .../crds/traefik.io_serverstransporttcps.yaml | 120 + .../31.1.0/crds/traefik.io_tlsoptions.yaml | 114 + .../31.1.0/crds/traefik.io_tlsstores.yaml | 97 + .../crds/traefik.io_traefikservices.yaml | 639 + .../traefik/31.1.0/templates/NOTES.txt | 36 + .../traefik/31.1.0/templates/_helpers.tpl | 161 + .../traefik/31.1.0/templates/_podtemplate.tpl | 837 + .../31.1.0/templates/_service-metrics.tpl | 25 + .../traefik/31.1.0/templates/_service.tpl | 84 + .../traefik/31.1.0/templates/daemonset.yaml | 50 + .../traefik/31.1.0/templates/deployment.yaml | 50 + .../31.1.0/templates/extra-objects.yaml | 4 + .../traefik/31.1.0/templates/gateway.yaml | 58 + .../31.1.0/templates/gatewayclass.yaml | 14 + .../traefik/traefik/31.1.0/templates/hpa.yaml | 35 + .../templates/hub-admission-controller.yaml | 198 + .../31.1.0/templates/hub-apiportal.yaml | 19 + .../31.1.0/templates/ingressclass.yaml | 12 + .../31.1.0/templates/ingressroute.yaml | 43 + .../31.1.0/templates/poddisruptionbudget.yaml | 23 + .../31.1.0/templates/prometheusrules.yaml | 28 + .../31.1.0/templates/provider-file-cm.yaml | 12 + .../traefik/traefik/31.1.0/templates/pvc.yaml | 26 + .../31.1.0/templates/rbac/clusterrole.yaml | 254 + .../templates/rbac/clusterrolebinding.yaml | 17 + .../templates/rbac/podsecuritypolicy.yaml | 68 + .../traefik/31.1.0/templates/rbac/role.yaml | 143 + .../31.1.0/templates/rbac/rolebinding.yaml | 25 + .../31.1.0/templates/rbac/serviceaccount.yaml | 14 + .../31.1.0/templates/requirements.yaml | 29 + .../31.1.0/templates/service-metrics.yaml | 33 + .../traefik/31.1.0/templates/service.yaml | 83 + .../31.1.0/templates/servicemonitor.yaml | 69 + .../traefik/31.1.0/templates/tlsoption.yaml | 39 + .../traefik/31.1.0/templates/tlsstore.yaml | 12 + .../traefik/traefik/31.1.0/values.schema.json | 1668 ++ charts/traefik/traefik/31.1.0/values.yaml | 945 ++ .../4.0.5/.helmignore | 23 + .../4.0.5/BUILD.bazel | 9 + .../k8s-triliovault-operator/4.0.5/Chart.yaml | 24 + .../k8s-triliovault-operator/4.0.5/LICENSE | 1 + .../k8s-triliovault-operator/4.0.5/README.md | 206 + .../4.0.5/charts/observability/Chart.yaml | 21 + .../observability/charts/logging/Chart.yaml | 18 + .../charts/logging/charts/loki/Chart.yaml | 13 + .../logging/charts/loki/templates/NOTES.txt | 3 + .../charts/loki/templates/_helpers.tpl | 99 + .../loki/templates/configmap-alert.yaml | 14 + .../charts/loki/templates/ingress.yaml | 52 + .../charts/loki/templates/networkpolicy.yaml | 23 + .../logging/charts/loki/templates/pdb.yaml | 14 + .../loki/templates/podsecuritypolicy.yaml | 40 + .../charts/loki/templates/prometheusrule.yaml | 20 + .../logging/charts/loki/templates/role.yaml | 17 + .../charts/loki/templates/rolebinding.yaml | 17 + .../logging/charts/loki/templates/secret.yaml | 11 + .../loki/templates/service-headless.yaml | 26 + .../loki/templates/service-memberlist.yaml | 21 + .../charts/loki/templates/service.yaml | 43 + .../charts/loki/templates/serviceaccount.yaml | 13 + .../charts/loki/templates/servicemonitor.yaml | 42 + .../charts/loki/templates/statefulset.yaml | 180 + .../charts/logging/charts/loki/values.yaml | 346 + .../charts/logging/charts/promtail/Chart.yaml | 16 + .../charts/promtail/templates/NOTES.txt | 15 + .../charts/promtail/templates/_helpers.tpl | 102 + .../charts/promtail/templates/_pod.tpl | 118 + .../promtail/templates/clusterrole.yaml | 21 + .../templates/clusterrolebinding.yaml | 16 + .../charts/promtail/templates/configmap.yaml | 12 + .../charts/promtail/templates/daemonset.yaml | 21 + .../charts/promtail/templates/deployment.yaml | 22 + .../promtail/templates/extra-manifests.yaml | 4 + .../charts/promtail/templates/hpa.yaml | 31 + .../promtail/templates/networkpolicy.yaml | 123 + .../promtail/templates/podsecuritypolicy.yaml | 10 + .../promtail/templates/prometheus-rules.yaml | 21 + .../charts/promtail/templates/role.yaml | 18 + .../promtail/templates/rolebinding.yaml | 16 + .../charts/promtail/templates/secret.yaml | 19 + .../promtail/templates/service-extra.yaml | 52 + .../promtail/templates/service-metrics.yaml | 18 + .../promtail/templates/serviceaccount.yaml | 17 + .../promtail/templates/servicemonitor.yaml | 58 + .../logging/charts/promtail/values.yaml | 534 + .../charts/logging/templates/_helpers.tpl | 50 + .../charts/logging/templates/datasources.yaml | 24 + .../charts/monitoring/Chart.yaml | 16 + .../monitoring/charts/prometheus/Chart.yaml | 22 + .../charts/kube-state-metrics/Chart.yaml | 17 + .../kube-state-metrics/templates/NOTES.txt | 10 + .../kube-state-metrics/templates/_helpers.tpl | 82 + .../templates/clusterrolebinding.yaml | 20 + .../templates/deployment.yaml | 156 + .../templates/kubeconfig-secret.yaml | 12 + .../kube-state-metrics/templates/pdb.yaml | 14 + .../templates/podsecuritypolicy.yaml | 39 + .../templates/psp-clusterrole.yaml | 19 + .../templates/psp-clusterrolebinding.yaml | 16 + .../kube-state-metrics/templates/role.yaml | 187 + .../templates/rolebinding.yaml | 24 + .../kube-state-metrics/templates/service.yaml | 38 + .../templates/serviceaccount.yaml | 15 + .../templates/servicemonitor.yaml | 66 + .../templates/stsdiscovery-role.yaml | 26 + .../templates/stsdiscovery-rolebinding.yaml | 17 + .../charts/kube-state-metrics/values.yaml | 233 + .../charts/prometheus/templates/NOTES.txt | 112 + .../charts/prometheus/templates/_helpers.tpl | 288 + .../templates/alertmanager/clusterrole.yaml | 21 + .../alertmanager/clusterrolebinding.yaml | 20 + .../prometheus/templates/alertmanager/cm.yaml | 19 + .../templates/alertmanager/deploy.yaml | 208 + .../templates/alertmanager/headless-svc.yaml | 31 + .../templates/alertmanager/ingress.yaml | 57 + .../templates/alertmanager/netpol.yaml | 20 + .../templates/alertmanager/pdb.yaml | 14 + .../templates/alertmanager/psp.yaml | 46 + .../templates/alertmanager/pvc.yaml | 37 + .../templates/alertmanager/role.yaml | 24 + .../templates/alertmanager/rolebinding.yaml | 23 + .../templates/alertmanager/service.yaml | 53 + .../alertmanager/serviceaccount.yaml | 11 + .../templates/alertmanager/sts.yaml | 188 + .../templates/node-exporter/daemonset.yaml | 150 + .../templates/node-exporter/psp.yaml | 55 + .../templates/node-exporter/role.yaml | 17 + .../templates/node-exporter/rolebinding.yaml | 19 + .../node-exporter/serviceaccount.yaml | 11 + .../templates/node-exporter/svc.yaml | 47 + .../templates/pushgateway/clusterrole.yaml | 21 + .../pushgateway/clusterrolebinding.yaml | 16 + .../templates/pushgateway/deploy.yaml | 119 + .../templates/pushgateway/ingress.yaml | 54 + .../templates/pushgateway/netpol.yaml | 20 + .../prometheus/templates/pushgateway/pdb.yaml | 14 + .../prometheus/templates/pushgateway/psp.yaml | 42 + .../prometheus/templates/pushgateway/pvc.yaml | 31 + .../templates/pushgateway/service.yaml | 41 + .../templates/pushgateway/serviceaccount.yaml | 11 + .../prometheus/templates/pushgateway/vpa.yaml | 20 + .../templates/server/clusterrole.yaml | 48 + .../templates/server/clusterrolebinding.yaml | 16 + .../prometheus/templates/server/cm.yaml | 85 + .../prometheus/templates/server/deploy.yaml | 324 + .../templates/server/extra-manifests.yaml | 4 + .../templates/server/headless-svc.yaml | 37 + .../prometheus/templates/server/ingress.yaml | 59 + .../prometheus/templates/server/netpol.yaml | 18 + .../prometheus/templates/server/pdb.yaml | 14 + .../prometheus/templates/server/psp.yaml | 51 + .../prometheus/templates/server/pvc.yaml | 39 + .../templates/server/rolebinding.yaml | 20 + .../prometheus/templates/server/service.yaml | 60 + .../templates/server/serviceaccount.yaml | 13 + .../prometheus/templates/server/sts.yaml | 302 + .../prometheus/templates/server/vpa.yaml | 24 + .../monitoring/charts/prometheus/values.yaml | 1861 +++ .../charts/monitoring/templates/_helpers.tpl | 49 + .../monitoring/templates/datasources.yaml | 25 + .../charts/visualization/Chart.yaml | 16 + .../visualization/charts/grafana/Chart.yaml | 14 + .../grafana/dashboards/backup-detail.json | 926 ++ .../grafana/dashboards/backup-overview.json | 883 + .../grafana/dashboards/backupplan-detail.json | 1198 ++ .../dashboards/backupplan-overview.json | 883 + .../dashboards/clusterbackup-detail.json | 820 + .../dashboards/clusterbackupplan-detail.json | 1234 ++ .../dashboards/clusterrestore-detail.json | 802 + .../dashboards/consistentset-detail.json | 832 + .../continuousrestoreplan-detail.json | 1052 ++ .../grafana/dashboards/logging-dashboard.json | 212 + .../grafana/dashboards/metadata-detail.json | 889 + .../charts/grafana/dashboards/overview.json | 1429 ++ .../grafana/dashboards/restore-detail.json | 901 ++ .../grafana/dashboards/restore-overview.json | 853 + .../grafana/dashboards/target-detail.json | 1327 ++ .../charts/grafana/templates/NOTES.txt | 54 + .../charts/grafana/templates/_helpers.tpl | 165 + .../charts/grafana/templates/_pod.tpl | 748 + .../charts/grafana/templates/clusterrole.yaml | 25 + .../grafana/templates/clusterrolebinding.yaml | 24 + .../configmap-dashboard-provider.yaml | 29 + .../charts/grafana/templates/configmap.yaml | 88 + .../templates/dashboards-json-configmap.yaml | 36 + .../charts/grafana/templates/deployment.yaml | 50 + .../grafana/templates/extra-manifests.yaml | 4 + .../grafana/templates/headless-service.yaml | 22 + .../charts/grafana/templates/hpa.yaml | 20 + .../templates/image-renderer-deployment.yaml | 121 + .../image-renderer-network-policy.yaml | 76 + .../templates/image-renderer-service.yaml | 30 + .../charts/grafana/templates/ingress.yaml | 78 + .../grafana/templates/networkpolicy.yaml | 37 + .../templates/poddisruptionbudget.yaml | 22 + .../grafana/templates/podsecuritypolicy.yaml | 49 + .../charts/grafana/templates/pvc.yaml | 33 + .../charts/grafana/templates/role.yaml | 32 + .../charts/grafana/templates/rolebinding.yaml | 25 + .../charts/grafana/templates/secret-env.yaml | 14 + .../charts/grafana/templates/secret.yaml | 26 + .../charts/grafana/templates/service.yaml | 51 + .../grafana/templates/serviceaccount.yaml | 14 + .../grafana/templates/servicemonitor.yaml | 44 + .../charts/grafana/templates/statefulset.yaml | 52 + .../charts/grafana/templates/user-secret.yaml | 9 + .../visualization/charts/grafana/values.yaml | 938 ++ ...iovault.trilio.io_triliovaultmanagers.yaml | 1227 ++ .../4.0.5/questions.yaml | 158 + .../4.0.5/templates/NOTES.txt | 59 + .../4.0.5/templates/TVMCustomResource.yaml | 64 + .../4.0.5/templates/TVMSecret.yaml | 25 + .../4.0.5/templates/_helpers.tpl | 134 + .../4.0.5/templates/clusterrole.yaml | 148 + .../4.0.5/templates/clusterrole_binding.yaml | 15 + .../4.0.5/templates/deployment.yaml | 276 + .../4.0.5/templates/mutating-webhook.yaml | 28 + .../preflight_job_preinstall_hook.yaml | 196 + .../4.0.5/templates/secret.yaml | 9 + .../4.0.5/templates/serviceAccount.yaml | 14 + .../4.0.5/templates/validating-webhook.yaml | 104 + .../4.0.5/templates/webhook-service.yaml | 17 + .../4.0.5/values.yaml | 242 + index.yaml | 180 +- 405 files changed, 81062 insertions(+), 1 deletion(-) create mode 100644 assets/cockroach-labs/cockroachdb-14.0.2.tgz create mode 100644 assets/codefresh/cf-runtime-6.4.0.tgz create mode 100644 assets/jenkins/jenkins-5.6.2.tgz create mode 100644 assets/traefik/traefik-31.1.0.tgz create mode 100644 assets/trilio/k8s-triliovault-operator-4.0.5.tgz create mode 100644 charts/cockroach-labs/cockroachdb/14.0.2/CONTRIBUTING.md create mode 100644 charts/cockroach-labs/cockroachdb/14.0.2/Chart.yaml create mode 100644 charts/cockroach-labs/cockroachdb/14.0.2/README.md create mode 100644 charts/cockroach-labs/cockroachdb/14.0.2/app-readme.md create mode 100644 charts/cockroach-labs/cockroachdb/14.0.2/templates/NOTES.txt create mode 100644 charts/cockroach-labs/cockroachdb/14.0.2/templates/_helpers.tpl create mode 100644 charts/cockroach-labs/cockroachdb/14.0.2/templates/backendconfig.yaml create mode 100644 charts/cockroach-labs/cockroachdb/14.0.2/templates/certificate.ca.yaml create mode 100644 charts/cockroach-labs/cockroachdb/14.0.2/templates/certificate.client.yaml create mode 100644 charts/cockroach-labs/cockroachdb/14.0.2/templates/certificate.issuer.yaml create mode 100644 charts/cockroach-labs/cockroachdb/14.0.2/templates/certificate.node.yaml create mode 100644 charts/cockroach-labs/cockroachdb/14.0.2/templates/clusterrole.yaml create mode 100644 charts/cockroach-labs/cockroachdb/14.0.2/templates/clusterrolebinding.yaml create mode 100644 charts/cockroach-labs/cockroachdb/14.0.2/templates/cronjob-ca-certSelfSigner.yaml create mode 100644 charts/cockroach-labs/cockroachdb/14.0.2/templates/cronjob-client-node-certSelfSigner.yaml create mode 100644 charts/cockroach-labs/cockroachdb/14.0.2/templates/ingress.yaml create mode 100644 charts/cockroach-labs/cockroachdb/14.0.2/templates/job-certSelfSigner.yaml create mode 100644 charts/cockroach-labs/cockroachdb/14.0.2/templates/job-cleaner.yaml create mode 100644 charts/cockroach-labs/cockroachdb/14.0.2/templates/job.init.yaml create mode 100644 charts/cockroach-labs/cockroachdb/14.0.2/templates/networkpolicy.yaml create mode 100644 charts/cockroach-labs/cockroachdb/14.0.2/templates/poddisruptionbudget.yaml create mode 100644 charts/cockroach-labs/cockroachdb/14.0.2/templates/role-certRotateSelfSigner.yaml create mode 100644 charts/cockroach-labs/cockroachdb/14.0.2/templates/role-certSelfSigner.yaml create mode 100644 charts/cockroach-labs/cockroachdb/14.0.2/templates/role.yaml create mode 100644 charts/cockroach-labs/cockroachdb/14.0.2/templates/rolebinding-certRotateSelfSigner.yaml create mode 100644 charts/cockroach-labs/cockroachdb/14.0.2/templates/rolebinding-certSelfSigner.yaml create mode 100644 charts/cockroach-labs/cockroachdb/14.0.2/templates/rolebinding.yaml create mode 100644 charts/cockroach-labs/cockroachdb/14.0.2/templates/secret.backendconfig.yaml create mode 100644 charts/cockroach-labs/cockroachdb/14.0.2/templates/secret.logconfig.yaml create mode 100644 charts/cockroach-labs/cockroachdb/14.0.2/templates/secret.registry.yaml create mode 100644 charts/cockroach-labs/cockroachdb/14.0.2/templates/secrets.init.yaml create mode 100644 charts/cockroach-labs/cockroachdb/14.0.2/templates/service.discovery.yaml create mode 100644 charts/cockroach-labs/cockroachdb/14.0.2/templates/service.public.yaml create mode 100644 charts/cockroach-labs/cockroachdb/14.0.2/templates/serviceMonitor.yaml create mode 100644 charts/cockroach-labs/cockroachdb/14.0.2/templates/serviceaccount-certRotateSelfSigner.yaml create mode 100644 charts/cockroach-labs/cockroachdb/14.0.2/templates/serviceaccount-certSelfSigner.yaml create mode 100644 charts/cockroach-labs/cockroachdb/14.0.2/templates/serviceaccount.yaml create mode 100644 charts/cockroach-labs/cockroachdb/14.0.2/templates/statefulset.yaml create mode 100644 charts/cockroach-labs/cockroachdb/14.0.2/templates/tests/client.yaml create mode 100644 charts/cockroach-labs/cockroachdb/14.0.2/values.schema.json create mode 100644 charts/cockroach-labs/cockroachdb/14.0.2/values.yaml create mode 100644 charts/codefresh/cf-runtime/6.4.0/.helmignore create mode 100644 charts/codefresh/cf-runtime/6.4.0/Chart.yaml create mode 100644 charts/codefresh/cf-runtime/6.4.0/README.md create mode 100644 charts/codefresh/cf-runtime/6.4.0/README.md.gotmpl create mode 100644 charts/codefresh/cf-runtime/6.4.0/files/cleanup-runtime.sh create mode 100644 charts/codefresh/cf-runtime/6.4.0/files/configure-dind-certs.sh create mode 100644 charts/codefresh/cf-runtime/6.4.0/files/init-runtime.sh create mode 100644 charts/codefresh/cf-runtime/6.4.0/files/reconcile-runtime.sh create mode 100644 charts/codefresh/cf-runtime/6.4.0/templates/_components/app-proxy/_deployment.yaml create mode 100644 charts/codefresh/cf-runtime/6.4.0/templates/_components/app-proxy/_env-vars.yaml create mode 100644 charts/codefresh/cf-runtime/6.4.0/templates/_components/app-proxy/_helpers.tpl create mode 100644 charts/codefresh/cf-runtime/6.4.0/templates/_components/app-proxy/_ingress.yaml create mode 100644 charts/codefresh/cf-runtime/6.4.0/templates/_components/app-proxy/_rbac.yaml create mode 100644 charts/codefresh/cf-runtime/6.4.0/templates/_components/app-proxy/_service.yaml create mode 100644 charts/codefresh/cf-runtime/6.4.0/templates/_components/event-exporter/_deployment.yaml create mode 100644 charts/codefresh/cf-runtime/6.4.0/templates/_components/event-exporter/_env-vars.yaml create mode 100644 charts/codefresh/cf-runtime/6.4.0/templates/_components/event-exporter/_helpers.tpl create mode 100644 charts/codefresh/cf-runtime/6.4.0/templates/_components/event-exporter/_rbac.yaml create mode 100644 charts/codefresh/cf-runtime/6.4.0/templates/_components/event-exporter/_service.yaml create mode 100644 charts/codefresh/cf-runtime/6.4.0/templates/_components/event-exporter/_serviceMontor.yaml create mode 100644 charts/codefresh/cf-runtime/6.4.0/templates/_components/monitor/_deployment.yaml create mode 100644 charts/codefresh/cf-runtime/6.4.0/templates/_components/monitor/_env-vars.yaml create mode 100644 charts/codefresh/cf-runtime/6.4.0/templates/_components/monitor/_helpers.tpl create mode 100644 charts/codefresh/cf-runtime/6.4.0/templates/_components/monitor/_rbac.yaml create mode 100644 charts/codefresh/cf-runtime/6.4.0/templates/_components/monitor/_service.yaml create mode 100644 charts/codefresh/cf-runtime/6.4.0/templates/_components/runner/_deployment.yaml create mode 100644 charts/codefresh/cf-runtime/6.4.0/templates/_components/runner/_helpers.tpl create mode 100644 charts/codefresh/cf-runtime/6.4.0/templates/_components/runner/_rbac.yaml create mode 100644 charts/codefresh/cf-runtime/6.4.0/templates/_components/runner/environment-variables/_init-container.yaml create mode 100644 charts/codefresh/cf-runtime/6.4.0/templates/_components/runner/environment-variables/_main-container.yaml create mode 100644 charts/codefresh/cf-runtime/6.4.0/templates/_components/runner/environment-variables/_sidecar-container.yaml create mode 100644 charts/codefresh/cf-runtime/6.4.0/templates/_components/volume-provisioner/_cronjob.yaml create mode 100644 charts/codefresh/cf-runtime/6.4.0/templates/_components/volume-provisioner/_daemonset.yaml create mode 100644 charts/codefresh/cf-runtime/6.4.0/templates/_components/volume-provisioner/_deployment.yaml create mode 100644 charts/codefresh/cf-runtime/6.4.0/templates/_components/volume-provisioner/_env-vars.yaml create mode 100644 charts/codefresh/cf-runtime/6.4.0/templates/_components/volume-provisioner/_helpers.tpl create mode 100644 charts/codefresh/cf-runtime/6.4.0/templates/_components/volume-provisioner/_rbac.yaml create mode 100644 charts/codefresh/cf-runtime/6.4.0/templates/_components/volume-provisioner/_secret.yaml create mode 100644 charts/codefresh/cf-runtime/6.4.0/templates/_components/volume-provisioner/_storageclass.yaml create mode 100644 charts/codefresh/cf-runtime/6.4.0/templates/_helpers.tpl create mode 100644 charts/codefresh/cf-runtime/6.4.0/templates/app-proxy/deployment.yaml create mode 100644 charts/codefresh/cf-runtime/6.4.0/templates/app-proxy/ingress.yaml create mode 100644 charts/codefresh/cf-runtime/6.4.0/templates/app-proxy/rbac.yaml create mode 100644 charts/codefresh/cf-runtime/6.4.0/templates/app-proxy/service.yaml create mode 100644 charts/codefresh/cf-runtime/6.4.0/templates/event-exporter/deployment.yaml create mode 100644 charts/codefresh/cf-runtime/6.4.0/templates/event-exporter/rbac.yaml create mode 100644 charts/codefresh/cf-runtime/6.4.0/templates/event-exporter/service.yaml create mode 100644 charts/codefresh/cf-runtime/6.4.0/templates/extra/extra-resources.yaml create mode 100644 charts/codefresh/cf-runtime/6.4.0/templates/extra/runtime-images-cm.yaml create mode 100644 charts/codefresh/cf-runtime/6.4.0/templates/hooks/post-install/cm-update-runtime.yaml create mode 100644 charts/codefresh/cf-runtime/6.4.0/templates/hooks/post-install/job-gencerts-dind.yaml create mode 100644 charts/codefresh/cf-runtime/6.4.0/templates/hooks/post-install/job-update-runtime.yaml create mode 100644 charts/codefresh/cf-runtime/6.4.0/templates/hooks/post-install/rbac-gencerts-dind.yaml create mode 100644 charts/codefresh/cf-runtime/6.4.0/templates/hooks/pre-delete/job-cleanup-resources.yaml create mode 100644 charts/codefresh/cf-runtime/6.4.0/templates/hooks/pre-delete/rbac-cleanup-resources.yaml create mode 100644 charts/codefresh/cf-runtime/6.4.0/templates/monitor/deployment.yaml create mode 100644 charts/codefresh/cf-runtime/6.4.0/templates/monitor/rbac.yaml create mode 100644 charts/codefresh/cf-runtime/6.4.0/templates/monitor/service.yaml create mode 100644 charts/codefresh/cf-runtime/6.4.0/templates/other/external-secrets.yaml create mode 100644 charts/codefresh/cf-runtime/6.4.0/templates/other/podMonitor.yaml create mode 100644 charts/codefresh/cf-runtime/6.4.0/templates/other/serviceMonitor.yaml create mode 100644 charts/codefresh/cf-runtime/6.4.0/templates/runner/deployment.yaml create mode 100644 charts/codefresh/cf-runtime/6.4.0/templates/runner/rbac.yaml create mode 100644 charts/codefresh/cf-runtime/6.4.0/templates/runtime/_helpers.tpl create mode 100644 charts/codefresh/cf-runtime/6.4.0/templates/runtime/cm-dind-daemon.yaml create mode 100644 charts/codefresh/cf-runtime/6.4.0/templates/runtime/rbac.yaml create mode 100644 charts/codefresh/cf-runtime/6.4.0/templates/runtime/runtime-env-spec-tmpl.yaml create mode 100644 charts/codefresh/cf-runtime/6.4.0/templates/runtime/secret.yaml create mode 100644 charts/codefresh/cf-runtime/6.4.0/templates/runtime/svc-dind.yaml create mode 100644 charts/codefresh/cf-runtime/6.4.0/templates/volume-provisioner/cronjob.yaml create mode 100644 charts/codefresh/cf-runtime/6.4.0/templates/volume-provisioner/daemonset.yaml create mode 100644 charts/codefresh/cf-runtime/6.4.0/templates/volume-provisioner/deployment.yaml create mode 100644 charts/codefresh/cf-runtime/6.4.0/templates/volume-provisioner/rbac.yaml create mode 100644 charts/codefresh/cf-runtime/6.4.0/templates/volume-provisioner/secret.yaml create mode 100644 charts/codefresh/cf-runtime/6.4.0/templates/volume-provisioner/storageclass.yaml create mode 100644 charts/codefresh/cf-runtime/6.4.0/values.yaml create mode 100644 charts/jenkins/jenkins/5.6.2/CHANGELOG.md create mode 100644 charts/jenkins/jenkins/5.6.2/Chart.yaml create mode 100644 charts/jenkins/jenkins/5.6.2/README.md create mode 100644 charts/jenkins/jenkins/5.6.2/UPGRADING.md create mode 100644 charts/jenkins/jenkins/5.6.2/VALUES.md create mode 100644 charts/jenkins/jenkins/5.6.2/VALUES.md.gotmpl create mode 100644 charts/jenkins/jenkins/5.6.2/templates/NOTES.txt create mode 100644 charts/jenkins/jenkins/5.6.2/templates/_helpers.tpl create mode 100644 charts/jenkins/jenkins/5.6.2/templates/auto-reload-config.yaml create mode 100644 charts/jenkins/jenkins/5.6.2/templates/config-init-scripts.yaml create mode 100644 charts/jenkins/jenkins/5.6.2/templates/config.yaml create mode 100644 charts/jenkins/jenkins/5.6.2/templates/deprecation.yaml create mode 100644 charts/jenkins/jenkins/5.6.2/templates/home-pvc.yaml create mode 100644 charts/jenkins/jenkins/5.6.2/templates/jcasc-config.yaml create mode 100644 charts/jenkins/jenkins/5.6.2/templates/jenkins-agent-svc.yaml create mode 100644 charts/jenkins/jenkins/5.6.2/templates/jenkins-aws-security-group-policies.yaml create mode 100644 charts/jenkins/jenkins/5.6.2/templates/jenkins-controller-alerting-rules.yaml create mode 100644 charts/jenkins/jenkins/5.6.2/templates/jenkins-controller-backendconfig.yaml create mode 100644 charts/jenkins/jenkins/5.6.2/templates/jenkins-controller-ingress.yaml create mode 100644 charts/jenkins/jenkins/5.6.2/templates/jenkins-controller-networkpolicy.yaml create mode 100644 charts/jenkins/jenkins/5.6.2/templates/jenkins-controller-pdb.yaml create mode 100644 charts/jenkins/jenkins/5.6.2/templates/jenkins-controller-podmonitor.yaml create mode 100644 charts/jenkins/jenkins/5.6.2/templates/jenkins-controller-route.yaml create mode 100644 charts/jenkins/jenkins/5.6.2/templates/jenkins-controller-secondary-ingress.yaml create mode 100644 charts/jenkins/jenkins/5.6.2/templates/jenkins-controller-servicemonitor.yaml create mode 100644 charts/jenkins/jenkins/5.6.2/templates/jenkins-controller-statefulset.yaml create mode 100644 charts/jenkins/jenkins/5.6.2/templates/jenkins-controller-svc.yaml create mode 100644 charts/jenkins/jenkins/5.6.2/templates/rbac.yaml create mode 100644 charts/jenkins/jenkins/5.6.2/templates/secret-additional.yaml create mode 100644 charts/jenkins/jenkins/5.6.2/templates/secret-claims.yaml create mode 100644 charts/jenkins/jenkins/5.6.2/templates/secret-https-jks.yaml create mode 100644 charts/jenkins/jenkins/5.6.2/templates/secret.yaml create mode 100644 charts/jenkins/jenkins/5.6.2/templates/service-account-agent.yaml create mode 100644 charts/jenkins/jenkins/5.6.2/templates/service-account.yaml create mode 100644 charts/jenkins/jenkins/5.6.2/templates/tests/jenkins-test.yaml create mode 100644 charts/jenkins/jenkins/5.6.2/templates/tests/test-config.yaml create mode 100644 charts/jenkins/jenkins/5.6.2/values.yaml create mode 100644 charts/traefik/traefik/31.1.0/.helmignore create mode 100644 charts/traefik/traefik/31.1.0/Changelog.md create mode 100644 charts/traefik/traefik/31.1.0/Chart.yaml create mode 100644 charts/traefik/traefik/31.1.0/EXAMPLES.md create mode 100644 charts/traefik/traefik/31.1.0/Guidelines.md create mode 100644 charts/traefik/traefik/31.1.0/LICENSE create mode 100644 charts/traefik/traefik/31.1.0/README.md create mode 100644 charts/traefik/traefik/31.1.0/VALUES.md create mode 100644 charts/traefik/traefik/31.1.0/app-readme.md create mode 100644 charts/traefik/traefik/31.1.0/crds/gateway-standard-install-v1.1.0.yaml create mode 100644 charts/traefik/traefik/31.1.0/crds/hub.traefik.io_accesscontrolpolicies.yaml create mode 100644 charts/traefik/traefik/31.1.0/crds/hub.traefik.io_apiaccesses.yaml create mode 100644 charts/traefik/traefik/31.1.0/crds/hub.traefik.io_apiportals.yaml create mode 100644 charts/traefik/traefik/31.1.0/crds/hub.traefik.io_apiratelimits.yaml create mode 100644 charts/traefik/traefik/31.1.0/crds/hub.traefik.io_apis.yaml create mode 100644 charts/traefik/traefik/31.1.0/crds/hub.traefik.io_apiversions.yaml create mode 100644 charts/traefik/traefik/31.1.0/crds/traefik.io_ingressroutes.yaml create mode 100644 charts/traefik/traefik/31.1.0/crds/traefik.io_ingressroutetcps.yaml create mode 100644 charts/traefik/traefik/31.1.0/crds/traefik.io_ingressrouteudps.yaml create mode 100644 charts/traefik/traefik/31.1.0/crds/traefik.io_middlewares.yaml create mode 100644 charts/traefik/traefik/31.1.0/crds/traefik.io_middlewaretcps.yaml create mode 100644 charts/traefik/traefik/31.1.0/crds/traefik.io_serverstransports.yaml create mode 100644 charts/traefik/traefik/31.1.0/crds/traefik.io_serverstransporttcps.yaml create mode 100644 charts/traefik/traefik/31.1.0/crds/traefik.io_tlsoptions.yaml create mode 100644 charts/traefik/traefik/31.1.0/crds/traefik.io_tlsstores.yaml create mode 100644 charts/traefik/traefik/31.1.0/crds/traefik.io_traefikservices.yaml create mode 100644 charts/traefik/traefik/31.1.0/templates/NOTES.txt create mode 100644 charts/traefik/traefik/31.1.0/templates/_helpers.tpl create mode 100644 charts/traefik/traefik/31.1.0/templates/_podtemplate.tpl create mode 100644 charts/traefik/traefik/31.1.0/templates/_service-metrics.tpl create mode 100644 charts/traefik/traefik/31.1.0/templates/_service.tpl create mode 100644 charts/traefik/traefik/31.1.0/templates/daemonset.yaml create mode 100644 charts/traefik/traefik/31.1.0/templates/deployment.yaml create mode 100644 charts/traefik/traefik/31.1.0/templates/extra-objects.yaml create mode 100644 charts/traefik/traefik/31.1.0/templates/gateway.yaml create mode 100644 charts/traefik/traefik/31.1.0/templates/gatewayclass.yaml create mode 100644 charts/traefik/traefik/31.1.0/templates/hpa.yaml create mode 100644 charts/traefik/traefik/31.1.0/templates/hub-admission-controller.yaml create mode 100644 charts/traefik/traefik/31.1.0/templates/hub-apiportal.yaml create mode 100644 charts/traefik/traefik/31.1.0/templates/ingressclass.yaml create mode 100644 charts/traefik/traefik/31.1.0/templates/ingressroute.yaml create mode 100644 charts/traefik/traefik/31.1.0/templates/poddisruptionbudget.yaml create mode 100644 charts/traefik/traefik/31.1.0/templates/prometheusrules.yaml create mode 100644 charts/traefik/traefik/31.1.0/templates/provider-file-cm.yaml create mode 100644 charts/traefik/traefik/31.1.0/templates/pvc.yaml create mode 100644 charts/traefik/traefik/31.1.0/templates/rbac/clusterrole.yaml create mode 100644 charts/traefik/traefik/31.1.0/templates/rbac/clusterrolebinding.yaml create mode 100644 charts/traefik/traefik/31.1.0/templates/rbac/podsecuritypolicy.yaml create mode 100644 charts/traefik/traefik/31.1.0/templates/rbac/role.yaml create mode 100644 charts/traefik/traefik/31.1.0/templates/rbac/rolebinding.yaml create mode 100644 charts/traefik/traefik/31.1.0/templates/rbac/serviceaccount.yaml create mode 100644 charts/traefik/traefik/31.1.0/templates/requirements.yaml create mode 100644 charts/traefik/traefik/31.1.0/templates/service-metrics.yaml create mode 100644 charts/traefik/traefik/31.1.0/templates/service.yaml create mode 100644 charts/traefik/traefik/31.1.0/templates/servicemonitor.yaml create mode 100644 charts/traefik/traefik/31.1.0/templates/tlsoption.yaml create mode 100644 charts/traefik/traefik/31.1.0/templates/tlsstore.yaml create mode 100644 charts/traefik/traefik/31.1.0/values.schema.json create mode 100644 charts/traefik/traefik/31.1.0/values.yaml create mode 100644 charts/trilio/k8s-triliovault-operator/4.0.5/.helmignore create mode 100644 charts/trilio/k8s-triliovault-operator/4.0.5/BUILD.bazel create mode 100644 charts/trilio/k8s-triliovault-operator/4.0.5/Chart.yaml create mode 100644 charts/trilio/k8s-triliovault-operator/4.0.5/LICENSE create mode 100644 charts/trilio/k8s-triliovault-operator/4.0.5/README.md create mode 100644 charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/Chart.yaml create mode 100644 charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/logging/Chart.yaml create mode 100644 charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/logging/charts/loki/Chart.yaml create mode 100644 charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/logging/charts/loki/templates/NOTES.txt create mode 100644 charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/logging/charts/loki/templates/_helpers.tpl create mode 100644 charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/logging/charts/loki/templates/configmap-alert.yaml create mode 100644 charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/logging/charts/loki/templates/ingress.yaml create mode 100644 charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/logging/charts/loki/templates/networkpolicy.yaml create mode 100644 charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/logging/charts/loki/templates/pdb.yaml create mode 100644 charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/logging/charts/loki/templates/podsecuritypolicy.yaml create mode 100644 charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/logging/charts/loki/templates/prometheusrule.yaml create mode 100644 charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/logging/charts/loki/templates/role.yaml create mode 100644 charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/logging/charts/loki/templates/rolebinding.yaml create mode 100644 charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/logging/charts/loki/templates/secret.yaml create mode 100644 charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/logging/charts/loki/templates/service-headless.yaml create mode 100644 charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/logging/charts/loki/templates/service-memberlist.yaml create mode 100644 charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/logging/charts/loki/templates/service.yaml create mode 100644 charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/logging/charts/loki/templates/serviceaccount.yaml create mode 100644 charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/logging/charts/loki/templates/servicemonitor.yaml create mode 100644 charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/logging/charts/loki/templates/statefulset.yaml create mode 100644 charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/logging/charts/loki/values.yaml create mode 100644 charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/logging/charts/promtail/Chart.yaml create mode 100644 charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/logging/charts/promtail/templates/NOTES.txt create mode 100644 charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/logging/charts/promtail/templates/_helpers.tpl create mode 100644 charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/logging/charts/promtail/templates/_pod.tpl create mode 100644 charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/logging/charts/promtail/templates/clusterrole.yaml create mode 100644 charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/logging/charts/promtail/templates/clusterrolebinding.yaml create mode 100644 charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/logging/charts/promtail/templates/configmap.yaml create mode 100644 charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/logging/charts/promtail/templates/daemonset.yaml create mode 100644 charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/logging/charts/promtail/templates/deployment.yaml create mode 100644 charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/logging/charts/promtail/templates/extra-manifests.yaml create mode 100644 charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/logging/charts/promtail/templates/hpa.yaml create mode 100644 charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/logging/charts/promtail/templates/networkpolicy.yaml create mode 100644 charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/logging/charts/promtail/templates/podsecuritypolicy.yaml create mode 100644 charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/logging/charts/promtail/templates/prometheus-rules.yaml create mode 100644 charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/logging/charts/promtail/templates/role.yaml create mode 100644 charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/logging/charts/promtail/templates/rolebinding.yaml create mode 100644 charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/logging/charts/promtail/templates/secret.yaml create mode 100644 charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/logging/charts/promtail/templates/service-extra.yaml create mode 100644 charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/logging/charts/promtail/templates/service-metrics.yaml create mode 100644 charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/logging/charts/promtail/templates/serviceaccount.yaml create mode 100644 charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/logging/charts/promtail/templates/servicemonitor.yaml create mode 100644 charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/logging/charts/promtail/values.yaml create mode 100644 charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/logging/templates/_helpers.tpl create mode 100644 charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/logging/templates/datasources.yaml create mode 100644 charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/Chart.yaml create mode 100644 charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/Chart.yaml create mode 100644 charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/charts/kube-state-metrics/Chart.yaml create mode 100644 charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/charts/kube-state-metrics/templates/NOTES.txt create mode 100644 charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/charts/kube-state-metrics/templates/_helpers.tpl create mode 100644 charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/charts/kube-state-metrics/templates/clusterrolebinding.yaml create mode 100644 charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/charts/kube-state-metrics/templates/deployment.yaml create mode 100644 charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/charts/kube-state-metrics/templates/kubeconfig-secret.yaml create mode 100644 charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/charts/kube-state-metrics/templates/pdb.yaml create mode 100644 charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/charts/kube-state-metrics/templates/podsecuritypolicy.yaml create mode 100644 charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/charts/kube-state-metrics/templates/psp-clusterrole.yaml create mode 100644 charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/charts/kube-state-metrics/templates/psp-clusterrolebinding.yaml create mode 100644 charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/charts/kube-state-metrics/templates/role.yaml create mode 100644 charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/charts/kube-state-metrics/templates/rolebinding.yaml create mode 100644 charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/charts/kube-state-metrics/templates/service.yaml create mode 100644 charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/charts/kube-state-metrics/templates/serviceaccount.yaml create mode 100644 charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/charts/kube-state-metrics/templates/servicemonitor.yaml create mode 100644 charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/charts/kube-state-metrics/templates/stsdiscovery-role.yaml create mode 100644 charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/charts/kube-state-metrics/templates/stsdiscovery-rolebinding.yaml create mode 100644 charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/charts/kube-state-metrics/values.yaml create mode 100644 charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/templates/NOTES.txt create mode 100644 charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/templates/_helpers.tpl create mode 100644 charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/templates/alertmanager/clusterrole.yaml create mode 100644 charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/templates/alertmanager/clusterrolebinding.yaml create mode 100644 charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/templates/alertmanager/cm.yaml create mode 100644 charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/templates/alertmanager/deploy.yaml create mode 100644 charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/templates/alertmanager/headless-svc.yaml create mode 100644 charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/templates/alertmanager/ingress.yaml create mode 100644 charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/templates/alertmanager/netpol.yaml create mode 100644 charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/templates/alertmanager/pdb.yaml create mode 100644 charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/templates/alertmanager/psp.yaml create mode 100644 charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/templates/alertmanager/pvc.yaml create mode 100644 charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/templates/alertmanager/role.yaml create mode 100644 charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/templates/alertmanager/rolebinding.yaml create mode 100644 charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/templates/alertmanager/service.yaml create mode 100644 charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/templates/alertmanager/serviceaccount.yaml create mode 100644 charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/templates/alertmanager/sts.yaml create mode 100644 charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/templates/node-exporter/daemonset.yaml create mode 100644 charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/templates/node-exporter/psp.yaml create mode 100644 charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/templates/node-exporter/role.yaml create mode 100644 charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/templates/node-exporter/rolebinding.yaml create mode 100644 charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/templates/node-exporter/serviceaccount.yaml create mode 100644 charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/templates/node-exporter/svc.yaml create mode 100644 charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/templates/pushgateway/clusterrole.yaml create mode 100644 charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/templates/pushgateway/clusterrolebinding.yaml create mode 100644 charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/templates/pushgateway/deploy.yaml create mode 100644 charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/templates/pushgateway/ingress.yaml create mode 100644 charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/templates/pushgateway/netpol.yaml create mode 100644 charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/templates/pushgateway/pdb.yaml create mode 100644 charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/templates/pushgateway/psp.yaml create mode 100644 charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/templates/pushgateway/pvc.yaml create mode 100644 charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/templates/pushgateway/service.yaml create mode 100644 charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/templates/pushgateway/serviceaccount.yaml create mode 100644 charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/templates/pushgateway/vpa.yaml create mode 100644 charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/templates/server/clusterrole.yaml create mode 100644 charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/templates/server/clusterrolebinding.yaml create mode 100644 charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/templates/server/cm.yaml create mode 100644 charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/templates/server/deploy.yaml create mode 100644 charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/templates/server/extra-manifests.yaml create mode 100644 charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/templates/server/headless-svc.yaml create mode 100644 charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/templates/server/ingress.yaml create mode 100644 charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/templates/server/netpol.yaml create mode 100644 charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/templates/server/pdb.yaml create mode 100644 charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/templates/server/psp.yaml create mode 100644 charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/templates/server/pvc.yaml create mode 100644 charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/templates/server/rolebinding.yaml create mode 100644 charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/templates/server/service.yaml create mode 100644 charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/templates/server/serviceaccount.yaml create mode 100644 charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/templates/server/sts.yaml create mode 100644 charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/templates/server/vpa.yaml create mode 100644 charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/values.yaml create mode 100644 charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/templates/_helpers.tpl create mode 100644 charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/templates/datasources.yaml create mode 100644 charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/visualization/Chart.yaml create mode 100644 charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/visualization/charts/grafana/Chart.yaml create mode 100644 charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/visualization/charts/grafana/dashboards/backup-detail.json create mode 100644 charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/visualization/charts/grafana/dashboards/backup-overview.json create mode 100644 charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/visualization/charts/grafana/dashboards/backupplan-detail.json create mode 100644 charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/visualization/charts/grafana/dashboards/backupplan-overview.json create mode 100644 charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/visualization/charts/grafana/dashboards/clusterbackup-detail.json create mode 100644 charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/visualization/charts/grafana/dashboards/clusterbackupplan-detail.json create mode 100644 charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/visualization/charts/grafana/dashboards/clusterrestore-detail.json create mode 100644 charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/visualization/charts/grafana/dashboards/consistentset-detail.json create mode 100644 charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/visualization/charts/grafana/dashboards/continuousrestoreplan-detail.json create mode 100644 charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/visualization/charts/grafana/dashboards/logging-dashboard.json create mode 100644 charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/visualization/charts/grafana/dashboards/metadata-detail.json create mode 100644 charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/visualization/charts/grafana/dashboards/overview.json create mode 100644 charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/visualization/charts/grafana/dashboards/restore-detail.json create mode 100644 charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/visualization/charts/grafana/dashboards/restore-overview.json create mode 100644 charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/visualization/charts/grafana/dashboards/target-detail.json create mode 100644 charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/visualization/charts/grafana/templates/NOTES.txt create mode 100644 charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/visualization/charts/grafana/templates/_helpers.tpl create mode 100644 charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/visualization/charts/grafana/templates/_pod.tpl create mode 100644 charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/visualization/charts/grafana/templates/clusterrole.yaml create mode 100644 charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/visualization/charts/grafana/templates/clusterrolebinding.yaml create mode 100644 charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/visualization/charts/grafana/templates/configmap-dashboard-provider.yaml create mode 100644 charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/visualization/charts/grafana/templates/configmap.yaml create mode 100644 charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/visualization/charts/grafana/templates/dashboards-json-configmap.yaml create mode 100644 charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/visualization/charts/grafana/templates/deployment.yaml create mode 100644 charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/visualization/charts/grafana/templates/extra-manifests.yaml create mode 100644 charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/visualization/charts/grafana/templates/headless-service.yaml create mode 100644 charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/visualization/charts/grafana/templates/hpa.yaml create mode 100644 charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/visualization/charts/grafana/templates/image-renderer-deployment.yaml create mode 100644 charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/visualization/charts/grafana/templates/image-renderer-network-policy.yaml create mode 100644 charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/visualization/charts/grafana/templates/image-renderer-service.yaml create mode 100644 charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/visualization/charts/grafana/templates/ingress.yaml create mode 100644 charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/visualization/charts/grafana/templates/networkpolicy.yaml create mode 100644 charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/visualization/charts/grafana/templates/poddisruptionbudget.yaml create mode 100644 charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/visualization/charts/grafana/templates/podsecuritypolicy.yaml create mode 100644 charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/visualization/charts/grafana/templates/pvc.yaml create mode 100644 charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/visualization/charts/grafana/templates/role.yaml create mode 100644 charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/visualization/charts/grafana/templates/rolebinding.yaml create mode 100644 charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/visualization/charts/grafana/templates/secret-env.yaml create mode 100644 charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/visualization/charts/grafana/templates/secret.yaml create mode 100644 charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/visualization/charts/grafana/templates/service.yaml create mode 100644 charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/visualization/charts/grafana/templates/serviceaccount.yaml create mode 100644 charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/visualization/charts/grafana/templates/servicemonitor.yaml create mode 100644 charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/visualization/charts/grafana/templates/statefulset.yaml create mode 100644 charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/visualization/charts/grafana/templates/user-secret.yaml create mode 100644 charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/visualization/charts/grafana/values.yaml create mode 100644 charts/trilio/k8s-triliovault-operator/4.0.5/crds/triliovault.trilio.io_triliovaultmanagers.yaml create mode 100644 charts/trilio/k8s-triliovault-operator/4.0.5/questions.yaml create mode 100644 charts/trilio/k8s-triliovault-operator/4.0.5/templates/NOTES.txt create mode 100644 charts/trilio/k8s-triliovault-operator/4.0.5/templates/TVMCustomResource.yaml create mode 100644 charts/trilio/k8s-triliovault-operator/4.0.5/templates/TVMSecret.yaml create mode 100644 charts/trilio/k8s-triliovault-operator/4.0.5/templates/_helpers.tpl create mode 100644 charts/trilio/k8s-triliovault-operator/4.0.5/templates/clusterrole.yaml create mode 100644 charts/trilio/k8s-triliovault-operator/4.0.5/templates/clusterrole_binding.yaml create mode 100644 charts/trilio/k8s-triliovault-operator/4.0.5/templates/deployment.yaml create mode 100644 charts/trilio/k8s-triliovault-operator/4.0.5/templates/mutating-webhook.yaml create mode 100644 charts/trilio/k8s-triliovault-operator/4.0.5/templates/preflight_job_preinstall_hook.yaml create mode 100644 charts/trilio/k8s-triliovault-operator/4.0.5/templates/secret.yaml create mode 100644 charts/trilio/k8s-triliovault-operator/4.0.5/templates/serviceAccount.yaml create mode 100644 charts/trilio/k8s-triliovault-operator/4.0.5/templates/validating-webhook.yaml create mode 100644 charts/trilio/k8s-triliovault-operator/4.0.5/templates/webhook-service.yaml create mode 100644 charts/trilio/k8s-triliovault-operator/4.0.5/values.yaml diff --git a/assets/cockroach-labs/cockroachdb-14.0.2.tgz b/assets/cockroach-labs/cockroachdb-14.0.2.tgz new file mode 100644 index 0000000000000000000000000000000000000000..e580c15a90d3eb88af3a2bd7fcbb6a6c0859afad GIT binary patch literal 31915 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 literal 0 HcmV?d00001 diff --git a/assets/codefresh/cf-runtime-6.4.0.tgz b/assets/codefresh/cf-runtime-6.4.0.tgz new file mode 100644 index 0000000000000000000000000000000000000000..cf57020ad65da839cc58fddf4036bfa946605ac9 GIT binary patch literal 43748 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 B-tqtd literal 0 HcmV?d00001 diff --git a/assets/jenkins/jenkins-5.6.2.tgz b/assets/jenkins/jenkins-5.6.2.tgz new file mode 100644 index 0000000000000000000000000000000000000000..7ea332753bf70d379cf16e2db3f59b5057c88383 GIT binary patch literal 78180 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 z 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

r@_#*6zZ+oQdH>!#JdtGC^o!>g+V>arSL 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 CTUL7j literal 0 HcmV?d00001 diff --git a/assets/traefik/traefik-31.1.0.tgz b/assets/traefik/traefik-31.1.0.tgz new file mode 100644 index 0000000000000000000000000000000000000000..c2035597d1a55e4306d23db1ede62fb5063fd30e GIT binary patch literal 241743 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

8lLQV+?1w!66no<946fWdQ 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 literal 0 HcmV?d00001 diff --git a/assets/trilio/k8s-triliovault-operator-4.0.5.tgz b/assets/trilio/k8s-triliovault-operator-4.0.5.tgz new file mode 100644 index 0000000000000000000000000000000000000000..e0570786f8a7af2b4c41d90a31ce5e173001caa6 GIT binary patch literal 109155 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%0AZ`T+P4(xVszl5=#8T}t1A_KDvEb4`tm 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%0AAw>^r;2 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 Q<>TW_8-@Oh0_vCl1tOCfp#T5? literal 0 HcmV?d00001 diff --git a/charts/cockroach-labs/cockroachdb/14.0.2/CONTRIBUTING.md b/charts/cockroach-labs/cockroachdb/14.0.2/CONTRIBUTING.md new file mode 100644 index 000000000..e248d72e1 --- /dev/null +++ b/charts/cockroach-labs/cockroachdb/14.0.2/CONTRIBUTING.md @@ -0,0 +1,14 @@ +# Contributing + +Contributions are welcome! + +For every change, please increment the `version` contained in +[Chart.yaml](https://github.com/cockroachdb/helm-charts/blob/master/cockroachdb/Chart.yaml). +The `version` roughly follows the [SEMVER](https://semver.org/) versioning +pattern. For changes which do not affect backwards compatibility, the PATCH or +MINOR version must be incremented, e.g. `4.1.3` -> `4.1.4`. For changes which +affect the backwards compatibility of the chart, the major version must be +incremented, e.g. `4.1.3` -> `5.0.0`. Examples of changes which affect backwards +compatibility include any major version releases of CockroachDB, as well as any +breaking changes to the CockroachDB chart templates. + diff --git a/charts/cockroach-labs/cockroachdb/14.0.2/Chart.yaml b/charts/cockroach-labs/cockroachdb/14.0.2/Chart.yaml new file mode 100644 index 000000000..1646b7d84 --- /dev/null +++ b/charts/cockroach-labs/cockroachdb/14.0.2/Chart.yaml @@ -0,0 +1,18 @@ +annotations: + catalog.cattle.io/certified: partner + catalog.cattle.io/display-name: CockroachDB + catalog.cattle.io/kube-version: '>=1.8-0' + catalog.cattle.io/release-name: cockroachdb +apiVersion: v1 +appVersion: 24.2.0 +description: CockroachDB is a scalable, survivable, strongly-consistent SQL database. +home: https://www.cockroachlabs.com +icon: file://assets/icons/cockroachdb.png +kubeVersion: '>=1.8-0' +maintainers: +- email: helm-charts@cockroachlabs.com + name: cockroachlabs +name: cockroachdb +sources: +- https://github.com/cockroachdb/cockroach +version: 14.0.2 diff --git a/charts/cockroach-labs/cockroachdb/14.0.2/README.md b/charts/cockroach-labs/cockroachdb/14.0.2/README.md new file mode 100644 index 000000000..4a16e47c3 --- /dev/null +++ b/charts/cockroach-labs/cockroachdb/14.0.2/README.md @@ -0,0 +1,586 @@ + +# CockroachDB Helm Chart + +[CockroachDB](https://github.com/cockroachdb/cockroach) - the open source, cloud-native distributed SQL database. + +## Documentation + +Below is a brief overview of operating the CockroachDB Helm Chart and some specific implementation details. For additional information on deploying CockroachDB, please see: +> + +Note that the documentation requires Helm 3.0 or higher. + +## Prerequisites Details + +* Kubernetes 1.8 +* PV support on the underlying infrastructure (only if using `storage.persistentVolume`). [Docker for windows hostpath provisioner is not supported](https://github.com/cockroachdb/docs/issues/3184). +* If you want to secure your cluster to use TLS certificates for all network communication, [Helm must be installed with RBAC privileges](https://helm.sh/docs/topics/rbac/) or else you will get an "attempt to grant extra privileges" error. + +## StatefulSet Details + +* + +## StatefulSet Caveats + +* + +## Chart Details + +This chart will do the following: + +* Set up a dynamically scalable CockroachDB cluster using a Kubernetes StatefulSet. + +## Add the CockroachDB Repository + +```shell +helm repo add cockroachdb https://charts.cockroachdb.com/ +``` + +## Installing the Chart + +To install the chart with the release name `my-release`: + +```shell +helm install my-release cockroachdb/cockroachdb +``` + +Note that for a production cluster, you will likely want to override the following parameters in [`values.yaml`](values.yaml) with your own values. + +- `statefulset.resources.requests.memory` and `statefulset.resources.limits.memory` allocate memory resources to CockroachDB pods in your cluster. +- `conf.cache` and `conf.max-sql-memory` are memory limits that we recommend setting to 1/4 of the above resource allocation. When running CockroachDB, you must set these limits explicitly to avoid running out of memory. +- `storage.persistentVolume.size` defaults to `100Gi` of disk space per pod, which you may increase or decrease for your use case. +- `storage.persistentVolume.storageClass` uses the default storage class for your environment. We strongly recommend that you specify a storage class which uses an SSD. +- `tls.enabled` must be set to `yes`/`true` to deploy in secure mode. + +For more information on overriding the `values.yaml` parameters, please see: +> + +Confirm that all pods are `Running` successfully and init has been completed: + +```shell +kubectl get pods +``` + +``` +NAME READY STATUS RESTARTS AGE +my-release-cockroachdb-0 1/1 Running 0 1m +my-release-cockroachdb-1 1/1 Running 0 1m +my-release-cockroachdb-2 1/1 Running 0 1m +my-release-cockroachdb-init-k6jcr 0/1 Completed 0 1m +``` + +Confirm that persistent volumes are created and claimed for each pod: + +```shell +kubectl get pv +``` + +``` +NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE +pvc-64878ebf-f3f0-11e8-ab5b-42010a8e0035 100Gi RWO Delete Bound default/datadir-my-release-cockroachdb-0 standard 51s +pvc-64945b4f-f3f0-11e8-ab5b-42010a8e0035 100Gi RWO Delete Bound default/datadir-my-release-cockroachdb-1 standard 51s +pvc-649d920d-f3f0-11e8-ab5b-42010a8e0035 100Gi RWO Delete Bound default/datadir-my-release-cockroachdb-2 standard 51s +``` + +### Running in secure mode + +In order to set up a secure cockroachdb cluster set `tls.enabled` to `yes`/`true` + +There are 3 ways to configure a secure cluster, with this chart. This all relates to how the certificates are issued: + +* Self-signer (default) +* Cert-manager +* Manual + +#### Self-signer + +This is the default behaviour, and requires no configuration beyond setting certificate durations if user wants to set custom duration. + +If you are running in this mode, self-signed certificates are created by self-signed utility for the nodes and root client and are stored in a secret. +You can look for the certificates created: +```shell +kubectl get secrets +``` + +```shell +crdb-cockroachdb-ca-secret Opaque 2 23s +crdb-cockroachdb-client-secret kubernetes.io/tls 3 22s +crdb-cockroachdb-node-secret kubernetes.io/tls 3 23s +``` + + +#### Manual + +If you wish to supply the certificates to the nodes yourself set `tls.certs.provided` to `yes`/`true`. You may want to use this if you want to use a different certificate authority from the one being used by Kubernetes or if your Kubernetes cluster doesn't fully support certificate-signing requests. To use this, first set up your certificates and load them into your Kubernetes cluster as Secrets using the commands below: + +```shell +$ mkdir certs +$ mkdir my-safe-directory +$ cockroach cert create-ca --certs-dir=certs --ca-key=my-safe-directory/ca.key +$ cockroach cert create-client root --certs-dir=certs --ca-key=my-safe-directory/ca.key +$ kubectl create secret generic cockroachdb-root --from-file=certs +secret/cockroachdb-root created +$ cockroach cert create-node --certs-dir=certs --ca-key=my-safe-directory/ca.key localhost 127.0.0.1 my-release-cockroachdb-public my-release-cockroachdb-public.my-namespace my-release-cockroachdb-public.my-namespace.svc.cluster.local *.my-release-cockroachdb *.my-release-cockroachdb.my-namespace *.my-release-cockroachdb.my-namespace.svc.cluster.local +$ kubectl create secret generic cockroachdb-node --from-file=certs +secret/cockroachdb-node created +``` + +> Note: The subject alternative names are based on a release called `my-release` in the `my-namespace` namespace. Make sure they match the services created with the release during `helm install` + +If your certificates are stored in tls secrets such as secrets generated by cert-manager, the secret will contain files named: + +* `ca.crt` +* `tls.crt` +* `tls.key` + +Cockroachdb, however, expects the files to be named like this: + +* `ca.crt` +* `node.crt` +* `node.key` +* `client.root.crt` +* `client.root.key` + +By enabling `tls.certs.tlsSecret` the tls secrets are projected on to the correct filenames, when they are mounted to the cockroachdb pods. + +#### Cert-manager + +If you wish to supply certificates with [cert-manager][3], set + +* `tls.certs.certManager` to `yes`/`true` +* `tls.certs.certManagerIssuer` to an IssuerRef (as they appear in certificate resources) pointing to a clusterIssuer or issuer, you have set up in the cluster + +Example issuer: + +```yaml +apiVersion: v1 +kind: Secret +metadata: + name: cockroachdb-ca + namespace: cockroachdb +data: + tls.crt: [BASE64 Encoded ca.crt] + tls.key: [BASE64 Encoded ca.key] +type: kubernetes.io/tls +--- +apiVersion: cert-manager.io/v1alpha3 +kind: Issuer +metadata: + name: cockroachdb-cert-issuer + namespace: cockroachdb +spec: + ca: + secretName: cockroachdb-ca +``` + +## Upgrading the cluster + +### Chart version 3.0.0 and after + +Launch a temporary interactive pod and start the built-in SQL client: + +```shell +kubectl run cockroachdb --rm -it \ +--image=cockroachdb/cockroach \ +--restart=Never \ +-- sql --insecure --host=my-release-cockroachdb-public +``` + +> If you are running in secure mode, you will have to provide a client certificate to the cluster in order to authenticate, so the above command will not work. See [here](https://github.com/cockroachdb/cockroach/blob/master/cloud/kubernetes/client-secure.yaml) for an example of how to set up an interactive SQL shell against a secure cluster or [here](https://github.com/cockroachdb/cockroach/blob/master/cloud/kubernetes/example-app-secure.yaml) for an example application connecting to a secure cluster. + +Set `cluster.preserve_downgrade_option`, where `$current_version` is the CockroachDB version currently running (e.g., `19.2`): + +```sql +> SET CLUSTER SETTING cluster.preserve_downgrade_option = '$current_version'; +``` + +Exit the shell and delete the temporary pod: + +```sql +> \q +``` + +Kick off the upgrade process by changing the new Docker image, where `$new_version` is the CockroachDB version to which you are upgrading: + +```shell +helm upgrade my-release cockroachdb/cockroachdb \ +--set image.tag=$new_version \ +--reuse-values +``` + +Kubernetes will carry out a safe [rolling upgrade](https://kubernetes.io/docs/tutorials/stateful-application/basic-stateful-set/#updating-statefulsets) of your CockroachDB nodes one-by-one. Monitor the cluster's pods until all have been successfully restarted: + +```shell +kubectl get pods +``` + +``` +NAME READY STATUS RESTARTS AGE +my-release-cockroachdb-0 1/1 Running 0 2m +my-release-cockroachdb-1 1/1 Running 0 3m +my-release-cockroachdb-2 1/1 Running 0 3m +my-release-cockroachdb-3 0/1 ContainerCreating 0 25s +my-release-cockroachdb-init-nwjkh 0/1 ContainerCreating 0 6s +``` + +```shell +kubectl get pods \ +-o jsonpath='{range .items[*]}{.metadata.name}{"\t"}{.spec.containers[0].image}{"\n"}' +``` + +``` +my-release-cockroachdb-0 cockroachdb/cockroach:v24.2.0 +my-release-cockroachdb-1 cockroachdb/cockroach:v24.2.0 +my-release-cockroachdb-2 cockroachdb/cockroach:v24.2.0 +my-release-cockroachdb-3 cockroachdb/cockroach:v24.2.0 +``` + +Resume normal operations. Once you are comfortable that the stability and performance of the cluster is what you'd expect post-upgrade, finalize the upgrade: + +```shell +kubectl run cockroachdb --rm -it \ +--image=cockroachdb/cockroach \ +--restart=Never \ +-- sql --insecure --host=my-release-cockroachdb-public +``` + +```sql +> RESET CLUSTER SETTING cluster.preserve_downgrade_option; +> \q +``` + +### Chart versions prior to 3.0.0 + +Due to a change in the label format in version 3.0.0 of this chart, upgrading requires that you delete the StatefulSet. Luckily there is a way to do it without actually deleting all the resources managed by the StatefulSet. Use the workaround below to upgrade from charts versions previous to 3.0.0: + +Get the new labels from the specs rendered by Helm: + +```shell +helm template -f deploy.vals.yml cockroachdb/cockroachdb -x templates/statefulset.yaml \ +| yq r - spec.template.metadata.labels +``` + +``` +app.kubernetes.io/name: cockroachdb +app.kubernetes.io/instance: my-release +app.kubernetes.io/component: cockroachdb +``` + +Place the new labels on all pods of the StatefulSet (change `my-release-cockroachdb-0` to the name of each pod): + +```shell +kubectl label pods my-release-cockroachdb-0 \ +app.kubernetes.io/name=cockroachdb \ +app.kubernetes.io/instance=my-release \ +app.kubernetes.io/component=cockroachdb +``` + +Delete the StatefulSet without deleting pods: + +```shell +kubectl delete statefulset my-release-cockroachdb --cascade=false +``` + +Verify that no pod is deleted and then upgrade as normal. A new StatefulSet will be created, taking over the management of the existing pods and upgrading them if needed. + +### See also + +For more information about upgrading a cluster to the latest major release of CockroachDB, see [Upgrade to CockroachDB](https://www.cockroachlabs.com/docs/stable/upgrade-cockroach-version.html). + +Note that there are sometimes backward-incompatible changes to SQL features between major CockroachDB releases. For details, see the [Upgrade Policy](https://www.cockroachlabs.com/docs/cockroachcloud/upgrade-policy). + +## Configuration + +The following table lists the configurable parameters of the CockroachDB chart and their default values. +For details see the [`values.yaml`](values.yaml) file. + +| Parameter | Description | Default | +| --------- | ----------- | ------- | +| `clusterDomain` | Cluster's default DNS domain | `cluster.local` | +| `conf.attrs` | CockroachDB node attributes | `[]` | +| `conf.cache` | Size of CockroachDB's in-memory cache | `25%` | +| `conf.cluster-name` | Name of CockroachDB cluster | `""` | +| `conf.disable-cluster-name-verification` | Disable CockroachDB cluster name verification | `no` | +| `conf.join` | List of already-existing CockroachDB instances | `[]` | +| `conf.max-disk-temp-storage` | Max storage capacity for temp data | `0` | +| `conf.max-offset` | Max allowed clock offset for CockroachDB cluster | `500ms` | +| `conf.max-sql-memory` | Max memory to use processing SQL querie | `25%` | +| `conf.locality` | Locality attribute for this deployment | `""` | +| `conf.single-node` | Disable CockroachDB clustering (standalone mode) | `no` | +| `conf.sql-audit-dir` | Directory for SQL audit log | `""` | +| `conf.port` | CockroachDB primary serving port in Pods | `26257` | +| `conf.http-port` | CockroachDB HTTP port in Pods | `8080` | +| `conf.path` | CockroachDB data directory mount path | `cockroach-data` | +| `conf.store.enabled` | Enable store configuration for CockroachDB | `false` | +| `conf.store.type` | CockroachDB storage type | `""` | +| `conf.store.size` | CockroachDB storage size | `""` | +| `conf.store.attrs` | CockroachDB storage attributes | `""` | +| `image.repository` | Container image name | `cockroachdb/cockroach` | +| `image.tag` | Container image tag | `v24.2.0` | +| `image.pullPolicy` | Container pull policy | `IfNotPresent` | +| `image.credentials` | `registry`, `user` and `pass` credentials to pull private image | `{}` | +| `statefulset.replicas` | StatefulSet replicas number | `3` | +| `statefulset.updateStrategy` | Update strategy for StatefulSet Pods | `{"type": "RollingUpdate"}` | +| `statefulset.podManagementPolicy` | `OrderedReady`/`Parallel` Pods creation/deletion order | `Parallel` | +| `statefulset.budget.maxUnavailable` | k8s PodDisruptionBudget parameter | `1` | +| `statefulset.args` | Extra command-line arguments | `[]` | +| `statefulset.env` | Extra env vars | `[]` | +| `statefulset.secretMounts` | Additional Secrets to mount at cluster members | `[]` | +| `statefulset.labels` | Additional labels of StatefulSet and its Pods | `{"app.kubernetes.io/component": "cockroachdb"}` | +| `statefulset.annotations` | Additional annotations of StatefulSet Pods | `{}` | +| `statefulset.nodeAffinity` | [Node affinity rules][2] of StatefulSet Pods | `{}` | +| `statefulset.podAffinity` | [Inter-Pod affinity rules][1] of StatefulSet Pods | `{}` | +| `statefulset.podAntiAffinity` | [Anti-affinity rules][1] of StatefulSet Pods | auto | +| `statefulset.podAntiAffinity.topologyKey` | The topologyKey for auto [anti-affinity rules][1] | `kubernetes.io/hostname` | +| `statefulset.podAntiAffinity.type` | Type of auto [anti-affinity rules][1] | `soft` | +| `statefulset.podAntiAffinity.weight` | Weight for `soft` auto [anti-affinity rules][1] | `100` | +| `statefulset.nodeSelector` | Node labels for StatefulSet Pods assignment | `{}` | +| `statefulset.priorityClassName` | [PriorityClassName][4] for StatefulSet Pods | `""` | +| `statefulset.tolerations` | Node taints to tolerate by StatefulSet Pods | `[]` | +| `statefulset.topologySpreadConstraints` | [Topology Spread Constraints rules][5] of StatefulSet Pods | auto | +| `statefulset.topologySpreadConstraints.maxSkew` | Degree to which Pods may be unevenly distributed | `1` | +| `statefulset.topologySpreadConstraints.topologyKey` | The key of node labels | `topology.kubernetes.io/zone` | +| `statefulset.topologySpreadConstraints.whenUnsatisfiable` | `ScheduleAnyway`/`DoNotSchedule` for unsatisfiable constraints | `ScheduleAnyway` | +| `statefulset.resources` | Resource requests and limits for StatefulSet Pods | `{}` | +| `statefulset.customLivenessProbe` | Custom Liveness probe | `{}` | +| `statefulset.customReadinessProbe` | Custom Rediness probe | `{}` | +| `service.ports.grpc.external.port` | CockroachDB primary serving port in Services | `26257` | +| `service.ports.grpc.external.name` | CockroachDB primary serving port name in Services | `grpc` | +| `service.ports.grpc.internal.port` | CockroachDB inter-communication port in Services | `26257` | +| `service.ports.grpc.internal.name` | CockroachDB inter-communication port name in Services | `grpc-internal` | +| `service.ports.http.port` | CockroachDB HTTP port in Services | `8080` | +| `service.ports.http.name` | CockroachDB HTTP port name in Services | `http` | +| `service.public.type` | Public Service type | `ClusterIP` | +| `service.public.labels` | Additional labels of public Service | `{"app.kubernetes.io/component": "cockroachdb"}` | +| `service.public.annotations` | Additional annotations of public Service | `{}` | +| `service.discovery.labels` | Additional labels of discovery Service | `{"app.kubernetes.io/component": "cockroachdb"}` | +| `service.discovery.annotations` | Additional annotations of discovery Service | `{}` | +| `ingress.enabled` | Enable ingress resource for CockroachDB | `false` | +| `ingress.labels` | Additional labels of Ingress | `{}` | +| `ingress.annotations` | Additional annotations of Ingress | `{}` | +| `ingress.paths` | Paths for the default host | `[/]` | +| `ingress.hosts` | CockroachDB Ingress hostnames | `[]` | +| `ingress.tls[0].hosts` | CockroachDB Ingress tls hostnames | `nil` | +| `ingress.tls[0].secretName` | CockroachDB Ingress tls secret name | `nil` | +| `prometheus.enabled` | Enable automatic monitoring of all instances when Prometheus is running | `true` | +| `serviceMonitor.enabled` | Create [ServiceMonitor](https://github.com/prometheus-operator/prometheus-operator/blob/master/Documentation/design.md#servicemonitor) Resource for scraping metrics using [PrometheusOperator](https://github.com/prometheus-operator/prometheus-operator/blob/master/Documentation/user-guides/getting-started.md#prometheus-operator) | `false` | +| `serviceMonitor.labels` | Additional labels of ServiceMonitor | `{}` | +| `serviceMonitor.annotations` | Additional annotations of ServiceMonitor | `{}` | +| `serviceMonitor.interval` | ServiceMonitor scrape metrics interval | `10s` | +| `serviceMonitor.scrapeTimeout` | ServiceMonitor scrape timeout | `nil` | +| `serviceMonitor.tlsConfig` | Additional TLS configuration of ServiceMonitor | `{}` | +| `serviceMonitor.namespaced` | Limit ServiceMonitor to current namespace | `false` | +| `storage.hostPath` | Absolute path on host to store data | `""` | +| `storage.persistentVolume.enabled` | Whether to use PersistentVolume to store data | `yes` | +| `storage.persistentVolume.size` | PersistentVolume size | `100Gi` | +| `storage.persistentVolume.storageClass` | PersistentVolume class | `""` | +| `storage.persistentVolume.labels` | Additional labels of PersistentVolumeClaim | `{}` | +| `storage.persistentVolume.annotations` | Additional annotations of PersistentVolumeClaim | `{}` | +| `init.labels` | Additional labels of init Job and its Pod | `{"app.kubernetes.io/component": "init"}` | +| `init.jobAnnotations` | Additional annotations of the init Job itself | `{}` | +| `init.annotations` | Additional annotations of the Pod of init Job | `{}` | +| `init.affinity` | [Affinity rules][2] of init Job Pod | `{}` | +| `init.nodeSelector` | Node labels for init Job Pod assignment | `{}` | +| `init.tolerations` | Node taints to tolerate by init Job Pod | `[]` | +| `init.resources` | Resource requests and limits for the `cluster-init` container | `{}` | +| `tls.enabled` | Whether to run securely using TLS certificates | `no` | +| `tls.serviceAccount.create` | Whether to create a new RBAC service account | `yes` | +| `tls.serviceAccount.name` | Name of RBAC service account to use | `""` | +| `tls.copyCerts.image` | Image used in copy certs init container | `busybox` | +| `tls.copyCerts.resources` | Resource requests and limits for the `copy-certs` container | `{}` | +| `tls.certs.provided` | Bring your own certs scenario, i.e certificates are provided | `no` | +| `tls.certs.clientRootSecret` | If certs are provided, secret name for client root cert | `cockroachdb-root` | +| `tls.certs.nodeSecret` | If certs are provided, secret name for node cert | `cockroachdb-node` | +| `tls.certs.tlsSecret` | Own certs are stored in TLS secret | `no` | +| `tls.certs.selfSigner.enabled` | Whether cockroachdb should generate its own self-signed certs | `true` | +| `tls.certs.selfSigner.caProvided` | Bring your own CA scenario. This CA will be used to generate node and client cert | `false` | +| `tls.certs.selfSigner.caSecret` | If CA is provided, secret name for CA cert | `""` | +| `tls.certs.selfSigner.minimumCertDuration` | Minimum cert duration for all the certs, all certs duration will be validated against this duration | `624h` | +| `tls.certs.selfSigner.caCertDuration` | Duration of CA cert in hour | `43824h` | +| `tls.certs.selfSigner.caCertExpiryWindow` | Expiry window of CA cert means a window before actual expiry in which CA cert should be rotated | `648h` | +| `tls.certs.selfSigner.clientCertDuration` | Duration of client cert in hour | `672h | +| `tls.certs.selfSigner.clientCertExpiryWindow` | Expiry window of client cert means a window before actual expiry in which client cert should be rotated | `48h` | +| `tls.certs.selfSigner.nodeCertDuration` | Duration of node cert in hour | `8760h` | +| `tls.certs.selfSigner.nodeCertExpiryWindow` | Expiry window of node cert means a window before actual expiry in which node certs should be rotated | `168h` | +| `tls.certs.selfSigner.rotateCerts` | Whether to rotate the certs generate by cockroachdb | `true` | +| `tls.certs.selfSigner.readinessWait` | Wait time for each cockroachdb replica to become ready once it comes in running state. Only considered when rotateCerts is set to true | `30s` | +| `tls.certs.selfSigner.podUpdateTimeout` | Wait time for each cockroachdb replica to get to running state. Only considered when rotateCerts is set to true | `2m` | +| `tls.certs.certManager` | Provision certificates with cert-manager | `false` | +| `tls.certs.certManagerIssuer.group` | IssuerRef group to use when generating certificates | `cert-manager.io` | +| `tls.certs.certManagerIssuer.kind` | IssuerRef kind to use when generating certificates | `Issuer` | +| `tls.certs.certManagerIssuer.name` | IssuerRef name to use when generating certificates | `cockroachdb` | +| `tls.certs.certManagerIssuer.caCertDuration` | Duration of CA cert in hour | `43824h` | +| `tls.certs.certManagerIssuer.caCertExpiryWindow` | Expiry window of CA cert means a window before actual expiry in which CA cert should be rotated | `648h` | +| `tls.certs.certManagerIssuer.clientCertDuration` | Duration of client cert in hours | `672h` | +| `tls.certs.certManagerIssuer.clientCertExpiryWindow` | Expiry window of client cert means a window before actual expiry in which client cert should be rotated | `48h` | +| `tls.certs.certManagerIssuer.nodeCertDuration` | Duration of node cert in hours | `8760h` | +| `tls.certs.certManagerIssuer.nodeCertExpiryWindow` | Expiry window of node certificates means a window before actual expiry in which node certs should be rotated. | `168h` | +| `tls.selfSigner.image.repository` | Image to use for self signing TLS certificates | `cockroachlabs-helm-charts/cockroach-self-signer-cert`| +| `tls.selfSigner.image.tag` | Image tag to use for self signing TLS certificates | `0.1` | +| `tls.selfSigner.image.pullPolicy` | Self signing TLS certificates container pull policy | `IfNotPresent` | +| `tls.selfSigner.image.credentials` | `registry`, `user` and `pass` credentials to pull private image | `{}` | +| `networkPolicy.enabled` | Enable NetworkPolicy for CockroachDB's Pods | `no` | +| `networkPolicy.ingress.grpc` | Whitelist resources to access gRPC port of CockroachDB's Pods | `[]` | +| `networkPolicy.ingress.http` | Whitelist resources to access gRPC port of CockroachDB's Pods | `[]` | + + +Override the default parameters using the `--set key=value[,key=value]` argument to `helm install`. + +Alternatively, a YAML file that specifies custom values for the parameters can be provided while installing the chart. For example: + +```shell +helm install my-release -f my-values.yaml cockroachdb/cockroachdb +``` + +> **Tip**: You can use the default [values.yaml](values.yaml) + +## Deep dive + +### Connecting to the CockroachDB cluster + +Once you've created the cluster, you can start talking to it by connecting to its `-public` Service. CockroachDB is PostgreSQL wire protocol compatible, so there's a [wide variety of supported clients](https://www.cockroachlabs.com/docs/install-client-drivers.html). As an example, we'll open up a SQL shell using CockroachDB's built-in shell and play around with it a bit, like this (likely needing to replace `my-release-cockroachdb-public` with the name of the `-public` Service that was created with your installed chart): + +```shell +kubectl run cockroach-client --rm -it \ +--image=cockroachdb/cockroach \ +--restart=Never \ +-- sql --insecure --host my-release-cockroachdb-public +``` + +``` +Waiting for pod default/cockroach-client to be running, status is Pending, +pod ready: false +If you don't see a command prompt, try pressing enter. +root@my-release-cockroachdb-public:26257> SHOW DATABASES; ++--------------------+ +| Database | ++--------------------+ +| information_schema | +| pg_catalog | +| system | ++--------------------+ +(3 rows) +root@my-release-cockroachdb-public:26257> CREATE DATABASE bank; +CREATE DATABASE +root@my-release-cockroachdb-public:26257> CREATE TABLE bank.accounts (id INT +PRIMARY KEY, balance DECIMAL); +CREATE TABLE +root@my-release-cockroachdb-public:26257> INSERT INTO bank.accounts VALUES +(1234, 10000.50); +INSERT 1 +root@my-release-cockroachdb-public:26257> SELECT * FROM bank.accounts; ++------+---------+ +| id | balance | ++------+---------+ +| 1234 | 10000.5 | ++------+---------+ +(1 row) +root@my-release-cockroachdb-public:26257> \q +Waiting for pod default/cockroach-client to terminate, status is Running +pod "cockroach-client" deleted +``` + +> If you are running in secure mode, you will have to provide a client certificate to the cluster in order to authenticate, so the above command will not work. See [here](https://github.com/cockroachdb/cockroach/blob/master/cloud/kubernetes/client-secure.yaml) for an example of how to set up an interactive SQL shell against a secure cluster or [here](https://github.com/cockroachdb/cockroach/blob/master/cloud/kubernetes/example-app-secure.yaml) for an example application connecting to a secure cluster. + +### Cluster health + +Because our pod spec includes regular health checks of the CockroachDB processes, simply running `kubectl get pods` and looking at the `STATUS` column is sufficient to determine the health of each instance in the cluster. + +If you want more detailed information about the cluster, the best place to look is the Admin UI. + +### Accessing the Admin UI + +If you want to see information about how the cluster is doing, you can try pulling up the CockroachDB Admin UI by port-forwarding from your local machine to one of the pods (replacing `my-release-cockroachdb-0` with the name of one of your pods: + +```shell +kubectl port-forward my-release-cockroachdb-0 8080 +``` + +You should then be able to access the Admin UI by visiting in your web browser. + +### Failover + +If any CockroachDB member fails, it is restarted or recreated automatically by the Kubernetes infrastructure, and will re-join the cluster automatically when it comes back up. You can test this scenario by killing any of the CockroachDB pods: + +```shell +kubectl delete pod my-release-cockroachdb-1 +``` + +```shell +kubectl get pods -l "app.kubernetes.io/instance=my-release,app.kubernetes.io/component=cockroachdb" +``` + +``` +NAME READY STATUS RESTARTS AGE +my-release-cockroachdb-0 1/1 Running 0 5m +my-release-cockroachdb-2 1/1 Running 0 5m +``` + +After a while: + +```shell +kubectl get pods -l "app.kubernetes.io/instance=my-release,app.kubernetes.io/component=cockroachdb" +``` + +``` +NAME READY STATUS RESTARTS AGE +my-release-cockroachdb-0 1/1 Running 0 5m +my-release-cockroachdb-1 1/1 Running 0 20s +my-release-cockroachdb-2 1/1 Running 0 5m +``` + +You can check the state of re-joining from the new pod's logs: + +```shell +kubectl logs my-release-cockroachdb-1 +``` + +``` +[...] +I161028 19:32:09.754026 1 server/node.go:586 [n1] node connected via gossip and +verified as part of cluster {"35ecbc27-3f67-4e7d-9b8f-27c31aae17d6"} +[...] +cockroachdb-0.my-release-cockroachdb.default.svc.cluster.local:26257 +build: beta-20161027-55-gd2d3c7f @ 2016/10/28 19:27:25 (go1.7.3) +admin: http://0.0.0.0:8080 +sql: +postgresql://root@my-release-cockroachdb-1.my-release-cockroachdb.default.svc.cluster.local:26257?sslmode=disable +logs: cockroach-data/logs +store[0]: path=cockroach-data +status: restarted pre-existing node +clusterID: {35ecbc27-3f67-4e7d-9b8f-27c31aae17d6} +nodeID: 2 +[...] +``` + +### NetworkPolicy + +To enable NetworkPolicy for CockroachDB, 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 `yes`/`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: + +```shell +kubectl annotate namespace default "net.beta.kubernetes.io/network-policy={\"ingress\":{\"isolation\":\"DefaultDeny\"}}" +``` + +For more precise policy, set `networkPolicy.ingress.grpc` and `networkPolicy.ingress.http` rules. This will only allow pods that match the provided rules to connect to CockroachDB. + +### Scaling + +Scaling should be managed via the `helm upgrade` command. After resizing your cluster on your cloud environment (e.g., GKE or EKS), run the following command to add a pod. This assumes you scaled from 3 to 4 nodes: + +```shell +helm upgrade \ +my-release \ +cockroachdb/cockroachdb \ +--set statefulset.replicas=4 \ +--reuse-values +``` + +Note, that if you are running in secure mode (`tls.enabled` is `yes`/`true`) and increase the size of your cluster, you will also have to approve the CSR (certificate-signing request) of each new node (using `kubectl get csr` and `kubectl certificate approve`). + +[1]: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#inter-pod-affinity-and-anti-affinity +[2]: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#node-affinity +[3]: https://cert-manager.io/ +[4]: https://kubernetes.io/docs/concepts/configuration/pod-priority-preemption/#priorityclass +[5]: https://kubernetes.io/docs/concepts/workloads/pods/pod-topology-spread-constraints/ diff --git a/charts/cockroach-labs/cockroachdb/14.0.2/app-readme.md b/charts/cockroach-labs/cockroachdb/14.0.2/app-readme.md new file mode 100644 index 000000000..8fcc1fd6f --- /dev/null +++ b/charts/cockroach-labs/cockroachdb/14.0.2/app-readme.md @@ -0,0 +1,9 @@ +# CockroachDB Chart + +CockroachDB is a Distributed SQL database that runs natively in Kubernetes. It gives you resilient, horizontal scale across multiple clouds with always-on availability and data partitioned by location. + +CockroachDB scales horizontally without reconfiguration or need for a massive architectural overhaul. Simply add a new node to the cluster and CockroachDB takes care of the underlying complexity. + + - Scale by simply adding new nodes to a CockroachDB cluster + - Automate balancing and distribution of ranges, not shards + - Optimize server utilization evenly across all nodes diff --git a/charts/cockroach-labs/cockroachdb/14.0.2/templates/NOTES.txt b/charts/cockroach-labs/cockroachdb/14.0.2/templates/NOTES.txt new file mode 100644 index 000000000..13b421f62 --- /dev/null +++ b/charts/cockroach-labs/cockroachdb/14.0.2/templates/NOTES.txt @@ -0,0 +1,50 @@ +CockroachDB can be accessed via port {{ .Values.service.ports.grpc.external.port }} at the +following DNS name from within your cluster: + +{{ template "cockroachdb.fullname" . }}-public.{{ .Release.Namespace }}.svc.cluster.local + +Because CockroachDB supports the PostgreSQL wire protocol, you can connect to +the cluster using any available PostgreSQL client. + +{{- if not .Values.tls.enabled }} + +For example, you can open up a SQL shell to the cluster by running: + + kubectl run -it --rm cockroach-client \ + --image=cockroachdb/cockroach \ + --restart=Never \ + {{- if .Values.networkPolicy.enabled }} + --labels="{{ template "cockroachdb.fullname" . }}-client=true" \ + {{- end }} + --command -- \ + ./cockroach sql --insecure --host={{ template "cockroachdb.fullname" . }}-public.{{ .Release.Namespace }} + +From there, you can interact with the SQL shell as you would any other SQL +shell, confident that any data you write will be safe and available even if +parts of your cluster fail. +{{- else }} + +Note that because the cluster is running in secure mode, any client application +that you attempt to connect will either need to have a valid client certificate +or a valid username and password. +{{- end }} + +{{- if and (.Values.networkPolicy.enabled) (not (empty .Values.networkPolicy.ingress.grpc)) }} + +Note: Since NetworkPolicy is enabled, the only Pods allowed to connect to this +CockroachDB cluster are: + +1. Having the label: "{{ template "cockroachdb.fullname" . }}-client=true" + +2. Matching the following rules: {{- toYaml .Values.networkPolicy.ingress.grpc | nindent 0 }} +{{- end }} + +Finally, to open up the CockroachDB admin UI, you can port-forward from your +local machine into one of the instances in the cluster: + + kubectl port-forward -n {{ .Release.Namespace }} {{ template "cockroachdb.fullname" . }}-0 {{ index .Values.conf `http-port` | int64 }} + +Then you can access the admin UI at http{{ if .Values.tls.enabled }}s{{ end }}://localhost:{{ index .Values.conf `http-port` | int64 }}/ in your web browser. + +For more information on using CockroachDB, please see the project's docs at: +https://www.cockroachlabs.com/docs/ diff --git a/charts/cockroach-labs/cockroachdb/14.0.2/templates/_helpers.tpl b/charts/cockroach-labs/cockroachdb/14.0.2/templates/_helpers.tpl new file mode 100644 index 000000000..9ef769a70 --- /dev/null +++ b/charts/cockroach-labs/cockroachdb/14.0.2/templates/_helpers.tpl @@ -0,0 +1,291 @@ +{{/* +Expand the name of the chart. +*/}} +{{- define "cockroachdb.name" -}} +{{- default .Chart.Name .Values.nameOverride | trunc 56 | 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 "cockroachdb.fullname" -}} +{{- if .Values.fullnameOverride -}} + {{- .Values.fullnameOverride | trunc 56 | trimSuffix "-" -}} +{{- else -}} + {{- $name := default .Chart.Name .Values.nameOverride -}} + {{- if contains $name .Release.Name -}} + {{- .Release.Name | trunc 56 | trimSuffix "-" -}} + {{- else -}} + {{- printf "%s-%s" .Release.Name $name | trunc 56 | trimSuffix "-" -}} + {{- end -}} +{{- end -}} +{{- end -}} + +{{/* +Create a default fully qualified app name for cluster scope resource. +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 with release namespace appended at the end. +*/}} +{{- define "cockroachdb.clusterfullname" -}} +{{- if .Values.fullnameOverride -}} + {{- printf "%s-%s" .Values.fullnameOverride .Release.Namespace | trunc 56 | trimSuffix "-" -}} +{{- else -}} + {{- $name := default .Chart.Name .Values.nameOverride -}} + {{- if contains $name .Release.Name -}} + {{- printf "%s-%s" .Release.Name .Release.Namespace | trunc 56 | trimSuffix "-" -}} + {{- else -}} + {{- printf "%s-%s-%s" .Release.Name $name .Release.Namespace | trunc 56 | trimSuffix "-" -}} + {{- end -}} +{{- end -}} +{{- end -}} + +{{/* +Create chart name and version as used by the chart label. +*/}} +{{- define "cockroachdb.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 56 | trimSuffix "-" -}} +{{- end -}} + +{{/* +Create the name of the ServiceAccount to use. +*/}} +{{- define "cockroachdb.serviceAccount.name" -}} +{{- if .Values.statefulset.serviceAccount.create -}} + {{- default (include "cockroachdb.fullname" .) .Values.statefulset.serviceAccount.name -}} +{{- else -}} + {{- default "default" .Values.statefulset.serviceAccount.name -}} +{{- end -}} +{{- end -}} + +{{/* +Return the appropriate apiVersion for NetworkPolicy. +*/}} +{{- define "cockroachdb.networkPolicy.apiVersion" -}} +{{- if semverCompare ">=1.4-0, <=1.7-0" .Capabilities.KubeVersion.Version -}} + {{- print "extensions/v1beta1" -}} +{{- else if semverCompare "^1.7-0" .Capabilities.KubeVersion.Version -}} + {{- print "networking.k8s.io/v1" -}} +{{- end -}} +{{- end -}} + +{{/* +Return the appropriate apiVersion for StatefulSets +*/}} +{{- define "cockroachdb.statefulset.apiVersion" -}} +{{- if semverCompare "<1.12-0" .Capabilities.KubeVersion.Version -}} + {{- print "apps/v1beta1" -}} +{{- else -}} + {{- print "apps/v1" -}} +{{- end -}} +{{- end -}} + +{{/* +Return CockroachDB store expression +*/}} +{{- define "cockroachdb.conf.store" -}} +{{- $isInMemory := eq (.Values.conf.store.type | toString) "mem" -}} +{{- $persistentSize := empty .Values.conf.store.size | ternary .Values.storage.persistentVolume.size .Values.conf.store.size -}} + +{{- $store := dict -}} +{{- $_ := set $store "type" ($isInMemory | ternary "type=mem" "") -}} +{{- $_ := set $store "path" ($isInMemory | ternary "" (print "path=" .Values.conf.path)) -}} +{{- $_ := set $store "size" (print "size=" ($isInMemory | ternary .Values.conf.store.size $persistentSize)) -}} +{{- $_ := set $store "attrs" (empty .Values.conf.store.attrs | ternary "" (print "attrs=" .Values.conf.store.attrs)) -}} + +{{ compact (values $store) | join "," }} +{{- end -}} + +{{/* +Define the default values for the certificate selfSigner inputs +*/}} +{{- define "selfcerts.fullname" -}} + {{- printf "%s-%s" (include "cockroachdb.fullname" .) "self-signer" | trunc 56 | trimSuffix "-" -}} +{{- end -}} + +{{- define "rotatecerts.fullname" -}} + {{- printf "%s-%s" (include "cockroachdb.fullname" .) "rotate-self-signer" | trunc 56 | trimSuffix "-" -}} +{{- end -}} + +{{- define "selfcerts.minimumCertDuration" -}} + {{- if .Values.tls.certs.selfSigner.minimumCertDuration -}} + {{- print (.Values.tls.certs.selfSigner.minimumCertDuration | trimSuffix "h") -}} + {{- else }} + {{- $minCertDuration := min (sub (.Values.tls.certs.selfSigner.clientCertDuration | trimSuffix "h" ) (.Values.tls.certs.selfSigner.clientCertExpiryWindow | trimSuffix "h")) (sub (.Values.tls.certs.selfSigner.nodeCertDuration | trimSuffix "h") (.Values.tls.certs.selfSigner.nodeCertExpiryWindow | trimSuffix "h")) -}} + {{- print $minCertDuration -}} + {{- end }} +{{- end -}} + +{{/* +Define the cron schedules for certificate rotate jobs and converting from hours to valid cron string. +We assume that each month has 31 days, hence the cron job may run few days earlier in a year. In a cron schedule, +we can not set a cron of more than a year, hence we try to run the cron in such a way that the cron run comes to +as close possible to the expiry window. However, it is possible that cron may run earlier than the expiry window. +*/}} +{{- define "selfcerts.caRotateSchedule" -}} +{{- $tempHours := sub (.Values.tls.certs.selfSigner.caCertDuration | trimSuffix "h") (.Values.tls.certs.selfSigner.caCertExpiryWindow | trimSuffix "h") -}} +{{- $days := "*" -}} +{{- $months := "*" -}} +{{- $hours := mod $tempHours 24 -}} +{{- if not (eq $hours $tempHours) -}} +{{- $tempDays := div $tempHours 24 -}} +{{- $days = mod $tempDays 31 -}} +{{- if not (eq $days $tempDays) -}} +{{- $days = add $days 1 -}} +{{- $tempMonths := div $tempDays 31 -}} +{{- $months = mod $tempMonths 12 -}} +{{- if not (eq $months $tempMonths) -}} +{{- $months = add $months 1 -}} +{{- end -}} +{{- end -}} +{{- end -}} +{{- if ne (toString $months) "*" -}} +{{- $months = printf "*/%s" (toString $months) -}} +{{- else -}} +{{- if ne (toString $days) "*" -}} +{{- $days = printf "*/%s" (toString $days) -}} +{{- else -}} +{{- if ne $hours 0 -}} +{{- $hours = printf "*/%s" (toString $hours) -}} +{{- end -}} +{{- end -}} +{{- end -}} +{{- printf "0 %s %s %s *" (toString $hours) (toString $days) (toString $months) -}} +{{- end -}} + +{{- define "selfcerts.clientRotateSchedule" -}} +{{- $tempHours := int64 (include "selfcerts.minimumCertDuration" .) -}} +{{- $days := "*" -}} +{{- $months := "*" -}} +{{- $hours := mod $tempHours 24 -}} +{{- if not (eq $hours $tempHours) -}} +{{- $tempDays := div $tempHours 24 -}} +{{- $days = mod $tempDays 31 -}} +{{- if not (eq $days $tempDays) -}} +{{- $days = add $days 1 -}} +{{- $tempMonths := div $tempDays 31 -}} +{{- $months = mod $tempMonths 12 -}} +{{- if not (eq $months $tempMonths) -}} +{{- $months = add $months 1 -}} +{{- end -}} +{{- end -}} +{{- end -}} +{{- if ne (toString $months) "*" -}} +{{- $months = printf "*/%s" (toString $months) -}} +{{- else -}} +{{- if ne (toString $days) "*" -}} +{{- $days = printf "*/%s" (toString $days) -}} +{{- else -}} +{{- if ne $hours 0 -}} +{{- $hours = printf "*/%s" (toString $hours) -}} +{{- end -}} +{{- end -}} +{{- end -}} +{{- printf "0 %s %s %s *" (toString $hours) (toString $days) (toString $months) -}} +{{- end -}} + +{{/* +Define the appropriate validations for the certificate selfSigner inputs +*/}} + +{{/* +Validate that if caProvided is true, then the caSecret must not be empty and secret must be present in the namespace. +*/}} +{{- define "cockroachdb.tls.certs.selfSigner.caProvidedValidation" -}} +{{- if .Values.tls.certs.selfSigner.caProvided -}} +{{- if eq "" .Values.tls.certs.selfSigner.caSecret -}} + {{ fail "CA secret can't be empty if caProvided is set to true" }} +{{- else -}} + {{- if not (lookup "v1" "Secret" .Release.Namespace .Values.tls.certs.selfSigner.caSecret) }} + {{ fail "CA secret is not present in the release namespace" }} + {{- end }} +{{- end -}} +{{- end -}} +{{- end -}} + +{{/* +Validate that if caCertDuration or caCertExpiryWindow must not be empty and caCertExpiryWindow must be greater than +minimumCertDuration. +*/}} +{{- define "cockroachdb.tls.certs.selfSigner.caCertValidation" -}} +{{- if not .Values.tls.certs.selfSigner.caProvided -}} +{{- if or (not .Values.tls.certs.selfSigner.caCertDuration) (not .Values.tls.certs.selfSigner.caCertExpiryWindow) }} + {{ fail "CA cert duration or CA cert expiry window can not be empty" }} +{{- else }} +{{- if gt (int64 (include "selfcerts.minimumCertDuration" .)) (int64 (.Values.tls.certs.selfSigner.caCertExpiryWindow | trimSuffix "h")) -}} + {{ fail "CA cert expiration window should not be less than minimum Cert duration" }} +{{- end -}} +{{- if gt (int64 (include "selfcerts.minimumCertDuration" .)) (sub (.Values.tls.certs.selfSigner.caCertDuration | trimSuffix "h") (.Values.tls.certs.selfSigner.caCertExpiryWindow | trimSuffix "h")) -}} + {{ fail "CA cert Duration minus CA cert expiration window should not be less than minimum Cert duration" }} +{{- end -}} +{{- end -}} +{{- end }} +{{- end -}} + +{{/* +Validate that if clientCertDuration must not be empty and it must be greater than minimumCertDuration. +*/}} +{{- define "cockroachdb.tls.certs.selfSigner.clientCertValidation" -}} +{{- if or (not .Values.tls.certs.selfSigner.clientCertDuration) (not .Values.tls.certs.selfSigner.clientCertExpiryWindow) }} + {{ fail "Client cert duration can not be empty" }} +{{- else }} +{{- if lt (sub (.Values.tls.certs.selfSigner.clientCertDuration | trimSuffix "h") (.Values.tls.certs.selfSigner.clientCertExpiryWindow | trimSuffix "h")) (int64 (include "selfcerts.minimumCertDuration" .)) }} + {{ fail "Client cert duration minus client cert expiry window should not be less than minimum Cert duration" }} +{{- end }} +{{- end }} +{{- end -}} + +{{/* +Validate that nodeCertDuration must not be empty and nodeCertDuration minus nodeCertExpiryWindow must be greater than minimumCertDuration. +*/}} +{{- define "cockroachdb.tls.certs.selfSigner.nodeCertValidation" -}} +{{- if or (not .Values.tls.certs.selfSigner.nodeCertDuration) (not .Values.tls.certs.selfSigner.nodeCertExpiryWindow) }} + {{ fail "Node cert duration can not be empty" }} +{{- else }} +{{- if lt (sub (.Values.tls.certs.selfSigner.nodeCertDuration | trimSuffix "h") (.Values.tls.certs.selfSigner.nodeCertExpiryWindow | trimSuffix "h")) (int64 (include "selfcerts.minimumCertDuration" .))}} + {{ fail "Node cert duration minus node cert expiry window should not be less than minimum Cert duration" }} +{{- end }} +{{- end }} +{{- end -}} + +{{/* +Validate that if user enabled tls, then either self-signed certificates or certificate manager is enabled +*/}} +{{- define "cockroachdb.tlsValidation" -}} +{{- if .Values.tls.enabled -}} +{{- if and .Values.tls.certs.selfSigner.enabled .Values.tls.certs.certManager -}} + {{ fail "Can not enable the self signed certificates and certificate manager at the same time" }} +{{- end -}} +{{- if and (not .Values.tls.certs.selfSigner.enabled) (not .Values.tls.certs.certManager) -}} + {{- if not .Values.tls.certs.provided -}} + {{ fail "You have to enable either self signed certificates or certificate manager, if you have enabled tls" }} + {{- end -}} +{{- end -}} +{{- end -}} +{{- end -}} + + +{{- define "cockroachdb.tls.certs.selfSigner.validation" -}} +{{ include "cockroachdb.tls.certs.selfSigner.caProvidedValidation" . }} +{{ include "cockroachdb.tls.certs.selfSigner.caCertValidation" . }} +{{ include "cockroachdb.tls.certs.selfSigner.clientCertValidation" . }} +{{ include "cockroachdb.tls.certs.selfSigner.nodeCertValidation" . }} +{{- end -}} + +{{- define "cockroachdb.securityContext.versionValidation" }} +{{- /* Allow using `securityContext` for custom images. */}} +{{- if ne "cockroachdb/cockroach" .Values.image.repository -}} + {{ print true }} +{{- else -}} +{{- if semverCompare ">=22.1.2" .Values.image.tag -}} + {{ print true }} +{{- else -}} +{{- if semverCompare ">=21.2.13, <22.1.0" .Values.image.tag -}} + {{ print true }} +{{- else -}} + {{ print false }} +{{- end }} +{{- end }} +{{- end }} +{{- end }} diff --git a/charts/cockroach-labs/cockroachdb/14.0.2/templates/backendconfig.yaml b/charts/cockroach-labs/cockroachdb/14.0.2/templates/backendconfig.yaml new file mode 100644 index 000000000..2edc88619 --- /dev/null +++ b/charts/cockroach-labs/cockroachdb/14.0.2/templates/backendconfig.yaml @@ -0,0 +1,21 @@ +{{- if .Values.iap.enabled }} +apiVersion: cloud.google.com/v1beta1 +kind: BackendConfig +metadata: + name: {{ template "cockroachdb.fullname" . }} + namespace: {{ .Release.Namespace | quote }} + labels: + helm.sh/chart: {{ template "cockroachdb.chart" . }} + app.kubernetes.io/name: {{ template "cockroachdb.name" . }} + app.kubernetes.io/instance: {{ .Release.Name | quote }} + app.kubernetes.io/managed-by: {{ .Release.Service | quote }} + {{- with .Values.labels }} + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + iap: + enabled: true + oauthclientCredentials: + secretName: {{ template "cockroachdb.fullname" . }}.iap + timeoutSec: 120 +{{- end }} \ No newline at end of file diff --git a/charts/cockroach-labs/cockroachdb/14.0.2/templates/certificate.ca.yaml b/charts/cockroach-labs/cockroachdb/14.0.2/templates/certificate.ca.yaml new file mode 100644 index 000000000..4043fafb0 --- /dev/null +++ b/charts/cockroach-labs/cockroachdb/14.0.2/templates/certificate.ca.yaml @@ -0,0 +1,33 @@ +{{- if and .Values.tls.enabled .Values.tls.certs.certManager }} + {{- if .Values.tls.certs.certManagerIssuer.isSelfSignedIssuer }} +apiVersion: cert-manager.io/v1 +kind: Certificate +metadata: + name: {{ template "cockroachdb.fullname" . }}-ca-cert + namespace: {{ .Release.Namespace | quote }} + labels: + helm.sh/chart: {{ template "cockroachdb.chart" . }} + app.kubernetes.io/name: {{ template "cockroachdb.name" . }} + app.kubernetes.io/instance: {{ .Release.Name | quote }} + app.kubernetes.io/managed-by: {{ .Release.Service | quote }} + {{- with .Values.labels }} + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + duration: {{ .Values.tls.certs.certManagerIssuer.caCertDuration }} + renewBefore: {{ .Values.tls.certs.certManagerIssuer.caCertExpiryWindow }} + isCA: true + secretName: {{ .Values.tls.certs.caSecret }} + privateKey: + algorithm: ECDSA + size: 256 + commonName: root + subject: + organizations: + - Cockroach + issuerRef: + name: {{ .Values.tls.certs.certManagerIssuer.name }} + kind: {{ .Values.tls.certs.certManagerIssuer.kind }} + group: {{ .Values.tls.certs.certManagerIssuer.group }} + {{- end }} +{{- end }} diff --git a/charts/cockroach-labs/cockroachdb/14.0.2/templates/certificate.client.yaml b/charts/cockroach-labs/cockroachdb/14.0.2/templates/certificate.client.yaml new file mode 100644 index 000000000..dd0272f3e --- /dev/null +++ b/charts/cockroach-labs/cockroachdb/14.0.2/templates/certificate.client.yaml @@ -0,0 +1,40 @@ +{{- if and .Values.tls.enabled .Values.tls.certs.certManager }} +apiVersion: cert-manager.io/v1 +kind: Certificate +metadata: + name: {{ template "cockroachdb.fullname" . }}-root-client + namespace: {{ .Release.Namespace | quote }} + labels: + helm.sh/chart: {{ template "cockroachdb.chart" . }} + app.kubernetes.io/name: {{ template "cockroachdb.name" . }} + app.kubernetes.io/instance: {{ .Release.Name | quote }} + app.kubernetes.io/managed-by: {{ .Release.Service | quote }} + {{- with .Values.labels }} + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + duration: {{ .Values.tls.certs.certManagerIssuer.clientCertDuration }} + renewBefore: {{ .Values.tls.certs.certManagerIssuer.clientCertExpiryWindow }} + usages: + - digital signature + - key encipherment + - client auth + privateKey: + algorithm: RSA + size: 2048 + commonName: root + subject: + organizations: + - Cockroach + secretName: {{ .Values.tls.certs.clientRootSecret }} + issuerRef: + {{- if .Values.tls.certs.certManagerIssuer.isSelfSignedIssuer }} + name: {{ template "cockroachdb.fullname" . }}-ca-issuer + kind: Issuer + group: cert-manager.io + {{- else }} + name: {{ .Values.tls.certs.certManagerIssuer.name }} + kind: {{ .Values.tls.certs.certManagerIssuer.kind }} + group: {{ .Values.tls.certs.certManagerIssuer.group }} + {{- end }} +{{- end }} diff --git a/charts/cockroach-labs/cockroachdb/14.0.2/templates/certificate.issuer.yaml b/charts/cockroach-labs/cockroachdb/14.0.2/templates/certificate.issuer.yaml new file mode 100644 index 000000000..5cf579ff9 --- /dev/null +++ b/charts/cockroach-labs/cockroachdb/14.0.2/templates/certificate.issuer.yaml @@ -0,0 +1,20 @@ +{{- if and .Values.tls.enabled .Values.tls.certs.certManager }} + {{- if .Values.tls.certs.certManagerIssuer.isSelfSignedIssuer }} +apiVersion: cert-manager.io/v1 +kind: Issuer +metadata: + name: {{ template "cockroachdb.fullname" . }}-ca-issuer + namespace: {{ .Release.Namespace | quote }} + labels: + helm.sh/chart: {{ template "cockroachdb.chart" . }} + app.kubernetes.io/name: {{ template "cockroachdb.name" . }} + app.kubernetes.io/instance: {{ .Release.Name | quote }} + app.kubernetes.io/managed-by: {{ .Release.Service | quote }} + {{- with .Values.labels }} + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + ca: + secretName: {{ .Values.tls.certs.caSecret }} + {{- end }} +{{- end }} diff --git a/charts/cockroach-labs/cockroachdb/14.0.2/templates/certificate.node.yaml b/charts/cockroach-labs/cockroachdb/14.0.2/templates/certificate.node.yaml new file mode 100644 index 000000000..05e909d0b --- /dev/null +++ b/charts/cockroach-labs/cockroachdb/14.0.2/templates/certificate.node.yaml @@ -0,0 +1,50 @@ +{{- if and .Values.tls.enabled .Values.tls.certs.certManager }} +apiVersion: cert-manager.io/v1 +kind: Certificate +metadata: + name: {{ template "cockroachdb.fullname" . }}-node + namespace: {{ .Release.Namespace | quote }} + labels: + helm.sh/chart: {{ template "cockroachdb.chart" . }} + app.kubernetes.io/name: {{ template "cockroachdb.name" . }} + app.kubernetes.io/instance: {{ .Release.Name | quote }} + app.kubernetes.io/managed-by: {{ .Release.Service | quote }} + {{- with .Values.labels }} + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + duration: {{ .Values.tls.certs.certManagerIssuer.nodeCertDuration }} + renewBefore: {{ .Values.tls.certs.certManagerIssuer.nodeCertExpiryWindow }} + usages: + - digital signature + - key encipherment + - server auth + - client auth + privateKey: + algorithm: RSA + size: 2048 + commonName: node + subject: + organizations: + - Cockroach + dnsNames: + - "localhost" + - "127.0.0.1" + - {{ printf "%s-public" (include "cockroachdb.fullname" .) | quote }} + - {{ printf "%s-public.%s" (include "cockroachdb.fullname" .) .Release.Namespace | quote }} + - {{ printf "%s-public.%s.svc.%s" (include "cockroachdb.fullname" .) .Release.Namespace .Values.clusterDomain | quote }} + - {{ printf "*.%s" (include "cockroachdb.fullname" .) | quote }} + - {{ printf "*.%s.%s" (include "cockroachdb.fullname" .) .Release.Namespace | quote }} + - {{ printf "*.%s.%s.svc.%s" (include "cockroachdb.fullname" .) .Release.Namespace .Values.clusterDomain | quote }} + secretName: {{ .Values.tls.certs.nodeSecret }} + issuerRef: + {{- if .Values.tls.certs.certManagerIssuer.isSelfSignedIssuer }} + name: {{ template "cockroachdb.fullname" . }}-ca-issuer + kind: Issuer + group: cert-manager.io + {{- else }} + name: {{ .Values.tls.certs.certManagerIssuer.name }} + kind: {{ .Values.tls.certs.certManagerIssuer.kind }} + group: {{ .Values.tls.certs.certManagerIssuer.group }} + {{- end }} +{{- end }} diff --git a/charts/cockroach-labs/cockroachdb/14.0.2/templates/clusterrole.yaml b/charts/cockroach-labs/cockroachdb/14.0.2/templates/clusterrole.yaml new file mode 100644 index 000000000..6b8a3dc5f --- /dev/null +++ b/charts/cockroach-labs/cockroachdb/14.0.2/templates/clusterrole.yaml @@ -0,0 +1,19 @@ +{{- if and .Values.tls.enabled (not .Values.tls.certs.provided) (not .Values.tls.certs.certManager) }} +kind: ClusterRole +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: {{ template "cockroachdb.clusterfullname" . }} + namespace: {{ .Release.Namespace | quote }} + labels: + helm.sh/chart: {{ template "cockroachdb.chart" . }} + app.kubernetes.io/name: {{ template "cockroachdb.name" . }} + app.kubernetes.io/instance: {{ .Release.Name | quote }} + app.kubernetes.io/managed-by: {{ .Release.Service | quote }} + {{- with .Values.labels }} + {{- toYaml . | nindent 4 }} + {{- end }} +rules: + - apiGroups: ["certificates.k8s.io"] + resources: ["certificatesigningrequests"] + verbs: ["create", "get", "watch"] +{{- end }} \ No newline at end of file diff --git a/charts/cockroach-labs/cockroachdb/14.0.2/templates/clusterrolebinding.yaml b/charts/cockroach-labs/cockroachdb/14.0.2/templates/clusterrolebinding.yaml new file mode 100644 index 000000000..3c18694ef --- /dev/null +++ b/charts/cockroach-labs/cockroachdb/14.0.2/templates/clusterrolebinding.yaml @@ -0,0 +1,23 @@ +{{- if and .Values.tls.enabled (not .Values.tls.certs.provided) (not .Values.tls.certs.certManager) }} +kind: ClusterRoleBinding +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: {{ template "cockroachdb.clusterfullname" . }} + namespace: {{ .Release.Namespace | quote }} + labels: + helm.sh/chart: {{ template "cockroachdb.chart" . }} + app.kubernetes.io/name: {{ template "cockroachdb.name" . }} + app.kubernetes.io/instance: {{ .Release.Name | quote }} + app.kubernetes.io/managed-by: {{ .Release.Service | quote }} + {{- with .Values.labels }} + {{- toYaml . | nindent 4 }} + {{- end }} +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: {{ template "cockroachdb.clusterfullname" . }} +subjects: + - kind: ServiceAccount + name: {{ template "cockroachdb.serviceAccount.name" . }} + namespace: {{ .Release.Namespace | quote }} +{{- end }} \ No newline at end of file diff --git a/charts/cockroach-labs/cockroachdb/14.0.2/templates/cronjob-ca-certSelfSigner.yaml b/charts/cockroach-labs/cockroachdb/14.0.2/templates/cronjob-ca-certSelfSigner.yaml new file mode 100644 index 000000000..4cd53900c --- /dev/null +++ b/charts/cockroach-labs/cockroachdb/14.0.2/templates/cronjob-ca-certSelfSigner.yaml @@ -0,0 +1,62 @@ +{{- if and .Values.tls.enabled (and .Values.tls.certs.selfSigner.enabled (not .Values.tls.certs.selfSigner.caProvided)) }} + {{- if .Values.tls.certs.selfSigner.rotateCerts }} + {{- if .Capabilities.APIVersions.Has "batch/v1/CronJob" }} +apiVersion: batch/v1 + {{- else }} +apiVersion: batch/v1beta1 + {{- end }} +kind: CronJob +metadata: + name: {{ template "rotatecerts.fullname" . }} + namespace: {{ .Release.Namespace | quote }} + labels: + helm.sh/chart: {{ template "cockroachdb.chart" . }} + app.kubernetes.io/name: {{ template "cockroachdb.name" . }} + app.kubernetes.io/instance: {{ .Release.Name | quote }} + app.kubernetes.io/managed-by: {{ .Release.Service | quote }} +spec: + schedule: {{ template "selfcerts.caRotateSchedule" . }} + jobTemplate: + spec: + backoffLimit: 1 + template: + metadata: + {{- with .Values.tls.selfSigner.labels }} + labels: {{- toYaml . | nindent 12 }} + {{- end }} + {{- with .Values.tls.selfSigner.annotations }} + annotations: {{- toYaml . | nindent 12 }} + {{- end }} + spec: + restartPolicy: Never + {{- with .Values.tls.selfSigner.affinity }} + affinity: {{- toYaml . | nindent 12 }} + {{- end }} + {{- with .Values.tls.selfSigner.nodeSelector }} + nodeSelector: {{- toYaml . | nindent 12 }} + {{- end }} + {{- with .Values.tls.selfSigner.tolerations }} + tolerations: {{- toYaml . | nindent 12 }} + {{- end }} + containers: + - name: cert-rotate-job + image: "{{ .Values.tls.selfSigner.image.registry }}/{{ .Values.tls.selfSigner.image.repository }}:{{ .Values.tls.selfSigner.image.tag }}" + imagePullPolicy: "{{ .Values.tls.selfSigner.image.pullPolicy }}" + args: + - rotate + - --ca + - --ca-duration={{ .Values.tls.certs.selfSigner.caCertDuration }} + - --ca-expiry={{ .Values.tls.certs.selfSigner.caCertExpiryWindow }} + - --ca-cron={{ template "selfcerts.caRotateSchedule" . }} + - --readiness-wait={{ .Values.tls.certs.selfSigner.readinessWait }} + - --pod-update-timeout={{ .Values.tls.certs.selfSigner.podUpdateTimeout }} + env: + - name: STATEFULSET_NAME + value: {{ template "cockroachdb.fullname" . }} + - name: NAMESPACE + value: {{ .Release.Namespace }} + - name: CLUSTER_DOMAIN + value: {{ .Values.clusterDomain}} + serviceAccountName: {{ template "rotatecerts.fullname" . }} + {{- end }} +{{- end }} diff --git a/charts/cockroach-labs/cockroachdb/14.0.2/templates/cronjob-client-node-certSelfSigner.yaml b/charts/cockroach-labs/cockroachdb/14.0.2/templates/cronjob-client-node-certSelfSigner.yaml new file mode 100644 index 000000000..d500cbeb6 --- /dev/null +++ b/charts/cockroach-labs/cockroachdb/14.0.2/templates/cronjob-client-node-certSelfSigner.yaml @@ -0,0 +1,69 @@ +{{- if and .Values.tls.certs.selfSigner.enabled .Values.tls.certs.selfSigner.rotateCerts }} + {{- if .Capabilities.APIVersions.Has "batch/v1/CronJob" }} +apiVersion: batch/v1 + {{- else }} +apiVersion: batch/v1beta1 + {{- end }} +kind: CronJob +metadata: + name: {{ template "rotatecerts.fullname" . }}-client + namespace: {{ .Release.Namespace | quote }} + labels: + helm.sh/chart: {{ template "cockroachdb.chart" . }} + app.kubernetes.io/name: {{ template "cockroachdb.name" . }} + app.kubernetes.io/instance: {{ .Release.Name | quote }} + app.kubernetes.io/managed-by: {{ .Release.Service | quote }} +spec: + schedule: {{ template "selfcerts.clientRotateSchedule" . }} + jobTemplate: + spec: + backoffLimit: 1 + template: + metadata: + {{- with .Values.tls.selfSigner.labels }} + labels: {{- toYaml . | nindent 12 }} + {{- end }} + {{- with .Values.tls.selfSigner.annotations }} + annotations: {{- toYaml . | nindent 12 }} + {{- end }} + spec: + restartPolicy: Never + {{- with .Values.tls.selfSigner.affinity }} + affinity: {{- toYaml . | nindent 12 }} + {{- end }} + {{- with .Values.tls.selfSigner.nodeSelector }} + nodeSelector: {{- toYaml . | nindent 12 }} + {{- end }} + {{- with .Values.tls.selfSigner.tolerations }} + tolerations: {{- toYaml . | nindent 12 }} + {{- end }} + containers: + - name: cert-rotate-job + image: "{{ .Values.tls.selfSigner.image.registry }}/{{ .Values.tls.selfSigner.image.repository }}:{{ .Values.tls.selfSigner.image.tag }}" + imagePullPolicy: "{{ .Values.tls.selfSigner.image.pullPolicy }}" + args: + - rotate + {{- if .Values.tls.certs.selfSigner.caProvided }} + - --ca-secret={{ .Values.tls.certs.selfSigner.caSecret }} + {{- else }} + - --ca-duration={{ .Values.tls.certs.selfSigner.caCertDuration }} + - --ca-expiry={{ .Values.tls.certs.selfSigner.caCertExpiryWindow }} + {{- end }} + - --client + - --client-duration={{ .Values.tls.certs.selfSigner.clientCertDuration }} + - --client-expiry={{ .Values.tls.certs.selfSigner.clientCertExpiryWindow }} + - --node + - --node-duration={{ .Values.tls.certs.selfSigner.nodeCertDuration }} + - --node-expiry={{ .Values.tls.certs.selfSigner.nodeCertExpiryWindow }} + - --node-client-cron={{ template "selfcerts.clientRotateSchedule" . }} + - --readiness-wait={{ .Values.tls.certs.selfSigner.readinessWait }} + - --pod-update-timeout={{ .Values.tls.certs.selfSigner.podUpdateTimeout }} + env: + - name: STATEFULSET_NAME + value: {{ template "cockroachdb.fullname" . }} + - name: NAMESPACE + value: {{ .Release.Namespace }} + - name: CLUSTER_DOMAIN + value: {{ .Values.clusterDomain}} + serviceAccountName: {{ template "rotatecerts.fullname" . }} + {{- end}} diff --git a/charts/cockroach-labs/cockroachdb/14.0.2/templates/ingress.yaml b/charts/cockroach-labs/cockroachdb/14.0.2/templates/ingress.yaml new file mode 100644 index 000000000..2fa6373c8 --- /dev/null +++ b/charts/cockroach-labs/cockroachdb/14.0.2/templates/ingress.yaml @@ -0,0 +1,90 @@ +{{- if .Values.ingress.enabled -}} +{{- $paths := .Values.ingress.paths -}} +{{- $ports := .Values.service.ports -}} +{{- $fullName := include "cockroachdb.fullname" . -}} +{{- if $.Capabilities.APIVersions.Has "networking.k8s.io/v1/Ingress" }} +apiVersion: networking.k8s.io/v1 +{{- else if $.Capabilities.APIVersions.Has "networking.k8s.io/v1beta1/Ingress" }} +apiVersion: networking.k8s.io/v1beta1 +{{- else -}} +apiVersion: extensions/v1beta1 +{{- end }} +kind: Ingress +metadata: +{{- if or .Values.ingress.annotations .Values.iap.enabled }} + annotations: + {{- range $key, $value := .Values.ingress.annotations }} + {{ $key }}: {{ $value | quote }} + {{- end }} + {{- if .Values.iap.enabled }} + kubernetes.io/ingress.class: "gce" + kubernetes.io/ingress.allow-http: "false" + {{- end }} +{{- end }} + name: {{ $fullName }}-ingress + namespace: {{ .Release.Namespace }} + labels: + helm.sh/chart: {{ template "cockroachdb.chart" . }} + app.kubernetes.io/name: {{ template "cockroachdb.name" . }} + app.kubernetes.io/instance: {{ $.Release.Name | quote }} + app.kubernetes.io/managed-by: {{ $.Release.Service | quote }} +{{- if .Values.ingress.labels }} +{{- toYaml .Values.ingress.labels | nindent 4 }} +{{- end }} +spec: + rules: + {{- if .Values.ingress.hosts }} + {{- range $host := .Values.ingress.hosts }} + - host: {{ $host }} + http: + paths: + {{- range $path := $paths }} + - path: {{ $path | quote }} + {{- if $.Capabilities.APIVersions.Has "networking.k8s.io/v1/Ingress" }} + {{- if $.Values.iap.enabled }} + pathType: ImplementationSpecific + {{- else }} + pathType: Prefix + {{- end }} + {{- end }} + backend: + {{- if $.Capabilities.APIVersions.Has "networking.k8s.io/v1/Ingress" }} + service: + name: {{ $fullName }}-public + port: + name: {{ $ports.http.name | quote }} + {{- else }} + serviceName: {{ $fullName }}-public + servicePort: {{ $ports.http.name | quote }} + {{- end }} + {{- end }} + {{- end }} + {{- else }} + - http: + paths: + {{- range $path := $paths }} + - path: {{ $path | quote }} + {{- if $.Capabilities.APIVersions.Has "networking.k8s.io/v1/Ingress" }} + {{- if $.Values.iap.enabled }} + pathType: ImplementationSpecific + {{- else }} + pathType: Prefix + {{- end }} + {{- end }} + backend: + {{- if $.Capabilities.APIVersions.Has "networking.k8s.io/v1/Ingress" }} + service: + name: {{ $fullName }}-public + port: + name: {{ $ports.http.name | quote }} + {{- else }} + serviceName: {{ $fullName }}-public + servicePort: {{ $ports.http.name | quote }} + {{- end }} + {{- end }} + {{- end }} + {{- if .Values.ingress.tls }} + tls: +{{- toYaml .Values.ingress.tls | nindent 4 }} + {{- end }} +{{- end }} diff --git a/charts/cockroach-labs/cockroachdb/14.0.2/templates/job-certSelfSigner.yaml b/charts/cockroach-labs/cockroachdb/14.0.2/templates/job-certSelfSigner.yaml new file mode 100644 index 000000000..54ed2cad3 --- /dev/null +++ b/charts/cockroach-labs/cockroachdb/14.0.2/templates/job-certSelfSigner.yaml @@ -0,0 +1,83 @@ +{{- if and .Values.tls.enabled .Values.tls.certs.selfSigner.enabled }} +apiVersion: batch/v1 +kind: Job +metadata: + name: {{ template "selfcerts.fullname" . }} + namespace: {{ .Release.Namespace | quote }} + annotations: + # This is what defines this resource as a hook. Without this line, the + # job is considered part of the release. + "helm.sh/hook": pre-install,pre-upgrade + "helm.sh/hook-weight": "4" + "helm.sh/hook-delete-policy": hook-succeeded,hook-failed + labels: + helm.sh/chart: {{ template "cockroachdb.chart" . }} + app.kubernetes.io/name: {{ template "cockroachdb.name" . }} + app.kubernetes.io/instance: {{ .Release.Name | quote }} + app.kubernetes.io/managed-by: {{ .Release.Service | quote }} +spec: + template: + metadata: + name: {{ template "selfcerts.fullname" . }} + labels: + helm.sh/chart: {{ template "cockroachdb.chart" . }} + app.kubernetes.io/name: {{ template "cockroachdb.name" . }} + app.kubernetes.io/instance: {{ .Release.Name | quote }} + app.kubernetes.io/managed-by: {{ .Release.Service | quote }} + {{- with .Values.tls.selfSigner.labels }} + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.tls.selfSigner.annotations }} + annotations: {{- toYaml . | nindent 8 }} + {{- end }} + spec: + {{- if and .Values.tls.certs.selfSigner.securityContext.enabled }} + securityContext: + seccompProfile: + type: "RuntimeDefault" + runAsGroup: 1000 + runAsUser: 1000 + fsGroup: 1000 + runAsNonRoot: true + {{- end }} + restartPolicy: Never + {{- with .Values.tls.selfSigner.affinity }} + affinity: {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.tls.selfSigner.nodeSelector }} + nodeSelector: {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.tls.selfSigner.tolerations }} + tolerations: {{- toYaml . | nindent 8 }} + {{- end }} + containers: + - name: cert-generate-job + image: "{{ .Values.tls.selfSigner.image.registry }}/{{ .Values.tls.selfSigner.image.repository }}:{{ .Values.tls.selfSigner.image.tag }}" + imagePullPolicy: "{{ .Values.tls.selfSigner.image.pullPolicy }}" + args: + - generate + {{- if .Values.tls.certs.selfSigner.caProvided }} + - --ca-secret={{ .Values.tls.certs.selfSigner.caSecret }} + {{- else }} + - --ca-duration={{ .Values.tls.certs.selfSigner.caCertDuration }} + - --ca-expiry={{ .Values.tls.certs.selfSigner.caCertExpiryWindow }} + {{- end }} + - --client-duration={{ .Values.tls.certs.selfSigner.clientCertDuration }} + - --client-expiry={{ .Values.tls.certs.selfSigner.clientCertExpiryWindow }} + - --node-duration={{ .Values.tls.certs.selfSigner.nodeCertDuration }} + - --node-expiry={{ .Values.tls.certs.selfSigner.nodeCertExpiryWindow }} + env: + - name: STATEFULSET_NAME + value: {{ template "cockroachdb.fullname" . }} + - name: NAMESPACE + value: {{ .Release.Namespace | quote }} + - name: CLUSTER_DOMAIN + value: {{ .Values.clusterDomain}} + {{- if and .Values.tls.certs.selfSigner.securityContext.enabled }} + securityContext: + allowPrivilegeEscalation: false + capabilities: + drop: ["ALL"] + {{- end }} + serviceAccountName: {{ template "selfcerts.fullname" . }} +{{- end}} diff --git a/charts/cockroach-labs/cockroachdb/14.0.2/templates/job-cleaner.yaml b/charts/cockroach-labs/cockroachdb/14.0.2/templates/job-cleaner.yaml new file mode 100644 index 000000000..1503ac459 --- /dev/null +++ b/charts/cockroach-labs/cockroachdb/14.0.2/templates/job-cleaner.yaml @@ -0,0 +1,70 @@ +{{- if and .Values.tls.enabled .Values.tls.certs.selfSigner.enabled }} +apiVersion: batch/v1 +kind: Job +metadata: + name: {{ template "selfcerts.fullname" . }}-cleaner + namespace: {{ .Release.Namespace | quote }} + annotations: + # This is what defines this resource as a hook. Without this line, the + # job is considered part of the release. + "helm.sh/hook": pre-delete + "helm.sh/hook-delete-policy": hook-succeeded,hook-failed + labels: + helm.sh/chart: {{ template "cockroachdb.chart" . }} + app.kubernetes.io/name: {{ template "cockroachdb.name" . }} + app.kubernetes.io/instance: {{ .Release.Name | quote }} + app.kubernetes.io/managed-by: {{ .Release.Service | quote }} +spec: + backoffLimit: 1 + template: + metadata: + name: {{ template "selfcerts.fullname" . }}-cleaner + labels: + helm.sh/chart: {{ template "cockroachdb.chart" . }} + app.kubernetes.io/name: {{ template "cockroachdb.name" . }} + app.kubernetes.io/instance: {{ .Release.Name | quote }} + app.kubernetes.io/managed-by: {{ .Release.Service | quote }} + {{- with .Values.tls.selfSigner.labels }} + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.tls.selfSigner.annotations }} + annotations: {{- toYaml . | nindent 8 }} + {{- end }} + spec: + {{- if and .Values.tls.certs.selfSigner.securityContext.enabled }} + securityContext: + seccompProfile: + type: "RuntimeDefault" + runAsGroup: 1000 + runAsUser: 1000 + fsGroup: 1000 + runAsNonRoot: true + {{- end }} + restartPolicy: Never + {{- with .Values.tls.selfSigner.affinity }} + affinity: {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.tls.selfSigner.nodeSelector }} + nodeSelector: {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.tls.selfSigner.tolerations }} + tolerations: {{- toYaml . | nindent 8 }} + {{- end }} + containers: + - name: cleaner + image: "{{ .Values.tls.selfSigner.image.registry }}/{{ .Values.tls.selfSigner.image.repository }}:{{ .Values.tls.selfSigner.image.tag }}" + imagePullPolicy: "{{ .Values.tls.selfSigner.image.pullPolicy }}" + args: + - cleanup + - --namespace={{ .Release.Namespace }} + env: + - name: STATEFULSET_NAME + value: {{ template "cockroachdb.fullname" . }} + {{- if and .Values.tls.certs.selfSigner.securityContext.enabled }} + securityContext: + allowPrivilegeEscalation: false + capabilities: + drop: ["ALL"] + {{- end }} + serviceAccountName: {{ template "rotatecerts.fullname" . }} +{{- end}} diff --git a/charts/cockroach-labs/cockroachdb/14.0.2/templates/job.init.yaml b/charts/cockroach-labs/cockroachdb/14.0.2/templates/job.init.yaml new file mode 100644 index 000000000..e6ec412ab --- /dev/null +++ b/charts/cockroach-labs/cockroachdb/14.0.2/templates/job.init.yaml @@ -0,0 +1,303 @@ +{{ $isClusterInitEnabled := and (eq (len .Values.conf.join) 0) (not (index .Values.conf `single-node`)) }} +{{ $isDatabaseProvisioningEnabled := .Values.init.provisioning.enabled }} +{{- if or $isClusterInitEnabled $isDatabaseProvisioningEnabled }} + {{ template "cockroachdb.tlsValidation" . }} +kind: Job +apiVersion: batch/v1 +metadata: + name: {{ template "cockroachdb.fullname" . }}-init + namespace: {{ .Release.Namespace | quote }} + labels: + helm.sh/chart: {{ template "cockroachdb.chart" . }} + app.kubernetes.io/name: {{ template "cockroachdb.name" . }} + app.kubernetes.io/instance: {{ .Release.Name | quote }} + app.kubernetes.io/managed-by: {{ .Release.Service | quote }} + {{- with .Values.init.labels }} + {{- toYaml . | nindent 4 }} + {{- end }} + {{- with .Values.labels }} + {{- toYaml . | nindent 4 }} + {{- end }} + annotations: + helm.sh/hook: post-install,post-upgrade + helm.sh/hook-delete-policy: before-hook-creation + {{- with .Values.init.jobAnnotations }} + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + template: + metadata: + labels: + app.kubernetes.io/name: {{ template "cockroachdb.name" . }} + app.kubernetes.io/instance: {{ .Release.Name | quote }} + {{- with .Values.init.labels }} + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.init.annotations }} + annotations: {{- toYaml . | nindent 8 }} + {{- end }} + spec: + {{- if eq (include "cockroachdb.securityContext.versionValidation" .) "true" }} + {{- if and .Values.init.securityContext.enabled }} + securityContext: + seccompProfile: + type: "RuntimeDefault" + runAsGroup: 1000 + runAsUser: 1000 + fsGroup: 1000 + runAsNonRoot: true + {{- end }} + {{- end }} + restartPolicy: OnFailure + terminationGracePeriodSeconds: 300 + {{- if or .Values.image.credentials (and .Values.tls.enabled .Values.tls.selfSigner.image.credentials (not .Values.tls.certs.provided) (not .Values.tls.certs.certManager)) }} + imagePullSecrets: + {{- if .Values.image.credentials }} + - name: {{ template "cockroachdb.fullname" . }}.db.registry + {{- end }} + {{- if and .Values.tls.enabled .Values.tls.selfSigner.image.credentials (not .Values.tls.certs.provided) (not .Values.tls.certs.certManager) }} + - name: {{ template "cockroachdb.fullname" . }}.self-signed-certs.registry + {{- end }} + {{- end }} + serviceAccountName: {{ template "cockroachdb.serviceAccount.name" . }} + {{- if .Values.tls.enabled }} + initContainers: + - name: copy-certs + image: {{ .Values.tls.copyCerts.image | quote }} + imagePullPolicy: {{ .Values.tls.selfSigner.image.pullPolicy | quote }} + command: + - /bin/sh + - -c + - "cp -f /certs/* /cockroach-certs/; chmod 0400 /cockroach-certs/*.key" + env: + - name: POD_NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace + {{- if and .Values.init.securityContext.enabled }} + securityContext: + allowPrivilegeEscalation: false + capabilities: + drop: ["ALL"] + {{- end }} + volumeMounts: + - name: client-certs + mountPath: /cockroach-certs/ + - name: certs-secret + mountPath: /certs/ + {{- with .Values.tls.copyCerts.resources }} + resources: {{- toYaml . | nindent 12 }} + {{- end }} + {{- end }} + {{- with .Values.init.affinity }} + affinity: {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.init.nodeSelector }} + nodeSelector: {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.init.tolerations }} + tolerations: {{- toYaml . | nindent 8 }} + {{- end }} + containers: + - name: cluster-init + image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}" + imagePullPolicy: {{ .Values.image.pullPolicy | quote }} + # Run the command in an `while true` loop because this Job is bound + # to come up before the CockroachDB Pods (due to the time needed to + # get PersistentVolumes attached to Nodes), and sleeping 5 seconds + # between attempts is much better than letting the Pod fail when + # the init command does and waiting out Kubernetes' non-configurable + # exponential back-off for Pod restarts. + # Command completes either when cluster initialization succeeds, + # or when cluster has been initialized already. + command: + - /bin/bash + - -c + - >- + {{- if $isClusterInitEnabled }} + initCluster() { + while true; do + local output=$( + set -x; + + /cockroach/cockroach init \ + {{- if .Values.tls.enabled }} + --certs-dir=/cockroach-certs/ \ + {{- else }} + --insecure \ + {{- end }} + {{- with index .Values.conf "cluster-name" }} + --cluster-name={{.}} \ + {{- end }} + --host={{ template "cockroachdb.fullname" . }}-0.{{ template "cockroachdb.fullname" . -}} + :{{ .Values.service.ports.grpc.internal.port | int64 }} \ + {{- if .Values.init.pcr.enabled -}} + {{- if .Values.init.pcr.isPrimary }} + --virtualized \ + {{- else }} + --virtualized-empty \ + {{- end }} + {{- end }} + 2>&1); + + local exitCode="$?"; + echo $output; + + if [[ "$output" =~ .*"Cluster successfully initialized".* || "$output" =~ .*"cluster has already been initialized".* ]]; then + break; + fi + + echo "Cluster is not ready to be initialized, retrying in 5 seconds" + sleep 5; + done + } + + initCluster; + {{- end }} + + {{- if $isDatabaseProvisioningEnabled }} + provisionCluster() { + while true; do + /cockroach/cockroach sql \ + {{- if .Values.tls.enabled }} + --certs-dir=/cockroach-certs/ \ + {{- else }} + --insecure \ + {{- end }} + --host={{ template "cockroachdb.fullname" . }}-0.{{ template "cockroachdb.fullname" . -}} + :{{ .Values.service.ports.grpc.internal.port | int64 }} \ + --execute=" + {{- range $clusterSetting, $clusterSettingValue := .Values.init.provisioning.clusterSettings }} + SET CLUSTER SETTING {{ $clusterSetting }} = '${{ $clusterSetting | replace "." "_" }}_CLUSTER_SETTING'; + {{- end }} + + {{- range $user := .Values.init.provisioning.users }} + CREATE USER IF NOT EXISTS {{ $user.name }} WITH + {{- if $user.password }} + PASSWORD '${{ $user.name }}_PASSWORD' + {{- else }} + PASSWORD null + {{- end }} + {{ join " " $user.options }} + ; + {{- end }} + + {{- range $database := .Values.init.provisioning.databases }} + CREATE DATABASE IF NOT EXISTS {{ $database.name }} + {{- if $database.options }} + {{ join " " $database.options }} + {{- end }} + ; + + {{- range $owner := $database.owners }} + GRANT ALL ON DATABASE {{ $database.name }} TO {{ $owner }}; + {{- end }} + + {{- range $owner := $database.owners_with_grant_option }} + GRANT ALL ON DATABASE {{ $database.name }} TO {{ $owner }} WITH GRANT OPTION; + {{- end }} + + {{- if $database.backup }} + CREATE SCHEDULE IF NOT EXISTS {{ $database.name }}_scheduled_backup + FOR BACKUP DATABASE {{ $database.name }} INTO '{{ $database.backup.into }}' + + {{- if $database.backup.options }} + WITH {{ join "," $database.backup.options }} + {{- end }} + RECURRING '{{ $database.backup.recurring }}' + {{- if $database.backup.fullBackup }} + FULL BACKUP '{{ $database.backup.fullBackup }}' + {{- else }} + FULL BACKUP ALWAYS + {{- end }} + + {{- if and $database.backup.schedule $database.backup.schedule.options }} + WITH SCHEDULE OPTIONS {{ join "," $database.backup.schedule.options }} + {{- end }} + ; + {{- end }} + {{- end }} + " + &>/dev/null; + + local exitCode="$?"; + + if [[ "$exitCode" -eq "0" ]] + then break; + fi + + sleep 5; + done + + echo "Provisioning completed successfully"; + } + + provisionCluster; + {{- end }} + env: + {{- $secretName := printf "%s-init" (include "cockroachdb.fullname" .) }} + {{- range $user := .Values.init.provisioning.users }} + {{- if $user.password }} + - name: {{ $user.name }}_PASSWORD + valueFrom: + secretKeyRef: + name: {{ $secretName }} + key: {{ $user.name }}-password + {{- end }} + {{- end }} + {{- range $clusterSetting, $clusterSettingValue := .Values.init.provisioning.clusterSettings }} + {{- if $clusterSettingValue }} + - name: {{ $clusterSetting | replace "." "_" }}_CLUSTER_SETTING + valueFrom: + secretKeyRef: + name: {{ $secretName }} + key: {{ $clusterSetting | replace "." "-" }}-cluster-setting + {{- end }} + {{- end }} + {{- if .Values.tls.enabled }} + volumeMounts: + - name: client-certs + mountPath: /cockroach-certs/ + {{- end }} + {{- with .Values.init.resources }} + resources: {{- toYaml . | nindent 12 }} + {{- end }} + {{- if and .Values.init.securityContext.enabled }} + securityContext: + allowPrivilegeEscalation: false + capabilities: + drop: ["ALL"] + {{- end }} + {{- if .Values.tls.enabled }} + volumes: + - name: client-certs + emptyDir: {} + {{- if or .Values.tls.certs.provided .Values.tls.certs.certManager .Values.tls.certs.selfSigner.enabled }} + - name: certs-secret + {{- if or .Values.tls.certs.tlsSecret .Values.tls.certs.certManager .Values.tls.certs.selfSigner.enabled }} + projected: + sources: + - secret: + {{- if .Values.tls.certs.selfSigner.enabled }} + name: {{ template "cockroachdb.fullname" . }}-client-secret + {{ else }} + name: {{ .Values.tls.certs.clientRootSecret }} + {{ end -}} + items: + - key: ca.crt + path: ca.crt + mode: 0400 + - key: tls.crt + path: client.root.crt + mode: 0400 + - key: tls.key + path: client.root.key + mode: 0400 + {{- else }} + secret: + secretName: {{ .Values.tls.certs.clientRootSecret }} + defaultMode: 0400 + {{- end }} + {{- end }} + {{- end }} +{{- end }} diff --git a/charts/cockroach-labs/cockroachdb/14.0.2/templates/networkpolicy.yaml b/charts/cockroach-labs/cockroachdb/14.0.2/templates/networkpolicy.yaml new file mode 100644 index 000000000..d41afa32b --- /dev/null +++ b/charts/cockroach-labs/cockroachdb/14.0.2/templates/networkpolicy.yaml @@ -0,0 +1,59 @@ +{{- if .Values.networkPolicy.enabled }} +kind: NetworkPolicy +apiVersion: {{ template "cockroachdb.networkPolicy.apiVersion" . }} +metadata: + name: {{ template "cockroachdb.serviceAccount.name" . }} + namespace: {{ .Release.Namespace | quote }} + labels: + helm.sh/chart: {{ template "cockroachdb.chart" . }} + app.kubernetes.io/name: {{ template "cockroachdb.name" . }} + app.kubernetes.io/instance: {{ .Release.Name | quote }} + app.kubernetes.io/managed-by: {{ .Release.Service | quote }} + {{- with .Values.labels }} + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + podSelector: + matchLabels: + app.kubernetes.io/name: {{ template "cockroachdb.name" . }} + app.kubernetes.io/instance: {{ .Release.Name | quote }} + {{- with .Values.statefulset.labels }} + {{- toYaml . | nindent 6 }} + {{- end }} + ingress: + - ports: + - port: grpc + {{- with .Values.networkPolicy.ingress.grpc }} + from: + # Allow connections via custom rules. + {{- toYaml . | nindent 8 }} + # Allow client connection via pre-considered label. + - podSelector: + matchLabels: + {{ template "cockroachdb.fullname" . }}-client: "true" + # Allow other CockroachDBs to connect to form a cluster. + - podSelector: + matchLabels: + app.kubernetes.io/name: {{ template "cockroachdb.name" . }} + app.kubernetes.io/instance: {{ .Release.Name | quote }} + {{- with .Values.statefulset.labels }} + {{- toYaml . | nindent 14 }} + {{- end }} + {{- if gt (.Values.statefulset.replicas | int64) 1 }} + # Allow init Job to connect to bootstrap a cluster. + - podSelector: + matchLabels: + app.kubernetes.io/name: {{ template "cockroachdb.name" . }} + app.kubernetes.io/instance: {{ .Release.Name | quote }} + {{- with .Values.init.labels }} + {{- toYaml . | nindent 14 }} + {{- end }} + {{- end }} + {{- end }} + # Allow connections to admin UI and for Prometheus. + - ports: + - port: http + {{- with .Values.networkPolicy.ingress.http }} + from: {{- toYaml . | nindent 8 }} + {{- end }} +{{- end }} diff --git a/charts/cockroach-labs/cockroachdb/14.0.2/templates/poddisruptionbudget.yaml b/charts/cockroach-labs/cockroachdb/14.0.2/templates/poddisruptionbudget.yaml new file mode 100644 index 000000000..f707e4054 --- /dev/null +++ b/charts/cockroach-labs/cockroachdb/14.0.2/templates/poddisruptionbudget.yaml @@ -0,0 +1,26 @@ +kind: PodDisruptionBudget +{{- if or (.Capabilities.APIVersions.Has "policy/v1") (semverCompare ">=1.21-0" .Capabilities.KubeVersion.Version) }} +apiVersion: policy/v1 +{{- else }} +apiVersion: policy/v1beta1 +{{- end }} +metadata: + name: {{ template "cockroachdb.fullname" . }}-budget + namespace: {{ .Release.Namespace | quote }} + labels: + helm.sh/chart: {{ template "cockroachdb.chart" . }} + app.kubernetes.io/name: {{ template "cockroachdb.name" . }} + app.kubernetes.io/instance: {{ .Release.Name | quote }} + app.kubernetes.io/managed-by: {{ .Release.Service | quote }} + {{- with .Values.labels }} + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + selector: + matchLabels: + app.kubernetes.io/name: {{ template "cockroachdb.name" . }} + app.kubernetes.io/instance: {{ .Release.Name | quote }} + {{- with .Values.statefulset.labels }} + {{- toYaml . | nindent 6 }} + {{- end }} + maxUnavailable: {{ .Values.statefulset.budget.maxUnavailable | int64 }} diff --git a/charts/cockroach-labs/cockroachdb/14.0.2/templates/role-certRotateSelfSigner.yaml b/charts/cockroach-labs/cockroachdb/14.0.2/templates/role-certRotateSelfSigner.yaml new file mode 100644 index 000000000..f0e2b90ce --- /dev/null +++ b/charts/cockroach-labs/cockroachdb/14.0.2/templates/role-certRotateSelfSigner.yaml @@ -0,0 +1,27 @@ +{{- if and .Values.tls.enabled .Values.tls.certs.selfSigner.enabled }} +kind: Role +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: {{ template "rotatecerts.fullname" . }} + namespace: {{ .Release.Namespace | quote }} + labels: + helm.sh/chart: {{ template "cockroachdb.chart" . }} + app.kubernetes.io/name: {{ template "cockroachdb.name" . }} + app.kubernetes.io/instance: {{ .Release.Name | quote }} + app.kubernetes.io/managed-by: {{ .Release.Service | quote }} + {{- with .Values.labels }} + {{- toYaml . | nindent 4 }} + {{- end }} +rules: + - apiGroups: [""] + resources: ["secrets"] + verbs: ["create", "get", "update", "delete"] + - apiGroups: ["apps"] + resources: ["statefulsets"] + verbs: ["get"] + resourceNames: + - {{ template "cockroachdb.fullname" . }} + - apiGroups: [""] + resources: ["pods"] + verbs: ["delete", "get"] +{{- end }} diff --git a/charts/cockroach-labs/cockroachdb/14.0.2/templates/role-certSelfSigner.yaml b/charts/cockroach-labs/cockroachdb/14.0.2/templates/role-certSelfSigner.yaml new file mode 100644 index 000000000..1cbaab3dd --- /dev/null +++ b/charts/cockroach-labs/cockroachdb/14.0.2/templates/role-certSelfSigner.yaml @@ -0,0 +1,33 @@ +{{- if and .Values.tls.enabled .Values.tls.certs.selfSigner.enabled }} +kind: Role +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: {{ template "selfcerts.fullname" . }} + namespace: {{ .Release.Namespace | quote }} + annotations: + # This is what defines this resource as a hook. Without this line, the + # job is considered part of the release. + "helm.sh/hook": pre-install,pre-upgrade + "helm.sh/hook-weight": "2" + "helm.sh/hook-delete-policy": hook-succeeded,hook-failed + labels: + helm.sh/chart: {{ template "cockroachdb.chart" . }} + app.kubernetes.io/name: {{ template "cockroachdb.name" . }} + app.kubernetes.io/instance: {{ .Release.Name | quote }} + app.kubernetes.io/managed-by: {{ .Release.Service | quote }} + {{- with .Values.labels }} + {{- toYaml . | nindent 4 }} + {{- end }} +rules: + - apiGroups: [""] + resources: ["secrets"] + verbs: ["create", "get", "update", "delete"] + - apiGroups: ["apps"] + resources: ["statefulsets"] + verbs: ["get"] + resourceNames: + - {{ template "cockroachdb.fullname" . }} + - apiGroups: [""] + resources: ["pods"] + verbs: ["delete", "get"] +{{- end }} diff --git a/charts/cockroach-labs/cockroachdb/14.0.2/templates/role.yaml b/charts/cockroach-labs/cockroachdb/14.0.2/templates/role.yaml new file mode 100644 index 000000000..ebe5ce8ae --- /dev/null +++ b/charts/cockroach-labs/cockroachdb/14.0.2/templates/role.yaml @@ -0,0 +1,23 @@ +{{- if .Values.tls.enabled }} +kind: Role +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: {{ template "cockroachdb.fullname" . }} + namespace: {{ .Release.Namespace | quote }} + labels: + helm.sh/chart: {{ template "cockroachdb.chart" . }} + app.kubernetes.io/name: {{ template "cockroachdb.name" . }} + app.kubernetes.io/instance: {{ .Release.Name | quote }} + app.kubernetes.io/managed-by: {{ .Release.Service | quote }} + {{- with .Values.labels }} + {{- toYaml . | nindent 4 }} + {{- end }} +rules: + - apiGroups: [""] + resources: ["secrets"] + {{- if or .Values.tls.certs.provided .Values.tls.certs.certManager }} + verbs: ["get"] + {{- else }} + verbs: ["create", "get"] + {{- end }} +{{- end }} diff --git a/charts/cockroach-labs/cockroachdb/14.0.2/templates/rolebinding-certRotateSelfSigner.yaml b/charts/cockroach-labs/cockroachdb/14.0.2/templates/rolebinding-certRotateSelfSigner.yaml new file mode 100644 index 000000000..c1a45f797 --- /dev/null +++ b/charts/cockroach-labs/cockroachdb/14.0.2/templates/rolebinding-certRotateSelfSigner.yaml @@ -0,0 +1,23 @@ +{{- if and .Values.tls.enabled .Values.tls.certs.selfSigner.enabled }} +kind: RoleBinding +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: {{ template "rotatecerts.fullname" . }} + namespace: {{ .Release.Namespace | quote }} + labels: + helm.sh/chart: {{ template "cockroachdb.chart" . }} + app.kubernetes.io/name: {{ template "cockroachdb.name" . }} + app.kubernetes.io/instance: {{ .Release.Name | quote }} + app.kubernetes.io/managed-by: {{ .Release.Service | quote }} + {{- with .Values.labels }} + {{- toYaml . | nindent 4 }} + {{- end }} +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: {{ template "rotatecerts.fullname" . }} +subjects: + - kind: ServiceAccount + name: {{ template "rotatecerts.fullname" . }} + namespace: {{ .Release.Namespace | quote }} +{{- end }} diff --git a/charts/cockroach-labs/cockroachdb/14.0.2/templates/rolebinding-certSelfSigner.yaml b/charts/cockroach-labs/cockroachdb/14.0.2/templates/rolebinding-certSelfSigner.yaml new file mode 100644 index 000000000..5725d02a4 --- /dev/null +++ b/charts/cockroach-labs/cockroachdb/14.0.2/templates/rolebinding-certSelfSigner.yaml @@ -0,0 +1,29 @@ +{{- if and .Values.tls.enabled .Values.tls.certs.selfSigner.enabled }} +kind: RoleBinding +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: {{ template "selfcerts.fullname" . }} + namespace: {{ .Release.Namespace | quote }} + annotations: + # This is what defines this resource as a hook. Without this line, the + # job is considered part of the release. + "helm.sh/hook": pre-install,pre-upgrade + "helm.sh/hook-weight": "3" + "helm.sh/hook-delete-policy": hook-succeeded,hook-failed + labels: + helm.sh/chart: {{ template "cockroachdb.chart" . }} + app.kubernetes.io/name: {{ template "cockroachdb.name" . }} + app.kubernetes.io/instance: {{ .Release.Name | quote }} + app.kubernetes.io/managed-by: {{ .Release.Service | quote }} + {{- with .Values.labels }} + {{- toYaml . | nindent 4 }} + {{- end }} +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: {{ template "selfcerts.fullname" . }} +subjects: + - kind: ServiceAccount + name: {{ template "selfcerts.fullname" . }} + namespace: {{ .Release.Namespace | quote }} +{{- end }} diff --git a/charts/cockroach-labs/cockroachdb/14.0.2/templates/rolebinding.yaml b/charts/cockroach-labs/cockroachdb/14.0.2/templates/rolebinding.yaml new file mode 100644 index 000000000..00d9f9a55 --- /dev/null +++ b/charts/cockroach-labs/cockroachdb/14.0.2/templates/rolebinding.yaml @@ -0,0 +1,23 @@ +{{- if .Values.tls.enabled }} +kind: RoleBinding +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: {{ template "cockroachdb.fullname" . }} + namespace: {{ .Release.Namespace | quote }} + labels: + helm.sh/chart: {{ template "cockroachdb.chart" . }} + app.kubernetes.io/name: {{ template "cockroachdb.name" . }} + app.kubernetes.io/instance: {{ .Release.Name | quote }} + app.kubernetes.io/managed-by: {{ .Release.Service | quote }} + {{- with .Values.labels }} + {{- toYaml . | nindent 4 }} + {{- end }} +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: {{ template "cockroachdb.fullname" . }} +subjects: + - kind: ServiceAccount + name: {{ template "cockroachdb.serviceAccount.name" . }} + namespace: {{ .Release.Namespace | quote }} +{{- end }} diff --git a/charts/cockroach-labs/cockroachdb/14.0.2/templates/secret.backendconfig.yaml b/charts/cockroach-labs/cockroachdb/14.0.2/templates/secret.backendconfig.yaml new file mode 100644 index 000000000..61103060a --- /dev/null +++ b/charts/cockroach-labs/cockroachdb/14.0.2/templates/secret.backendconfig.yaml @@ -0,0 +1,25 @@ +{{- if .Values.iap.enabled }} +kind: Secret +apiVersion: v1 +metadata: + name: {{ template "cockroachdb.fullname" . }}.iap + namespace: {{ .Release.Namespace | quote }} + labels: + helm.sh/chart: {{ template "cockroachdb.chart" . }} + app.kubernetes.io/name: {{ template "cockroachdb.name" . }} + app.kubernetes.io/instance: {{ .Release.Name | quote }} + app.kubernetes.io/managed-by: {{ .Release.Service | quote }} + {{- with .Values.labels }} + {{- toYaml . | nindent 4 }} + {{- end }} +type: Opaque +data: + {{- if eq "" .Values.iap.clientId }} + {{ fail "iap.clientID can't be empty if iap.enabled is set to true" }} + {{- end }} + client_id: {{ .Values.iap.clientId | b64enc }} + {{- if eq "" .Values.iap.clientSecret }} + {{ fail "iap.clientSecret can't be empty if iap.enabled is set to true" }} + {{- end }} + client_secret: {{ .Values.iap.clientSecret | b64enc }} +{{- end }} \ No newline at end of file diff --git a/charts/cockroach-labs/cockroachdb/14.0.2/templates/secret.logconfig.yaml b/charts/cockroach-labs/cockroachdb/14.0.2/templates/secret.logconfig.yaml new file mode 100644 index 000000000..40b929ae7 --- /dev/null +++ b/charts/cockroach-labs/cockroachdb/14.0.2/templates/secret.logconfig.yaml @@ -0,0 +1,19 @@ +{{- if .Values.conf.log.enabled }} +kind: Secret +apiVersion: v1 +metadata: + name: {{ template "cockroachdb.fullname" . }}-log-config + namespace: {{ .Release.Namespace | quote }} + labels: + helm.sh/chart: {{ template "cockroachdb.chart" . }} + app.kubernetes.io/name: {{ template "cockroachdb.name" . }} + app.kubernetes.io/instance: {{ .Release.Name | quote }} + app.kubernetes.io/managed-by: {{ .Release.Service | quote }} + {{- with .Values.labels }} + {{- toYaml . | nindent 4 }} + {{- end }} +type: Opaque +stringData: + log-config.yaml: | + {{- toYaml .Values.conf.log.config | nindent 4 }} +{{- end }} diff --git a/charts/cockroach-labs/cockroachdb/14.0.2/templates/secret.registry.yaml b/charts/cockroach-labs/cockroachdb/14.0.2/templates/secret.registry.yaml new file mode 100644 index 000000000..a054069fb --- /dev/null +++ b/charts/cockroach-labs/cockroachdb/14.0.2/templates/secret.registry.yaml @@ -0,0 +1,23 @@ +{{- range $name, $cred := dict "db" (.Values.image.credentials) "init-certs" (.Values.tls.selfSigner.image.credentials) }} +{{- if not (empty $cred) }} +{{- if or (and (eq $name "init-certs") $.Values.tls.enabled) (ne $name "init-certs") }} +--- +kind: Secret +apiVersion: v1 +metadata: + name: {{ template "cockroachdb.fullname" $ }}.{{ $name }}.registry + namespace: {{ $.Release.Namespace | quote }} + labels: + helm.sh/chart: {{ template "cockroachdb.chart" $ }} + app.kubernetes.io/name: {{ template "cockroachdb.name" $ }} + app.kubernetes.io/instance: {{ $.Release.Name | quote }} + app.kubernetes.io/managed-by: {{ $.Release.Service | quote }} + {{- with $.Values.labels }} + {{- toYaml . | nindent 4 }} + {{- end }} +type: kubernetes.io/dockerconfigjson +data: + .dockerconfigjson: {{ printf `{"auths":{%s:{"auth":"%s"}}}` ($cred.registry | quote) (printf "%s:%s" $cred.username $cred.password | b64enc) | b64enc | quote }} +{{- end }} +{{- end }} +{{- end }} diff --git a/charts/cockroach-labs/cockroachdb/14.0.2/templates/secrets.init.yaml b/charts/cockroach-labs/cockroachdb/14.0.2/templates/secrets.init.yaml new file mode 100644 index 000000000..4d13a35ff --- /dev/null +++ b/charts/cockroach-labs/cockroachdb/14.0.2/templates/secrets.init.yaml @@ -0,0 +1,20 @@ +{{- if .Values.init.provisioning.enabled }} +apiVersion: v1 +kind: Secret +metadata: + name: {{ template "cockroachdb.fullname" . }}-init + namespace: {{ .Release.Namespace | quote }} +type: Opaque +stringData: + +{{- range $user := .Values.init.provisioning.users }} +{{- if $user.password }} + {{ $user.name }}-password: {{ $user.password | quote }} +{{- end }} +{{- end }} + +{{- range $clusterSetting, $clusterSettingValue := .Values.init.provisioning.clusterSettings }} + {{ $clusterSetting | replace "." "-" }}-cluster-setting: {{ $clusterSettingValue | quote }} +{{- end }} + +{{- end }} diff --git a/charts/cockroach-labs/cockroachdb/14.0.2/templates/service.discovery.yaml b/charts/cockroach-labs/cockroachdb/14.0.2/templates/service.discovery.yaml new file mode 100644 index 000000000..8fe2a427a --- /dev/null +++ b/charts/cockroach-labs/cockroachdb/14.0.2/templates/service.discovery.yaml @@ -0,0 +1,64 @@ +# This service only exists to create DNS entries for each pod in +# the StatefulSet such that they can resolve each other's IP addresses. +# It does not create a load-balanced ClusterIP and should not be used directly +# by clients in most circumstances. +kind: Service +apiVersion: v1 +metadata: + name: {{ template "cockroachdb.fullname" . }} + namespace: {{ .Release.Namespace | quote }} + labels: + helm.sh/chart: {{ template "cockroachdb.chart" . }} + app.kubernetes.io/name: {{ template "cockroachdb.name" . }} + app.kubernetes.io/instance: {{ .Release.Name | quote }} + app.kubernetes.io/managed-by: {{ .Release.Service | quote }} + {{- with .Values.service.discovery.labels }} + {{- toYaml . | nindent 4 }} + {{- end }} + {{- with .Values.labels }} + {{- toYaml . | nindent 4 }} + {{- end }} + annotations: + # Use this annotation in addition to the actual 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" + # Enable automatic monitoring of all instances when Prometheus is running + # in the cluster. + {{- if .Values.prometheus.enabled }} + prometheus.io/scrape: "true" + prometheus.io/path: _status/vars + prometheus.io/port: {{ .Values.service.ports.http.port | quote }} + {{- end }} + {{- with .Values.service.discovery.annotations }} + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + clusterIP: None + # We want all Pods in the StatefulSet to have their addresses published for + # the sake of the other CockroachDB 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: + {{- $ports := .Values.service.ports }} + # The main port, served by gRPC, serves Postgres-flavor SQL, inter-node + # traffic and the CLI. + - name: {{ $ports.grpc.external.name | quote }} + port: {{ $ports.grpc.external.port | int64 }} + targetPort: grpc + {{- if ne ($ports.grpc.internal.port | int64) ($ports.grpc.external.port | int64) }} + - name: {{ $ports.grpc.internal.name | quote }} + port: {{ $ports.grpc.internal.port | int64 }} + targetPort: grpc + {{- end }} + # The secondary port serves the UI as well as health and debug endpoints. + - name: {{ $ports.http.name | quote }} + port: {{ $ports.http.port | int64 }} + targetPort: http + selector: + app.kubernetes.io/name: {{ template "cockroachdb.name" . }} + app.kubernetes.io/instance: {{ .Release.Name | quote }} + {{- with .Values.statefulset.labels }} + {{- toYaml . | nindent 4 }} + {{- end }} diff --git a/charts/cockroach-labs/cockroachdb/14.0.2/templates/service.public.yaml b/charts/cockroach-labs/cockroachdb/14.0.2/templates/service.public.yaml new file mode 100644 index 000000000..251e9ab08 --- /dev/null +++ b/charts/cockroach-labs/cockroachdb/14.0.2/templates/service.public.yaml @@ -0,0 +1,55 @@ +# This Service is meant to be used by clients of the database. +# It exposes a ClusterIP that will automatically load balance connections +# to the different database Pods. +kind: Service +apiVersion: v1 +metadata: + name: {{ template "cockroachdb.fullname" . }}-public + namespace: {{ .Release.Namespace | quote }} + labels: + helm.sh/chart: {{ template "cockroachdb.chart" . }} + app.kubernetes.io/name: {{ template "cockroachdb.name" . }} + app.kubernetes.io/instance: {{ .Release.Name | quote }} + app.kubernetes.io/managed-by: {{ .Release.Service | quote }} + {{- with .Values.service.public.labels }} + {{- toYaml . | nindent 4 }} + {{- end }} + {{- with .Values.labels }} + {{- toYaml . | nindent 4 }} + {{- end }} + {{- if or .Values.service.public.annotations .Values.tls.enabled .Values.iap.enabled }} + annotations: + {{- with .Values.service.public.annotations }} + {{- toYaml . | nindent 4 }} + {{- end }} + {{- if .Values.tls.enabled }} + service.alpha.kubernetes.io/app-protocols: '{"http":"HTTPS"}' + {{- end }} + {{- if .Values.iap.enabled }} + beta.cloud.google.com/backend-config: '{"default": "{{ template "cockroachdb.fullname" . }}"}' + {{- end }} + {{- end }} +spec: + type: {{ .Values.service.public.type | quote }} + ports: + {{- $ports := .Values.service.ports }} + # The main port, served by gRPC, serves Postgres-flavor SQL, inter-node + # traffic and the CLI. + - name: {{ $ports.grpc.external.name | quote }} + port: {{ $ports.grpc.external.port | int64 }} + targetPort: grpc + {{- if ne ($ports.grpc.internal.port | int64) ($ports.grpc.external.port | int64) }} + - name: {{ $ports.grpc.internal.name | quote }} + port: {{ $ports.grpc.internal.port | int64 }} + targetPort: grpc + {{- end }} + # The secondary port serves the UI as well as health and debug endpoints. + - name: {{ $ports.http.name | quote }} + port: {{ $ports.http.port | int64 }} + targetPort: http + selector: + app.kubernetes.io/name: {{ template "cockroachdb.name" . }} + app.kubernetes.io/instance: {{ .Release.Name | quote }} + {{- with .Values.statefulset.labels }} + {{- toYaml . | nindent 4 }} + {{- end }} diff --git a/charts/cockroach-labs/cockroachdb/14.0.2/templates/serviceMonitor.yaml b/charts/cockroach-labs/cockroachdb/14.0.2/templates/serviceMonitor.yaml new file mode 100644 index 000000000..42f2390b4 --- /dev/null +++ b/charts/cockroach-labs/cockroachdb/14.0.2/templates/serviceMonitor.yaml @@ -0,0 +1,54 @@ +{{- $serviceMonitor := .Values.serviceMonitor -}} +{{- $ports := .Values.service.ports -}} +{{- if $serviceMonitor.enabled }} +apiVersion: monitoring.coreos.com/v1 +kind: ServiceMonitor +metadata: + name: {{ template "cockroachdb.fullname" . }} + namespace: {{ .Release.Namespace | quote }} + labels: + helm.sh/chart: {{ template "cockroachdb.chart" . }} + app.kubernetes.io/name: {{ template "cockroachdb.name" . }} + app.kubernetes.io/instance: {{ .Release.Name | quote }} + app.kubernetes.io/managed-by: {{ .Release.Service | quote }} + {{- if $serviceMonitor.labels }} + {{- toYaml $serviceMonitor.labels | nindent 4 }} + {{- end }} + {{- with .Values.labels }} + {{- toYaml . | nindent 4 }} + {{- end }} + {{- if $serviceMonitor.annotations }} + annotations: + {{- toYaml $serviceMonitor.annotations | nindent 4 }} + {{- end }} +spec: + selector: + matchLabels: + app.kubernetes.io/name: {{ template "cockroachdb.name" . }} + app.kubernetes.io/instance: {{ .Release.Name | quote }} + {{- with .Values.service.discovery.labels }} + {{- toYaml . | nindent 6 }} + {{- end }} + {{- with .Values.labels }} + {{- toYaml . | nindent 6 }} + {{- end }} + namespaceSelector: + {{- if $serviceMonitor.namespaced }} + matchNames: + - {{ .Release.Namespace }} + {{- else }} + any: true + {{- end }} + endpoints: + - port: {{ $ports.http.name | quote }} + path: /_status/vars + {{- if $serviceMonitor.interval }} + interval: {{ $serviceMonitor.interval }} + {{- end }} + {{- if $serviceMonitor.scrapeTimeout }} + scrapeTimeout: {{ $serviceMonitor.scrapeTimeout }} + {{- end }} + {{- if .Values.serviceMonitor.tlsConfig }} + tlsConfig: {{ toYaml .Values.serviceMonitor.tlsConfig | nindent 6 }} + {{- end }} +{{- end }} diff --git a/charts/cockroach-labs/cockroachdb/14.0.2/templates/serviceaccount-certRotateSelfSigner.yaml b/charts/cockroach-labs/cockroachdb/14.0.2/templates/serviceaccount-certRotateSelfSigner.yaml new file mode 100644 index 000000000..a27cba921 --- /dev/null +++ b/charts/cockroach-labs/cockroachdb/14.0.2/templates/serviceaccount-certRotateSelfSigner.yaml @@ -0,0 +1,22 @@ +{{- if and .Values.tls.enabled .Values.tls.certs.selfSigner.enabled }} + {{ template "cockroachdb.tls.certs.selfSigner.validation" . }} +kind: ServiceAccount +apiVersion: v1 +metadata: + name: {{ template "rotatecerts.fullname" . }} + namespace: {{ .Release.Namespace | quote }} + labels: + helm.sh/chart: {{ template "cockroachdb.chart" . }} + app.kubernetes.io/name: {{ template "cockroachdb.name" . }} + app.kubernetes.io/instance: {{ .Release.Name | quote }} + app.kubernetes.io/managed-by: {{ .Release.Service | quote }} + {{- with .Values.labels }} + {{- toYaml . | nindent 4 }} + {{- end }} + {{- if .Values.tls.certs.selfSigner.svcAccountAnnotations }} + annotations: + {{- with .Values.tls.certs.selfSigner.svcAccountAnnotations }} + {{- toYaml . | nindent 4 }} + {{- end }} + {{- end }} +{{- end }} diff --git a/charts/cockroach-labs/cockroachdb/14.0.2/templates/serviceaccount-certSelfSigner.yaml b/charts/cockroach-labs/cockroachdb/14.0.2/templates/serviceaccount-certSelfSigner.yaml new file mode 100644 index 000000000..3ce2d63e9 --- /dev/null +++ b/charts/cockroach-labs/cockroachdb/14.0.2/templates/serviceaccount-certSelfSigner.yaml @@ -0,0 +1,25 @@ +{{- if and .Values.tls.enabled .Values.tls.certs.selfSigner.enabled }} + {{ template "cockroachdb.tls.certs.selfSigner.validation" . }} +kind: ServiceAccount +apiVersion: v1 +metadata: + name: {{ template "selfcerts.fullname" . }} + namespace: {{ .Release.Namespace | quote }} + annotations: + # This is what defines this resource as a hook. Without this line, the + # job is considered part of the release. + "helm.sh/hook": pre-install,pre-upgrade + "helm.sh/hook-weight": "1" + "helm.sh/hook-delete-policy": hook-succeeded,hook-failed + {{- with .Values.tls.certs.selfSigner.svcAccountAnnotations }} + {{- toYaml . | nindent 4 }} + {{- end }} + labels: + helm.sh/chart: {{ template "cockroachdb.chart" . }} + app.kubernetes.io/name: {{ template "cockroachdb.name" . }} + app.kubernetes.io/instance: {{ .Release.Name | quote }} + app.kubernetes.io/managed-by: {{ .Release.Service | quote }} + {{- with .Values.labels }} + {{- toYaml . | nindent 4 }} + {{- end }} +{{- end }} diff --git a/charts/cockroach-labs/cockroachdb/14.0.2/templates/serviceaccount.yaml b/charts/cockroach-labs/cockroachdb/14.0.2/templates/serviceaccount.yaml new file mode 100644 index 000000000..3af9be9aa --- /dev/null +++ b/charts/cockroach-labs/cockroachdb/14.0.2/templates/serviceaccount.yaml @@ -0,0 +1,21 @@ +{{- if .Values.statefulset.serviceAccount.create }} +kind: ServiceAccount +apiVersion: v1 +metadata: + name: {{ template "cockroachdb.serviceAccount.name" . }} + namespace: {{ .Release.Namespace | quote }} + labels: + helm.sh/chart: {{ template "cockroachdb.chart" . }} + app.kubernetes.io/name: {{ template "cockroachdb.name" . }} + app.kubernetes.io/instance: {{ .Release.Name | quote }} + app.kubernetes.io/managed-by: {{ .Release.Service | quote }} + {{- with .Values.labels }} + {{- toYaml . | nindent 4 }} + {{- end }} + {{- if .Values.statefulset.serviceAccount.annotations }} + annotations: + {{- with .Values.statefulset.serviceAccount.annotations }} + {{- toYaml . | nindent 4 }} + {{- end }} + {{- end }} +{{- end }} diff --git a/charts/cockroach-labs/cockroachdb/14.0.2/templates/statefulset.yaml b/charts/cockroach-labs/cockroachdb/14.0.2/templates/statefulset.yaml new file mode 100644 index 000000000..a627c3516 --- /dev/null +++ b/charts/cockroach-labs/cockroachdb/14.0.2/templates/statefulset.yaml @@ -0,0 +1,402 @@ +kind: StatefulSet +apiVersion: {{ template "cockroachdb.statefulset.apiVersion" . }} +metadata: + name: {{ template "cockroachdb.fullname" . }} + namespace: {{ .Release.Namespace | quote }} + labels: + helm.sh/chart: {{ template "cockroachdb.chart" . }} + app.kubernetes.io/name: {{ template "cockroachdb.name" . }} + app.kubernetes.io/instance: {{ .Release.Name | quote }} + app.kubernetes.io/managed-by: {{ .Release.Service | quote }} + {{- with .Values.statefulset.labels }} + {{- toYaml . | nindent 4 }} + {{- end }} + {{- with .Values.labels }} + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + serviceName: {{ template "cockroachdb.fullname" . }} + replicas: {{ .Values.statefulset.replicas | int64 }} + updateStrategy: {{- toYaml .Values.statefulset.updateStrategy | nindent 4 }} + podManagementPolicy: {{ .Values.statefulset.podManagementPolicy | quote }} + selector: + matchLabels: + app.kubernetes.io/name: {{ template "cockroachdb.name" . }} + app.kubernetes.io/instance: {{ .Release.Name | quote }} + {{- with .Values.statefulset.labels }} + {{- toYaml . | nindent 6 }} + {{- end }} + template: + metadata: + labels: + app.kubernetes.io/name: {{ template "cockroachdb.name" . }} + app.kubernetes.io/instance: {{ .Release.Name | quote }} + {{- with .Values.statefulset.labels }} + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.labels }} + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.statefulset.annotations }} + annotations: {{- toYaml . | nindent 8 }} + {{- end }} + spec: + {{- if or .Values.image.credentials (and .Values.tls.enabled .Values.tls.selfSigner.image.credentials (not .Values.tls.certs.provided) (not .Values.tls.certs.certManager)) }} + imagePullSecrets: + {{- if .Values.image.credentials }} + - name: {{ template "cockroachdb.fullname" . }}.db.registry + {{- end }} + {{- if and .Values.tls.enabled .Values.tls.selfSigner.image.credentials (not .Values.tls.certs.provided) (not .Values.tls.certs.certManager) }} + - name: {{ template "cockroachdb.fullname" . }}.self-signed-certs.registry + {{- end }} + {{- end }} + serviceAccountName: {{ template "cockroachdb.serviceAccount.name" . }} + {{- if .Values.tls.enabled }} + initContainers: + - name: copy-certs + image: {{ .Values.tls.copyCerts.image | quote }} + imagePullPolicy: {{ .Values.tls.selfSigner.image.pullPolicy | quote }} + command: + - /bin/sh + - -c + - "cp -f /certs/* /cockroach-certs/; chmod 0400 /cockroach-certs/*.key" + env: + - name: POD_NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace + {{- if .Values.statefulset.securityContext.enabled }} + securityContext: + allowPrivilegeEscalation: false + capabilities: + drop: + - ALL + privileged: false + readOnlyRootFilesystem: true + {{- end }} + volumeMounts: + - name: certs + mountPath: /cockroach-certs/ + - name: certs-secret + mountPath: /certs/ + {{- with .Values.tls.copyCerts.resources }} + resources: {{- toYaml . | nindent 12 }} + {{- end }} + {{- end }} + {{- if or .Values.statefulset.nodeAffinity .Values.statefulset.podAffinity .Values.statefulset.podAntiAffinity }} + affinity: + {{- with .Values.statefulset.nodeAffinity }} + nodeAffinity: {{- toYaml . | nindent 10 }} + {{- end }} + {{- with .Values.statefulset.podAffinity }} + podAffinity: {{- toYaml . | nindent 10 }} + {{- end }} + {{- if .Values.statefulset.podAntiAffinity }} + podAntiAffinity: + {{- if .Values.statefulset.podAntiAffinity.type }} + {{- if eq .Values.statefulset.podAntiAffinity.type "hard" }} + requiredDuringSchedulingIgnoredDuringExecution: + - topologyKey: {{ .Values.statefulset.podAntiAffinity.topologyKey }} + labelSelector: + matchLabels: + app.kubernetes.io/name: {{ template "cockroachdb.name" . }} + app.kubernetes.io/instance: {{ .Release.Name | quote }} + {{- with .Values.statefulset.labels }} + {{- toYaml . | nindent 18 }} + {{- end }} + {{- else if eq .Values.statefulset.podAntiAffinity.type "soft" }} + preferredDuringSchedulingIgnoredDuringExecution: + - weight: {{ .Values.statefulset.podAntiAffinity.weight | int64 }} + podAffinityTerm: + topologyKey: {{ .Values.statefulset.podAntiAffinity.topologyKey }} + labelSelector: + matchLabels: + app.kubernetes.io/name: {{ template "cockroachdb.name" . }} + app.kubernetes.io/instance: {{ .Release.Name | quote }} + {{- with .Values.statefulset.labels }} + {{- toYaml . | nindent 20 }} + {{- end }} + {{- end }} + {{- else }} + {{- toYaml .Values.statefulset.podAntiAffinity | nindent 10 }} + {{- end }} + {{- end }} + {{- end }} + {{- if semverCompare ">=1.16-0" .Capabilities.KubeVersion.Version }} + topologySpreadConstraints: + - labelSelector: + matchLabels: + app.kubernetes.io/name: {{ template "cockroachdb.name" . }} + app.kubernetes.io/instance: {{ .Release.Name | quote }} + {{- with .Values.statefulset.labels }} + {{- toYaml . | nindent 12 }} + {{- end }} + {{- with .Values.statefulset.topologySpreadConstraints }} + maxSkew: {{ .maxSkew }} + topologyKey: {{ .topologyKey }} + whenUnsatisfiable: {{ .whenUnsatisfiable }} + {{- end }} + {{- end }} + {{- with .Values.statefulset.nodeSelector }} + nodeSelector: {{- toYaml . | nindent 8 }} + {{- end }} + {{- if .Values.statefulset.priorityClassName }} + priorityClassName: {{ .Values.statefulset.priorityClassName }} + {{- end }} + {{- with .Values.statefulset.tolerations }} + tolerations: {{- toYaml . | nindent 8 }} + {{- end }} + # No pre-stop hook is required, a SIGTERM plus some time is all that's + # needed for graceful shutdown of a node. + terminationGracePeriodSeconds: 300 + containers: + - name: db + image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}" + imagePullPolicy: {{ .Values.image.pullPolicy | quote }} + args: + - shell + - -ecx + # The use of qualified `hostname -f` is crucial: + # Other nodes aren't able to look up the unqualified hostname. + # + # `--join` CLI flag is hardcoded to exactly 3 Pods, because: + # 1. Having `--join` value depending on `statefulset.replicas` + # will trigger undesired restart of existing Pods when + # StatefulSet is scaled up/down. We want to scale without + # restarting existing Pods. + # 2. At least one Pod in `--join` is enough to successfully + # join CockroachDB cluster and gossip with all other existing + # Pods, even if there are 3 or more Pods. + # 3. It's harmless for `--join` to have 3 Pods even for 1-Pod + # clusters, while it gives us opportunity to scale up even if + # some Pods of existing cluster are down (for whatever reason). + # See details explained here: + # https://github.com/helm/charts/pull/18993#issuecomment-558795102 + - >- + exec /cockroach/cockroach + {{- if index .Values.conf `single-node` }} + start-single-node + {{- else }} + start --join= + {{- if .Values.conf.join }} + {{- join `,` .Values.conf.join -}} + {{- else }} + {{- range $i, $_ := until 3 -}} + {{- if gt $i 0 -}},{{- end -}} + ${STATEFULSET_NAME}-{{ $i }}.${STATEFULSET_FQDN}:{{ $.Values.service.ports.grpc.internal.port | int64 -}} + {{- end -}} + {{- end }} + {{- with index .Values.conf `cluster-name` }} + --cluster-name={{ . }} + {{- if index $.Values.conf `disable-cluster-name-verification` }} + --disable-cluster-name-verification + {{- end }} + {{- end }} + {{- end }} + --advertise-host=$(hostname).${STATEFULSET_FQDN} + {{- if .Values.tls.enabled }} + --certs-dir=/cockroach/cockroach-certs/ + {{- else }} + --insecure + {{- end }} + {{- with .Values.conf.attrs }} + --attrs={{ join `:` . }} + {{- end }} + --http-port={{ index .Values.conf `http-port` | int64 }} + --port={{ .Values.conf.port | int64 }} + --cache={{ .Values.conf.cache }} + {{- with index .Values.conf `max-disk-temp-storage` }} + --max-disk-temp-storage={{ . }} + {{- end }} + {{- with index .Values.conf `max-offset` }} + --max-offset={{ . }} + {{- end }} + --max-sql-memory={{ index .Values.conf `max-sql-memory` }} + {{- with .Values.conf.locality }} + --locality={{ . }} + {{- end }} + {{- with index .Values.conf `sql-audit-dir` }} + --sql-audit-dir={{ . }} + {{- end }} + {{- if .Values.conf.store.enabled }} + --store={{ template "cockroachdb.conf.store" . }} + {{- end }} + {{- if .Values.conf.log.enabled }} + --log-config-file=/cockroach/log-config/log-config.yaml + {{- else }} + --logtostderr={{ .Values.conf.logtostderr }} + {{- end }} + {{- range .Values.statefulset.args }} + {{ . }} + {{- end }} + env: + - name: STATEFULSET_NAME + value: {{ template "cockroachdb.fullname" . }} + - name: STATEFULSET_FQDN + value: {{ template "cockroachdb.fullname" . }}.{{ .Release.Namespace }}.svc.{{ .Values.clusterDomain }} + - name: COCKROACH_CHANNEL + value: kubernetes-helm + {{- with .Values.statefulset.env }} + {{- toYaml . | nindent 12 }} + {{- end }} + ports: + - name: grpc + containerPort: {{ .Values.conf.port | int64 }} + protocol: TCP + - name: http + containerPort: {{ index .Values.conf `http-port` | int64 }} + protocol: TCP + volumeMounts: + - name: datadir + mountPath: /cockroach/{{ .Values.conf.path }}/ + {{- if .Values.tls.enabled }} + - name: certs + mountPath: /cockroach/cockroach-certs/ + {{- if .Values.tls.certs.provided }} + - name: certs-secret + mountPath: /cockroach/certs/ + {{- end }} + {{- end }} + {{- range .Values.statefulset.secretMounts }} + - name: {{ printf "secret-%s" . | quote }} + mountPath: {{ printf "/etc/cockroach/secrets/%s" . | quote }} + readOnly: true + {{- end }} + {{- if .Values.conf.log.enabled }} + - name: log-config + mountPath: /cockroach/log-config + readOnly: true + {{- end }} + livenessProbe: + {{- if .Values.statefulset.customLivenessProbe }} + {{ toYaml .Values.statefulset.customLivenessProbe | nindent 12 }} + {{- else }} + httpGet: + path: /health + port: http + {{- if .Values.tls.enabled }} + scheme: HTTPS + {{- end }} + initialDelaySeconds: 30 + periodSeconds: 5 + {{- end }} + readinessProbe: + {{- if .Values.statefulset.customReadinessProbe }} + {{ toYaml .Values.statefulset.customReadinessProbe | nindent 12 }} + {{- else }} + httpGet: + path: /health?ready=1 + port: http + {{- if .Values.tls.enabled }} + scheme: HTTPS + {{- end }} + initialDelaySeconds: 10 + periodSeconds: 5 + failureThreshold: 2 + {{- end }} + {{- if eq (include "cockroachdb.securityContext.versionValidation" .) "true" }} + {{- if .Values.statefulset.securityContext.enabled }} + securityContext: + allowPrivilegeEscalation: false + capabilities: + drop: + - ALL + privileged: false + readOnlyRootFilesystem: true + {{- end }} + {{- end }} + {{- with .Values.statefulset.resources }} + resources: {{- toYaml . | nindent 12 }} + {{- end }} + volumes: + - name: datadir + {{- if .Values.storage.persistentVolume.enabled }} + persistentVolumeClaim: + claimName: datadir + {{- else if .Values.storage.hostPath }} + hostPath: + path: {{ .Values.storage.hostPath | quote }} + {{- else }} + emptyDir: {} + {{- end }} + {{- if .Values.tls.enabled }} + - name: certs + emptyDir: {} + {{- if or .Values.tls.certs.provided .Values.tls.certs.certManager .Values.tls.certs.selfSigner.enabled }} + - name: certs-secret + {{- if or .Values.tls.certs.tlsSecret .Values.tls.certs.certManager .Values.tls.certs.selfSigner.enabled }} + projected: + sources: + - secret: + {{- if .Values.tls.certs.selfSigner.enabled }} + name: {{ template "cockroachdb.fullname" . }}-node-secret + {{ else }} + name: {{ .Values.tls.certs.nodeSecret }} + {{ end -}} + items: + - key: ca.crt + path: ca.crt + mode: 256 + - key: tls.crt + path: node.crt + mode: 256 + - key: tls.key + path: node.key + mode: 256 + {{- else }} + secret: + secretName: {{ .Values.tls.certs.nodeSecret }} + defaultMode: 256 + {{- end }} + {{- end }} + {{- end }} + {{- range .Values.statefulset.secretMounts }} + - name: {{ printf "secret-%s" . | quote }} + secret: + secretName: {{ . | quote }} + {{- end }} + {{- if .Values.conf.log.enabled }} + - name: log-config + secret: + secretName: {{ template "cockroachdb.fullname" . }}-log-config + {{- end }} + {{- if eq (include "cockroachdb.securityContext.versionValidation" .) "true" }} + {{- if and .Values.securityContext.enabled }} + securityContext: + seccompProfile: + type: "RuntimeDefault" + fsGroup: 1000 + runAsGroup: 1000 + runAsUser: 1000 + runAsNonRoot: true + {{- end }} + {{- end }} +{{- if .Values.storage.persistentVolume.enabled }} + volumeClaimTemplates: + - metadata: + name: datadir + labels: + app.kubernetes.io/name: {{ template "cockroachdb.name" . }} + app.kubernetes.io/instance: {{ .Release.Name | quote }} + {{- with .Values.storage.persistentVolume.labels }} + {{- toYaml . | nindent 10 }} + {{- end }} + {{- with .Values.labels }} + {{- toYaml . | nindent 10 }} + {{- end }} + {{- with .Values.storage.persistentVolume.annotations }} + annotations: {{- toYaml . | nindent 10 }} + {{- end }} + spec: + accessModes: ["ReadWriteOnce"] + {{- if .Values.storage.persistentVolume.storageClass }} + {{- if (eq "-" .Values.storage.persistentVolume.storageClass) }} + storageClassName: "" + {{- else }} + storageClassName: {{ .Values.storage.persistentVolume.storageClass | quote}} + {{- end }} + {{- end }} + resources: + requests: + storage: {{ .Values.storage.persistentVolume.size | quote }} +{{- end }} diff --git a/charts/cockroach-labs/cockroachdb/14.0.2/templates/tests/client.yaml b/charts/cockroach-labs/cockroachdb/14.0.2/templates/tests/client.yaml new file mode 100644 index 000000000..8656b8ed6 --- /dev/null +++ b/charts/cockroach-labs/cockroachdb/14.0.2/templates/tests/client.yaml @@ -0,0 +1,65 @@ +kind: Pod +apiVersion: v1 +metadata: + name: {{ template "cockroachdb.fullname" . }}-test + namespace: {{ .Release.Namespace | quote }} +{{- if .Values.networkPolicy.enabled }} + labels: + {{ template "cockroachdb.fullname" . }}-client: "true" +{{- end }} + annotations: + helm.sh/hook: test-success +spec: + restartPolicy: Never +{{- if .Values.image.credentials }} + imagePullSecrets: + - name: {{ template "cockroachdb.fullname" . }}.db.registry +{{- end }} + {{- if or .Values.tls.certs.provided .Values.tls.certs.certManager }} + volumes: + - name: client-certs + {{- if or .Values.tls.certs.tlsSecret .Values.tls.certs.certManager }} + projected: + sources: + - secret: + name: {{ .Values.tls.certs.clientRootSecret }} + items: + - key: ca.crt + path: ca.crt + mode: 0400 + - key: tls.crt + path: client.root.crt + mode: 0400 + - key: tls.key + path: client.root.key + mode: 0400 + {{- else }} + secret: + secretName: {{ .Values.tls.certs.clientRootSecret }} + defaultMode: 0400 + {{- end }} + {{- end }} + containers: + - name: client-test + image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}" + imagePullPolicy: {{ .Values.image.pullPolicy | quote }} + {{- if or .Values.tls.certs.provided .Values.tls.certs.certManager }} + volumeMounts: + - name: client-certs + mountPath: /cockroach-certs + {{- end }} + command: + - /cockroach/cockroach + - sql + {{- if or .Values.tls.certs.provided .Values.tls.certs.certManager }} + - --certs-dir + - /cockroach-certs + {{- else }} + - --insecure + {{- end}} + - --host + - {{ template "cockroachdb.fullname" . }}-public.{{ .Release.Namespace }} + - --port + - {{ .Values.service.ports.grpc.external.port | quote }} + - -e + - SHOW DATABASES; diff --git a/charts/cockroach-labs/cockroachdb/14.0.2/values.schema.json b/charts/cockroach-labs/cockroachdb/14.0.2/values.schema.json new file mode 100644 index 000000000..b23c47974 --- /dev/null +++ b/charts/cockroach-labs/cockroachdb/14.0.2/values.schema.json @@ -0,0 +1,97 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "properties": { + "tls": { + "type": "object", + "properties": { + "certs": { + "type": "object", + "properties": { + "selfSigner": { + "type": "object", + "required": ["enabled", "caProvided"], + "properties": { + "enabled": { + "type": "boolean" + }, + "caProvided": { + "type": "boolean" + } + }, + "if": { + "properties": { + "enabled": { + "const": true + } + } + }, + "then": { + "if": { + "properties": { + "caProvided": { + "const": false + } + } + }, + "then": { + "properties": { + "caCertDuration" : { + "type": "string", + "pattern": "^[0-9]*h$" + }, + "caCertExpiryWindow": { + "type": "string", + "pattern": "^[0-9]*h$" + } + } + }, + "properties": { + "clientCertDuration": { + "type": "string", + "pattern": "^[0-9]*h$" + }, + "clientCertExpiryWindow": { + "type": "string", + "pattern": "^[0-9]*h$" + }, + "nodeCertDuration": { + "type": "string", + "pattern": "^[0-9]*h$" + }, + "nodeCertExpiryWindow": { + "type": "string", + "pattern": "^[0-9]*h$" + }, + "rotateCerts": { + "type": "boolean" + } + } + } + } + } + }, + "selfSigner": { + "type": "object", + "properties": { + "image": { + "type": "object", + "required": ["repository", "tag", "pullPolicy"], + "properties": { + "repository": { + "type": "string" + }, + "tag": { + "type": "string" + }, + "pullPolicy": { + "type": "string", + "pattern": "^(Always|Never|IfNotPresent)$" + } + } + } + } + } + } + } + } +} \ No newline at end of file diff --git a/charts/cockroach-labs/cockroachdb/14.0.2/values.yaml b/charts/cockroach-labs/cockroachdb/14.0.2/values.yaml new file mode 100644 index 000000000..44f79d41f --- /dev/null +++ b/charts/cockroach-labs/cockroachdb/14.0.2/values.yaml @@ -0,0 +1,600 @@ +# Generated file, DO NOT EDIT. Source: build/templates/values.yaml +# Overrides the chart name against the label "app.kubernetes.io/name: " placed on every resource this chart creates. +nameOverride: "" + +# Override the resource names created by this chart which originally is generated using release and chart name. +fullnameOverride: "" + +image: + repository: cockroachdb/cockroach + tag: v24.2.0 + pullPolicy: IfNotPresent + credentials: {} + # registry: docker.io + # username: john_doe + # password: changeme + + +# Additional labels to apply to all Kubernetes resources created by this chart. +labels: {} + # app.kubernetes.io/part-of: my-app + + +# Cluster's default DNS domain. +# You should overwrite it if you're using a different one, +# otherwise CockroachDB nodes discovery won't work. +clusterDomain: cluster.local + + +conf: + # An ordered list of CockroachDB node attributes. + # Attributes are arbitrary strings specifying machine capabilities. + # Machine capabilities might include specialized hardware or number of cores + # (e.g. "gpu", "x16c"). + attrs: [] + # - x16c + # - gpu + + # Total size in bytes for caches, shared evenly if there are multiple + # storage devices. Size suffixes are supported (e.g. `1GB` and `1GiB`). + # A percentage of physical memory can also be specified (e.g. `.25`). + cache: 25% + + # Sets a name to verify the identity of a cluster. + # The value must match between all nodes specified via `conf.join`. + # This can be used as an additional verification when either the node or + # cluster, or both, have not yet been initialized and do not yet know their + # cluster ID. + # To introduce a cluster name into an already-initialized cluster, pair this + # option with `conf.disable-cluster-name-verification: yes`. + cluster-name: "" + + # Tell the server to ignore `conf.cluster-name` mismatches. + # This is meant for use when opting an existing cluster into starting to use + # cluster name verification, or when changing the cluster name. + # The cluster should be restarted once with `conf.cluster-name` and + # `conf.disable-cluster-name-verification: yes` combined, and once all nodes + # have been updated to know the new cluster name, the cluster can be restarted + # again with `conf.disable-cluster-name-verification: no`. + # This option has no effect if `conf.cluster-name` is not specified. + disable-cluster-name-verification: false + + # The addresses for connecting a CockroachDB nodes to an existing cluster. + # If you are deploying a second CockroachDB instance that should join a first + # one, use the below list to join to the existing instance. + # Each item in the array should be a FQDN (and port if needed) resolvable by + # new Pods. + join: [] + + # New logging configuration. + log: + enabled: false + # https://www.cockroachlabs.com/docs/v21.1/configure-logs + config: {} + # file-defaults: + # dir: /custom/dir/path/ + # fluent-defaults: + # format: json-fluent + # sinks: + # stderr: + # channels: [DEV] + + # Logs at or above this threshold to STDERR. Ignored when "log" is enabled + logtostderr: INFO + + # Maximum storage capacity available to store temporary disk-based data for + # SQL queries that exceed the memory budget (e.g. join, sorts, etc are + # sometimes able to spill intermediate results to disk). + # Accepts numbers interpreted as bytes, size suffixes (e.g. `32GB` and + # `32GiB`) or a percentage of disk size (e.g. `10%`). + # The location of the temporary files is within the first store dir. + # If expressed as a percentage, `max-disk-temp-storage` is interpreted + # relative to the size of the storage device on which the first store is + # placed. The temp space usage is never counted towards any store usage + # (although it does share the device with the first store) so, when + # configuring this, make sure that the size of this temp storage plus the size + # of the first store don't exceed the capacity of the storage device. + # If the first store is an in-memory one (i.e. `type=mem`), then this + # temporary "disk" data is also kept in-memory. + # A percentage value is interpreted as a percentage of the available internal + # memory. + # max-disk-temp-storage: 0GB + + # Maximum allowed clock offset for the cluster. If observed clock offsets + # exceed this limit, servers will crash to minimize the likelihood of + # reading inconsistent data. Increasing this value will increase the time + # to recovery of failures as well as the frequency of uncertainty-based + # read restarts. + # Note, that this value must be the same on all nodes in the cluster. + # In order to change it, all nodes in the cluster must be stopped + # simultaneously and restarted with the new value. + # max-offset: 500ms + + # Maximum memory capacity available to store temporary data for SQL clients, + # including prepared queries and intermediate data rows during query + # execution. Accepts numbers interpreted as bytes, size suffixes + # (e.g. `1GB` and `1GiB`) or a percentage of physical memory (e.g. `.25`). + max-sql-memory: 25% + + # An ordered, comma-separated list of key-value pairs that describe the + # topography of the machine. Topography might include country, datacenter + # or rack designations. Data is automatically replicated to maximize + # diversities of each tier. The order of tiers is used to determine + # the priority of the diversity, so the more inclusive localities like + # country should come before less inclusive localities like datacenter. + # The tiers and order must be the same on all nodes. Including more tiers + # is better than including fewer. For example: + # locality: country=us,region=us-west,datacenter=us-west-1b,rack=12 + # locality: country=ca,region=ca-east,datacenter=ca-east-2,rack=4 + # locality: planet=earth,province=manitoba,colo=secondary,power=3 + locality: "" + + # Run CockroachDB instances in standalone mode with replication disabled + # (replication factor = 1). + # Enabling this option makes the following values to be ignored: + # - `conf.cluster-name` + # - `conf.disable-cluster-name-verification` + # - `conf.join` + # + # WARNING: Enabling this option makes each deployed Pod as a STANDALONE + # CockroachDB instance, so the StatefulSet does NOT FORM A CLUSTER. + # Don't use this option for production deployments unless you clearly + # understand what you're doing. + # Usually, this option is intended to be used in conjunction with + # `statefulset.replicas: 1` for temporary one-time deployments (like + # running E2E tests, for example). + single-node: false + + # If non-empty, create a SQL audit log in the specified directory. + sql-audit-dir: "" + + # CockroachDB's port to listen to inter-communications and client connections. + port: 26257 + + # CockroachDB's port to listen to HTTP requests. + http-port: 8080 + + # CockroachDB's data mount path. + path: cockroach-data + + # CockroachDB's storage configuration https://www.cockroachlabs.com/docs/v21.1/cockroach-start.html#storage + # Uses --store flag + store: + enabled: false + # Should be empty or 'mem' + type: + # Required for type=mem. If type and size is empty - storage.persistentVolume.size is used + size: + # Arbitrary strings, separated by colons, specifying disk type or capability + attrs: + +statefulset: + replicas: 3 + updateStrategy: + type: RollingUpdate + podManagementPolicy: Parallel + budget: + maxUnavailable: 1 + + # List of additional command-line arguments you want to pass to the + # `cockroach start` command. + args: [] + # - --disable-cluster-name-verification + + # List of extra environment variables to pass into container + env: [] + # - name: COCKROACH_ENGINE_MAX_SYNC_DURATION + # value: "24h" + + # List of Secrets names in the same Namespace as the CockroachDB cluster, + # which shall be mounted into `/etc/cockroach/secrets/` for every cluster + # member. + secretMounts: [] + + # Additional labels to apply to this StatefulSet and all its Pods. + labels: + app.kubernetes.io/component: cockroachdb + + # Additional annotations to apply to the Pods of this StatefulSet. + annotations: {} + + # Affinity rules for scheduling Pods of this StatefulSet on Nodes. + # https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#node-affinity + nodeAffinity: {} + # Inter-Pod Affinity rules for scheduling Pods of this StatefulSet. + # https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#inter-pod-affinity-and-anti-affinity + podAffinity: {} + # Anti-affinity rules for scheduling Pods of this StatefulSet. + # https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#inter-pod-affinity-and-anti-affinity + # You may either toggle options below for default anti-affinity rules, + # or specify the whole set of anti-affinity rules instead of them. + podAntiAffinity: + # The topologyKey to be used. + # Can be used to spread across different nodes, AZs, regions etc. + topologyKey: kubernetes.io/hostname + # Type of anti-affinity rules: either `soft`, `hard` or empty value (which + # disables anti-affinity rules). + type: soft + # Weight for `soft` anti-affinity rules. + # Does not apply for other anti-affinity types. + weight: 100 + + # Node selection constraints for scheduling Pods of this StatefulSet. + # https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#nodeselector + nodeSelector: {} + + # PriorityClassName given to Pods of this StatefulSet + # https://kubernetes.io/docs/concepts/configuration/pod-priority-preemption/#priorityclass + priorityClassName: "" + + # Taints to be tolerated by Pods of this StatefulSet. + # https://kubernetes.io/docs/concepts/configuration/taint-and-toleration/ + tolerations: [] + + # https://kubernetes.io/docs/concepts/workloads/pods/pod-topology-spread-constraints/ + topologySpreadConstraints: + maxSkew: 1 + topologyKey: topology.kubernetes.io/zone + whenUnsatisfiable: ScheduleAnyway + + # Uncomment the following resources definitions or pass them from + # command line to control the CPU and memory resources allocated + # by Pods of this StatefulSet. + resources: {} + # limits: + # cpu: 100m + # memory: 512Mi + # requests: + # cpu: 100m + # memory: 512Mi + + # Custom Liveness probe + # https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/#define-a-liveness-http-request + customLivenessProbe: {} + # httpGet: + # path: /health + # port: http + # scheme: HTTPS + # initialDelaySeconds: 30 + # periodSeconds: 5 + + # Custom Rediness probe + # https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/#define-readiness-probes + customReadinessProbe: {} + # httpGet: + # path: /health + # port: http + # scheme: HTTPS + # initialDelaySeconds: 30 + # periodSeconds: 5 + + securityContext: + enabled: true + + serviceAccount: + # Specifies whether this ServiceAccount should be created. + create: true + # The name of this ServiceAccount to use. + # If not set and `create` is `true`, then service account is auto-generated. + # If not set and `create` is `false`, then it uses default service account. + name: "" + # Additional serviceAccount annotations (e.g. for attaching AWS IAM roles to pods) + annotations: {} + +service: + ports: + # You can set a different external and internal gRPC ports and their name. + grpc: + external: + port: 26257 + name: grpc + # If the port number is different than `external.port`, then it will be + # named as `internal.name` in Service. + internal: + port: 26257 + # If using Istio set it to `cockroach`. + name: grpc-internal + http: + port: 8080 + name: http + + # This Service is meant to be used by clients of the database. + # It exposes a ClusterIP that will automatically load balance connections + # to the different database Pods. + public: + type: ClusterIP + # Additional labels to apply to this Service. + labels: + app.kubernetes.io/component: cockroachdb + # Additional annotations to apply to this Service. + annotations: {} + + # This service only exists to create DNS entries for each pod in + # the StatefulSet such that they can resolve each other's IP addresses. + # It does not create a load-balanced ClusterIP and should not be used directly + # by clients in most circumstances. + discovery: + # Additional labels to apply to this Service. + labels: + app.kubernetes.io/component: cockroachdb + # Additional annotations to apply to this Service. + annotations: {} + +# CockroachDB's ingress for web ui. +ingress: + enabled: false + labels: {} + annotations: {} + # kubernetes.io/ingress.class: nginx + # cert-manager.io/cluster-issuer: letsencrypt + paths: [/] + hosts: [] + # - cockroachlabs.com + tls: [] + # - hosts: [cockroachlabs.com] + # secretName: cockroachlabs-tls + +prometheus: + enabled: true + +securityContext: + enabled: true + +# CockroachDB's Prometheus operator ServiceMonitor support +serviceMonitor: + enabled: false + labels: {} + annotations: {} + interval: 10s + # scrapeTimeout: 10s + # Limits the ServiceMonitor to the current namespace if set to `true`. + namespaced: false + + # tlsConfig: TLS configuration to use when scraping the endpoint. + # Of type: https://github.com/coreos/prometheus-operator/blob/main/Documentation/api.md#tlsconfig + tlsConfig: {} + +# CockroachDB's data persistence. +# If neither `persistentVolume` nor `hostPath` is used, then data will be +# persisted in ad-hoc `emptyDir`. +storage: + # Absolute path on host to store CockroachDB's data. + # If not specified, then `emptyDir` will be used instead. + # If specified, but `persistentVolume.enabled` is `true`, then has no effect. + hostPath: "" + + # If `enabled` is `true` then a PersistentVolumeClaim will be created and + # used to store CockroachDB's data, otherwise `hostPath` is used. + persistentVolume: + enabled: true + + size: 100Gi + + # If defined, then `storageClassName: `. + # If set to "-", then `storageClassName: ""`, which disables dynamic + # provisioning. + # If undefined or empty (default), then no `storageClassName` spec is set, + # so the default provisioner will be chosen (gp2 on AWS, standard on + # GKE, AWS & OpenStack). + storageClass: "" + + # Additional labels to apply to the created PersistentVolumeClaims. + labels: {} + # Additional annotations to apply to the created PersistentVolumeClaims. + annotations: {} + + +# Kubernetes Job which initializes multi-node CockroachDB cluster. +# It's not created if `statefulset.replicas` is `1`. +init: + # Additional labels to apply to this Job and its Pod. + labels: + app.kubernetes.io/component: init + + # Additional annotations to apply to this Job. + jobAnnotations: {} + + # Additional annotations to apply to the Pod of this Job. + annotations: {} + + # Affinity rules for scheduling the Pod of this Job. + # https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#node-affinity + affinity: {} + + # Node selection constraints for scheduling the Pod of this Job. + # https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#nodeselector + nodeSelector: {} + + # Taints to be tolerated by the Pod of this Job. + # https://kubernetes.io/docs/concepts/configuration/taint-and-toleration/ + tolerations: [] + + # The init Pod runs at cluster creation to initialize CockroachDB. It finishes + # quickly and doesn't continue to consume resources in the Kubernetes + # cluster. Normally, you should leave this section commented out, but if your + # Kubernetes cluster uses Resource Quotas and requires all pods to specify + # resource requests or limits, you can set those here. + resources: {} + # requests: + # cpu: "10m" + # memory: "128Mi" + # limits: + # cpu: "10m" + # memory: "128Mi" + + securityContext: + enabled: true + + # Setup Physical Cluster Replication (PCR) between primary and standby cluster. + # If isPrimary is set to true, the CockroachDB cluster created is the primary cluster. + # If isPrimary is set to false, the CockroachDB cluster created is the standby cluster. + pcr: + enabled: false + # isPrimary: true + + provisioning: + enabled: false + # https://www.cockroachlabs.com/docs/stable/cluster-settings.html + clusterSettings: + # cluster.organization: "'FooCorp - Local Testing'" + # enterprise.license: "'xxxxx'" + users: [] + # - name: + # password: + # # https://www.cockroachlabs.com/docs/stable/create-user.html#parameters + # options: [LOGIN] + databases: [] + # - name: + # # https://www.cockroachlabs.com/docs/stable/create-database.html#parameters + # options: [encoding='utf-8'] + # owners: [] + # # https://www.cockroachlabs.com/docs/stable/grant.html#parameters + # owners_with_grant_option: [] + # # Backup schedules are not idemponent for now and will fail on next run + # # https://github.com/cockroachdb/cockroach/issues/57892 + # backup: + # into: s3:// + # # Enterprise-only option (revision_history) + # # https://www.cockroachlabs.com/docs/stable/create-schedule-for-backup.html#backup-options + # options: [revision_history] + # recurring: '@always' + # # Enterprise-only feature. Remove this value to use `FULL BACKUP ALWAYS` + # fullBackup: '@daily' + # schedule: + # # https://www.cockroachlabs.com/docs/stable/create-schedule-for-backup.html#schedule-options + # options: [first_run = 'now'] + + +# Whether to run securely using TLS certificates. +tls: + enabled: true + copyCerts: + image: busybox + certs: + # Bring your own certs scenario. If provided, tls.init section will be ignored. + provided: false + # Secret name for the client root cert. + clientRootSecret: cockroachdb-root + # Secret name for node cert. + nodeSecret: cockroachdb-node + # Secret name for CA cert + caSecret: cockroach-ca + # Enable if the secret is a dedicated TLS. + # TLS secrets are created by cert-mananger, for example. + tlsSecret: false + # Enable if the you want cockroach db to create its own certificates + selfSigner: + # If set, the cockroach db will generate its own certificates + enabled: true + # Run selfSigner as non-root + securityContext: + enabled: true + # If set, the user should provide the CA certificate to sign other certificates. + caProvided: false + # It holds the name of the secret with caCerts. If caProvided is set, this can not be empty. + caSecret: "" + # Minimum Certificate duration for all the certificates, all certs duration will be validated against this. + minimumCertDuration: 624h + # Duration of CA certificates in hour + caCertDuration: 43800h + # Expiry window of CA certificates means a window before actual expiry in which CA certs should be rotated. + caCertExpiryWindow: 648h + # Duration of Client certificates in hour + clientCertDuration: 672h + # Expiry window of client certificates means a window before actual expiry in which client certs should be rotated. + clientCertExpiryWindow: 48h + # Duration of node certificates in hour + nodeCertDuration: 8760h + # Expiry window of node certificates means a window before actual expiry in which node certs should be rotated. + nodeCertExpiryWindow: 168h + # If set, the cockroachdb cert selfSigner will rotate the certificates before expiry. + rotateCerts: true + # Wait time for each cockroachdb replica to become ready once it comes in running state. Only considered when rotateCerts is set to true + readinessWait: 30s + # Wait time for each cockroachdb replica to get to running state. Only considered when rotateCerts is set to true + podUpdateTimeout: 2m + # ServiceAccount annotations for selfSigner jobs (e.g. for attaching AWS IAM roles to pods) + svcAccountAnnotations: {} + + # Use cert-manager to issue certificates for mTLS. + certManager: false + # Specify an Issuer or a ClusterIssuer to use, when issuing + # node and client certificates. The values correspond to the + # issuerRef specified in the certificate. + certManagerIssuer: + group: cert-manager.io + kind: Issuer + name: cockroachdb + # Make it false when you are providing your own CA issuer + isSelfSignedIssuer: true + # Duration of CA certificates in hour + caCertDuration: 43800h + # Expiry window of CA certificates means a window before actual expiry in which CA certs should be rotated. + caCertExpiryWindow: 648h + # Duration of Client certificates in hours + clientCertDuration: 672h + # Expiry window of client certificates means a window before actual expiry in which client certs should be rotated. + clientCertExpiryWindow: 48h + # Duration of node certificates in hours + nodeCertDuration: 8760h + # Expiry window of node certificates means a window before actual expiry in which node certs should be rotated. + nodeCertExpiryWindow: 168h + + selfSigner: + # Additional labels to apply to the Pod of this Job. + labels: {} + + # Additional annotations to apply to the Pod of this Job. + annotations: {} + + # Affinity rules for scheduling the Pod of this Job. + # https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#node-affinity + affinity: {} + + # Node selection constraints for scheduling the Pod of this Job. + # https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#nodeselector + nodeSelector: {} + + # Taints to be tolerated by the Pod of this Job. + # https://kubernetes.io/docs/concepts/configuration/taint-and-toleration/ + tolerations: [] + + # Image Placeholder for the selfSigner utility. This will be changed once the CI workflows for the image is in place. + image: + repository: cockroachlabs-helm-charts/cockroach-self-signer-cert + tag: "1.5" + pullPolicy: IfNotPresent + credentials: {} + registry: gcr.io + # username: john_doe + # password: changeme + +networkPolicy: + enabled: false + + ingress: + # List of sources which should be able to access the CockroachDB Pods via + # gRPC port. Items in this list are combined using a logical OR operation. + # Rules for allowing inter-communication are applied automatically. + # If empty, then connections from any Pod is allowed. + grpc: [] + # - podSelector: + # matchLabels: + # app.kubernetes.io/name: my-app-django + # app.kubernetes.io/instance: my-app + + # List of sources which should be able to access the CockroachDB Pods via + # HTTP port. Items in this list are combined using a logical OR operation. + # If empty, then connections from any Pod is allowed. + http: [] + # - namespaceSelector: + # matchLabels: + # project: my-project + +# To put the admin interface behind Identity Aware Proxy (IAP) on Google Cloud Platform +# make sure to set ingress.paths: ['/*'] +iap: + enabled: false + # Create Google Cloud OAuth credentials and set client id and secret + # clientId: + # clientSecret: diff --git a/charts/codefresh/cf-runtime/6.4.0/.helmignore b/charts/codefresh/cf-runtime/6.4.0/.helmignore new file mode 100644 index 000000000..bc71d4240 --- /dev/null +++ b/charts/codefresh/cf-runtime/6.4.0/.helmignore @@ -0,0 +1,3 @@ +tests/ +.ci/ +test-values/ \ No newline at end of file diff --git a/charts/codefresh/cf-runtime/6.4.0/Chart.yaml b/charts/codefresh/cf-runtime/6.4.0/Chart.yaml new file mode 100644 index 000000000..9e45f6a3b --- /dev/null +++ b/charts/codefresh/cf-runtime/6.4.0/Chart.yaml @@ -0,0 +1,28 @@ +annotations: + artifacthub.io/changes: | + - kind: added + description: "Added support for terminationGracePeriodSeconds configuration for dind and engine" + artifacthub.io/containsSecurityUpdates: "false" + 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 +dependencies: +- name: cf-common + repository: file://./charts/cf-common + version: 0.16.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: 6.4.0 diff --git a/charts/codefresh/cf-runtime/6.4.0/README.md b/charts/codefresh/cf-runtime/6.4.0/README.md new file mode 100644 index 000000000..a3b9ea9c4 --- /dev/null +++ b/charts/codefresh/cf-runtime/6.4.0/README.md @@ -0,0 +1,1230 @@ +## Codefresh Runner + +![Version: 6.4.0](https://img.shields.io/badge/Version-6.4.0-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) + - [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) +- [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 + +### 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 +``` + +## 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), change dind image tag: + +`values.yaml` +```yaml +runtime: + dind: + image: + tag: rootless +``` + +### 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.16.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 | `{"registry":"quay.io","repository":"codefresh/cf-app-proxy","tag":"0.0.47"}` | 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 | `{"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 | `{"registry":"quay.io","repository":"codefresh/cf-k8s-agent","tag":"1.3.17"}` | 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 | `{"registry":"quay.io","repository":"codefresh/venona","tag":"1.10.2"}` | Set image | +| runner.init | object | `{"image":{"registry":"quay.io","repository":"codefresh/cli","tag":"0.85.0-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":{"registry":"quay.io","repository":"codefresh/codefresh-shell","tag":"0.0.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":{},"env":{"DOCKER_ENABLE_DEPRECATED_PULL_SCHEMA_1_IMAGE":true},"image":{"pullPolicy":"IfNotPresent","registry":"quay.io","repository":"codefresh/dind","tag":"26.1.4-1.28.7"},"nodeSelector":{},"podAnnotations":{},"podLabels":{},"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":{}}` | Parameters for DinD (docker-in-docker) pod (aka "runtime" pod). | +| runtime.dind.affinity | object | `{}` | Set affinity | +| runtime.dind.env | object | `{"DOCKER_ENABLE_DEPRECATED_PULL_SCHEMA_1_IMAGE":true}` | Set additional env vars. | +| runtime.dind.image | object | `{"pullPolicy":"IfNotPresent","registry":"quay.io","repository":"codefresh/dind","tag":"26.1.4-1.28.7"}` | 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.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},"image":{"pullPolicy":"IfNotPresent","registry":"quay.io","repository":"codefresh/engine","tag":"1.174.12"},"nodeSelector":{},"podAnnotations":{},"podLabels":{},"resources":{"limits":{"cpu":"1000m","memory":"2048Mi"},"requests":{"cpu":"100m","memory":"128Mi"}},"runtimeImages":{"COMPOSE_IMAGE":"quay.io/codefresh/compose:v2.28.1-1.5.0","CONTAINER_LOGGER_IMAGE":"quay.io/codefresh/cf-container-logger:1.11.6","COSIGN_IMAGE_SIGNER_IMAGE":"quay.io/codefresh/cf-cosign-image-signer:2.4.0-cf.2","CR_6177_FIXER":"quay.io/codefresh/alpine:edge","DOCKER_BUILDER_IMAGE":"quay.io/codefresh/cf-docker-builder:1.3.13","DOCKER_PULLER_IMAGE":"quay.io/codefresh/cf-docker-puller:8.0.17","DOCKER_PUSHER_IMAGE":"quay.io/codefresh/cf-docker-pusher:6.0.16","DOCKER_TAG_PUSHER_IMAGE":"quay.io/codefresh/cf-docker-tag-pusher:1.3.14","FS_OPS_IMAGE":"quay.io/codefresh/fs-ops:1.2.3","GC_BUILDER_IMAGE":"quay.io/codefresh/cf-gc-builder:0.5.3","GIT_CLONE_IMAGE":"quay.io/codefresh/cf-git-cloner:10.1.28","KUBE_DEPLOY":"quay.io/codefresh/cf-deploy-kubernetes:16.1.11","PIPELINE_DEBUGGER_IMAGE":"quay.io/codefresh/cf-debugger:1.3.0","TEMPLATE_ENGINE":"quay.io/codefresh/pikolo:0.14.1"},"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}` | 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.image | object | `{"pullPolicy":"IfNotPresent","registry":"quay.io","repository":"codefresh/engine","tag":"1.174.12"}` | 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 | `{"registry":"quay.io","repository":"codefresh/dind-volume-provisioner","tag":"1.35.0"}` | 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/6.4.0/README.md.gotmpl b/charts/codefresh/cf-runtime/6.4.0/README.md.gotmpl new file mode 100644 index 000000000..96e5ca574 --- /dev/null +++ b/charts/codefresh/cf-runtime/6.4.0/README.md.gotmpl @@ -0,0 +1,1007 @@ +## 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) + - [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) +- [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 + +### 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 +``` + +## 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), change dind image tag: + +`values.yaml` +```yaml +runtime: + dind: + image: + tag: rootless +``` + +### 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/6.4.0/files/cleanup-runtime.sh b/charts/codefresh/cf-runtime/6.4.0/files/cleanup-runtime.sh new file mode 100644 index 000000000..c1fc5f368 --- /dev/null +++ b/charts/codefresh/cf-runtime/6.4.0/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/6.4.0/files/configure-dind-certs.sh b/charts/codefresh/cf-runtime/6.4.0/files/configure-dind-certs.sh new file mode 100644 index 000000000..a1092eb1e --- /dev/null +++ b/charts/codefresh/cf-runtime/6.4.0/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/6.4.0/files/init-runtime.sh b/charts/codefresh/cf-runtime/6.4.0/files/init-runtime.sh new file mode 100644 index 000000000..eb3488af1 --- /dev/null +++ b/charts/codefresh/cf-runtime/6.4.0/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/6.4.0/templates/_components/app-proxy/_deployment.yaml b/charts/codefresh/cf-runtime/6.4.0/templates/_components/app-proxy/_deployment.yaml new file mode 100644 index 000000000..26f3576b7 --- /dev/null +++ b/charts/codefresh/cf-runtime/6.4.0/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/6.4.0/templates/_components/app-proxy/_env-vars.yaml b/charts/codefresh/cf-runtime/6.4.0/templates/_components/app-proxy/_env-vars.yaml new file mode 100644 index 000000000..c9b9a0e36 --- /dev/null +++ b/charts/codefresh/cf-runtime/6.4.0/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/6.4.0/templates/_components/app-proxy/_helpers.tpl b/charts/codefresh/cf-runtime/6.4.0/templates/_components/app-proxy/_helpers.tpl new file mode 100644 index 000000000..2d4272ca9 --- /dev/null +++ b/charts/codefresh/cf-runtime/6.4.0/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/6.4.0/templates/_components/app-proxy/_ingress.yaml b/charts/codefresh/cf-runtime/6.4.0/templates/_components/app-proxy/_ingress.yaml new file mode 100644 index 000000000..d7860b363 --- /dev/null +++ b/charts/codefresh/cf-runtime/6.4.0/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.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/6.4.0/templates/_components/app-proxy/_rbac.yaml b/charts/codefresh/cf-runtime/6.4.0/templates/_components/app-proxy/_rbac.yaml new file mode 100644 index 000000000..87bd869ba --- /dev/null +++ b/charts/codefresh/cf-runtime/6.4.0/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/6.4.0/templates/_components/app-proxy/_service.yaml b/charts/codefresh/cf-runtime/6.4.0/templates/_components/app-proxy/_service.yaml new file mode 100644 index 000000000..4c3a93bf2 --- /dev/null +++ b/charts/codefresh/cf-runtime/6.4.0/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/6.4.0/templates/_components/event-exporter/_deployment.yaml b/charts/codefresh/cf-runtime/6.4.0/templates/_components/event-exporter/_deployment.yaml new file mode 100644 index 000000000..62588b4d3 --- /dev/null +++ b/charts/codefresh/cf-runtime/6.4.0/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/6.4.0/templates/_components/event-exporter/_env-vars.yaml b/charts/codefresh/cf-runtime/6.4.0/templates/_components/event-exporter/_env-vars.yaml new file mode 100644 index 000000000..d28d0776f --- /dev/null +++ b/charts/codefresh/cf-runtime/6.4.0/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/6.4.0/templates/_components/event-exporter/_helpers.tpl b/charts/codefresh/cf-runtime/6.4.0/templates/_components/event-exporter/_helpers.tpl new file mode 100644 index 000000000..5b8b5eff7 --- /dev/null +++ b/charts/codefresh/cf-runtime/6.4.0/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/6.4.0/templates/_components/event-exporter/_rbac.yaml b/charts/codefresh/cf-runtime/6.4.0/templates/_components/event-exporter/_rbac.yaml new file mode 100644 index 000000000..69d7b6b2f --- /dev/null +++ b/charts/codefresh/cf-runtime/6.4.0/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/6.4.0/templates/_components/event-exporter/_service.yaml b/charts/codefresh/cf-runtime/6.4.0/templates/_components/event-exporter/_service.yaml new file mode 100644 index 000000000..6fa29ec1a --- /dev/null +++ b/charts/codefresh/cf-runtime/6.4.0/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/6.4.0/templates/_components/event-exporter/_serviceMontor.yaml b/charts/codefresh/cf-runtime/6.4.0/templates/_components/event-exporter/_serviceMontor.yaml new file mode 100644 index 000000000..6092443f0 --- /dev/null +++ b/charts/codefresh/cf-runtime/6.4.0/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/6.4.0/templates/_components/monitor/_deployment.yaml b/charts/codefresh/cf-runtime/6.4.0/templates/_components/monitor/_deployment.yaml new file mode 100644 index 000000000..7efa6557b --- /dev/null +++ b/charts/codefresh/cf-runtime/6.4.0/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/6.4.0/templates/_components/monitor/_env-vars.yaml b/charts/codefresh/cf-runtime/6.4.0/templates/_components/monitor/_env-vars.yaml new file mode 100644 index 000000000..f58c7fa25 --- /dev/null +++ b/charts/codefresh/cf-runtime/6.4.0/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/6.4.0/templates/_components/monitor/_helpers.tpl b/charts/codefresh/cf-runtime/6.4.0/templates/_components/monitor/_helpers.tpl new file mode 100644 index 000000000..71cc1c027 --- /dev/null +++ b/charts/codefresh/cf-runtime/6.4.0/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/6.4.0/templates/_components/monitor/_rbac.yaml b/charts/codefresh/cf-runtime/6.4.0/templates/_components/monitor/_rbac.yaml new file mode 100644 index 000000000..88204796a --- /dev/null +++ b/charts/codefresh/cf-runtime/6.4.0/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/6.4.0/templates/_components/monitor/_service.yaml b/charts/codefresh/cf-runtime/6.4.0/templates/_components/monitor/_service.yaml new file mode 100644 index 000000000..f6ae9bb0f --- /dev/null +++ b/charts/codefresh/cf-runtime/6.4.0/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/6.4.0/templates/_components/runner/_deployment.yaml b/charts/codefresh/cf-runtime/6.4.0/templates/_components/runner/_deployment.yaml new file mode 100644 index 000000000..e1fb9439a --- /dev/null +++ b/charts/codefresh/cf-runtime/6.4.0/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/6.4.0/templates/_components/runner/_helpers.tpl b/charts/codefresh/cf-runtime/6.4.0/templates/_components/runner/_helpers.tpl new file mode 100644 index 000000000..2608cb67e --- /dev/null +++ b/charts/codefresh/cf-runtime/6.4.0/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/6.4.0/templates/_components/runner/_rbac.yaml b/charts/codefresh/cf-runtime/6.4.0/templates/_components/runner/_rbac.yaml new file mode 100644 index 000000000..d95b958d5 --- /dev/null +++ b/charts/codefresh/cf-runtime/6.4.0/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/6.4.0/templates/_components/runner/environment-variables/_init-container.yaml b/charts/codefresh/cf-runtime/6.4.0/templates/_components/runner/environment-variables/_init-container.yaml new file mode 100644 index 000000000..6dda110f7 --- /dev/null +++ b/charts/codefresh/cf-runtime/6.4.0/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/6.4.0/templates/_components/runner/environment-variables/_main-container.yaml b/charts/codefresh/cf-runtime/6.4.0/templates/_components/runner/environment-variables/_main-container.yaml new file mode 100644 index 000000000..4d3f0304e --- /dev/null +++ b/charts/codefresh/cf-runtime/6.4.0/templates/_components/runner/environment-variables/_main-container.yaml @@ -0,0 +1,28 @@ +{{- 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 }} +{{- 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/6.4.0/templates/_components/runner/environment-variables/_sidecar-container.yaml b/charts/codefresh/cf-runtime/6.4.0/templates/_components/runner/environment-variables/_sidecar-container.yaml new file mode 100644 index 000000000..3adcbe5d4 --- /dev/null +++ b/charts/codefresh/cf-runtime/6.4.0/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/6.4.0/templates/_components/volume-provisioner/_cronjob.yaml b/charts/codefresh/cf-runtime/6.4.0/templates/_components/volume-provisioner/_cronjob.yaml new file mode 100644 index 000000000..20bd2d56e --- /dev/null +++ b/charts/codefresh/cf-runtime/6.4.0/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/6.4.0/templates/_components/volume-provisioner/_daemonset.yaml b/charts/codefresh/cf-runtime/6.4.0/templates/_components/volume-provisioner/_daemonset.yaml new file mode 100644 index 000000000..cb463231d --- /dev/null +++ b/charts/codefresh/cf-runtime/6.4.0/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.podSecurityContext.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 -}} \ No newline at end of file diff --git a/charts/codefresh/cf-runtime/6.4.0/templates/_components/volume-provisioner/_deployment.yaml b/charts/codefresh/cf-runtime/6.4.0/templates/_components/volume-provisioner/_deployment.yaml new file mode 100644 index 000000000..9252b4520 --- /dev/null +++ b/charts/codefresh/cf-runtime/6.4.0/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/6.4.0/templates/_components/volume-provisioner/_env-vars.yaml b/charts/codefresh/cf-runtime/6.4.0/templates/_components/volume-provisioner/_env-vars.yaml new file mode 100644 index 000000000..e1f5dfe60 --- /dev/null +++ b/charts/codefresh/cf-runtime/6.4.0/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/6.4.0/templates/_components/volume-provisioner/_helpers.tpl b/charts/codefresh/cf-runtime/6.4.0/templates/_components/volume-provisioner/_helpers.tpl new file mode 100644 index 000000000..e3d3a0d3f --- /dev/null +++ b/charts/codefresh/cf-runtime/6.4.0/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/6.4.0/templates/_components/volume-provisioner/_rbac.yaml b/charts/codefresh/cf-runtime/6.4.0/templates/_components/volume-provisioner/_rbac.yaml new file mode 100644 index 000000000..fbcbc684f --- /dev/null +++ b/charts/codefresh/cf-runtime/6.4.0/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/6.4.0/templates/_components/volume-provisioner/_secret.yaml b/charts/codefresh/cf-runtime/6.4.0/templates/_components/volume-provisioner/_secret.yaml new file mode 100644 index 000000000..f361a7991 --- /dev/null +++ b/charts/codefresh/cf-runtime/6.4.0/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/6.4.0/templates/_components/volume-provisioner/_storageclass.yaml b/charts/codefresh/cf-runtime/6.4.0/templates/_components/volume-provisioner/_storageclass.yaml new file mode 100644 index 000000000..62e910c87 --- /dev/null +++ b/charts/codefresh/cf-runtime/6.4.0/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/6.4.0/templates/_helpers.tpl b/charts/codefresh/cf-runtime/6.4.0/templates/_helpers.tpl new file mode 100644 index 000000000..72f44e36a --- /dev/null +++ b/charts/codefresh/cf-runtime/6.4.0/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/6.4.0/templates/app-proxy/deployment.yaml b/charts/codefresh/cf-runtime/6.4.0/templates/app-proxy/deployment.yaml new file mode 100644 index 000000000..90341b305 --- /dev/null +++ b/charts/codefresh/cf-runtime/6.4.0/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/6.4.0/templates/app-proxy/ingress.yaml b/charts/codefresh/cf-runtime/6.4.0/templates/app-proxy/ingress.yaml new file mode 100644 index 000000000..56ab5e95e --- /dev/null +++ b/charts/codefresh/cf-runtime/6.4.0/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/6.4.0/templates/app-proxy/rbac.yaml b/charts/codefresh/cf-runtime/6.4.0/templates/app-proxy/rbac.yaml new file mode 100644 index 000000000..4db87dcb4 --- /dev/null +++ b/charts/codefresh/cf-runtime/6.4.0/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/6.4.0/templates/app-proxy/service.yaml b/charts/codefresh/cf-runtime/6.4.0/templates/app-proxy/service.yaml new file mode 100644 index 000000000..0b9d85ec0 --- /dev/null +++ b/charts/codefresh/cf-runtime/6.4.0/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/6.4.0/templates/event-exporter/deployment.yaml b/charts/codefresh/cf-runtime/6.4.0/templates/event-exporter/deployment.yaml new file mode 100644 index 000000000..494288240 --- /dev/null +++ b/charts/codefresh/cf-runtime/6.4.0/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/6.4.0/templates/event-exporter/rbac.yaml b/charts/codefresh/cf-runtime/6.4.0/templates/event-exporter/rbac.yaml new file mode 100644 index 000000000..6a9bf5c65 --- /dev/null +++ b/charts/codefresh/cf-runtime/6.4.0/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/6.4.0/templates/event-exporter/service.yaml b/charts/codefresh/cf-runtime/6.4.0/templates/event-exporter/service.yaml new file mode 100644 index 000000000..c5d856dfe --- /dev/null +++ b/charts/codefresh/cf-runtime/6.4.0/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/6.4.0/templates/extra/extra-resources.yaml b/charts/codefresh/cf-runtime/6.4.0/templates/extra/extra-resources.yaml new file mode 100644 index 000000000..1a9777c64 --- /dev/null +++ b/charts/codefresh/cf-runtime/6.4.0/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/6.4.0/templates/extra/runtime-images-cm.yaml b/charts/codefresh/cf-runtime/6.4.0/templates/extra/runtime-images-cm.yaml new file mode 100644 index 000000000..f269c84b2 --- /dev/null +++ b/charts/codefresh/cf-runtime/6.4.0/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/6.4.0/templates/hooks/post-install/cm-update-runtime.yaml b/charts/codefresh/cf-runtime/6.4.0/templates/hooks/post-install/cm-update-runtime.yaml new file mode 100644 index 000000000..46a306c56 --- /dev/null +++ b/charts/codefresh/cf-runtime/6.4.0/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/6.4.0/templates/hooks/post-install/job-gencerts-dind.yaml b/charts/codefresh/cf-runtime/6.4.0/templates/hooks/post-install/job-gencerts-dind.yaml new file mode 100644 index 000000000..4a08a229c --- /dev/null +++ b/charts/codefresh/cf-runtime/6.4.0/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/6.4.0/templates/hooks/post-install/job-update-runtime.yaml b/charts/codefresh/cf-runtime/6.4.0/templates/hooks/post-install/job-update-runtime.yaml new file mode 100644 index 000000000..955e882d7 --- /dev/null +++ b/charts/codefresh/cf-runtime/6.4.0/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/6.4.0/templates/hooks/post-install/rbac-gencerts-dind.yaml b/charts/codefresh/cf-runtime/6.4.0/templates/hooks/post-install/rbac-gencerts-dind.yaml new file mode 100644 index 000000000..4907dac38 --- /dev/null +++ b/charts/codefresh/cf-runtime/6.4.0/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/6.4.0/templates/hooks/pre-delete/job-cleanup-resources.yaml b/charts/codefresh/cf-runtime/6.4.0/templates/hooks/pre-delete/job-cleanup-resources.yaml new file mode 100644 index 000000000..0e3c7659f --- /dev/null +++ b/charts/codefresh/cf-runtime/6.4.0/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/6.4.0/templates/hooks/pre-delete/rbac-cleanup-resources.yaml b/charts/codefresh/cf-runtime/6.4.0/templates/hooks/pre-delete/rbac-cleanup-resources.yaml new file mode 100644 index 000000000..468ec2212 --- /dev/null +++ b/charts/codefresh/cf-runtime/6.4.0/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/6.4.0/templates/monitor/deployment.yaml b/charts/codefresh/cf-runtime/6.4.0/templates/monitor/deployment.yaml new file mode 100644 index 000000000..00c9fb2f9 --- /dev/null +++ b/charts/codefresh/cf-runtime/6.4.0/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/6.4.0/templates/monitor/rbac.yaml b/charts/codefresh/cf-runtime/6.4.0/templates/monitor/rbac.yaml new file mode 100644 index 000000000..f9812d565 --- /dev/null +++ b/charts/codefresh/cf-runtime/6.4.0/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/6.4.0/templates/monitor/service.yaml b/charts/codefresh/cf-runtime/6.4.0/templates/monitor/service.yaml new file mode 100644 index 000000000..f99706614 --- /dev/null +++ b/charts/codefresh/cf-runtime/6.4.0/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/6.4.0/templates/other/external-secrets.yaml b/charts/codefresh/cf-runtime/6.4.0/templates/other/external-secrets.yaml new file mode 100644 index 000000000..dc24e24e5 --- /dev/null +++ b/charts/codefresh/cf-runtime/6.4.0/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/6.4.0/templates/other/podMonitor.yaml b/charts/codefresh/cf-runtime/6.4.0/templates/other/podMonitor.yaml new file mode 100644 index 000000000..4319b722b --- /dev/null +++ b/charts/codefresh/cf-runtime/6.4.0/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/6.4.0/templates/other/serviceMonitor.yaml b/charts/codefresh/cf-runtime/6.4.0/templates/other/serviceMonitor.yaml new file mode 100644 index 000000000..29f890fe2 --- /dev/null +++ b/charts/codefresh/cf-runtime/6.4.0/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/6.4.0/templates/runner/deployment.yaml b/charts/codefresh/cf-runtime/6.4.0/templates/runner/deployment.yaml new file mode 100644 index 000000000..85777c487 --- /dev/null +++ b/charts/codefresh/cf-runtime/6.4.0/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/6.4.0/templates/runner/rbac.yaml b/charts/codefresh/cf-runtime/6.4.0/templates/runner/rbac.yaml new file mode 100644 index 000000000..d5f8c1323 --- /dev/null +++ b/charts/codefresh/cf-runtime/6.4.0/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/6.4.0/templates/runtime/_helpers.tpl b/charts/codefresh/cf-runtime/6.4.0/templates/runtime/_helpers.tpl new file mode 100644 index 000000000..6ba04fcc3 --- /dev/null +++ b/charts/codefresh/cf-runtime/6.4.0/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/6.4.0/templates/runtime/cm-dind-daemon.yaml b/charts/codefresh/cf-runtime/6.4.0/templates/runtime/cm-dind-daemon.yaml new file mode 100644 index 000000000..fc7f92905 --- /dev/null +++ b/charts/codefresh/cf-runtime/6.4.0/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/6.4.0/templates/runtime/rbac.yaml b/charts/codefresh/cf-runtime/6.4.0/templates/runtime/rbac.yaml new file mode 100644 index 000000000..a51b12526 --- /dev/null +++ b/charts/codefresh/cf-runtime/6.4.0/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/6.4.0/templates/runtime/runtime-env-spec-tmpl.yaml b/charts/codefresh/cf-runtime/6.4.0/templates/runtime/runtime-env-spec-tmpl.yaml new file mode 100644 index 000000000..baf726511 --- /dev/null +++ b/charts/codefresh/cf-runtime/6.4.0/templates/runtime/runtime-env-spec-tmpl.yaml @@ -0,0 +1,214 @@ +{{- 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 := . }} + {{- if or (kindIs "bool" $val) (kindIs "int" $val) (kindIs "float64" $val) }} + {{ $key }}: {{ $val | squote }} + {{- else }} + {{ $key }}: {{ $val }} + {{- end }} + {{- 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 }} + {{- 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 := . }} + {{- if or (kindIs "bool" $val) (kindIs "int" $val) (kindIs "float64" $val) }} + {{ $key }}: {{ $val | squote }} + {{- else }} + {{ $key }}: {{ $val }} + {{- end }} + {{- 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 }} +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/6.4.0/templates/runtime/secret.yaml b/charts/codefresh/cf-runtime/6.4.0/templates/runtime/secret.yaml new file mode 100644 index 000000000..2366d3ccf --- /dev/null +++ b/charts/codefresh/cf-runtime/6.4.0/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/6.4.0/templates/runtime/svc-dind.yaml b/charts/codefresh/cf-runtime/6.4.0/templates/runtime/svc-dind.yaml new file mode 100644 index 000000000..098edb4e8 --- /dev/null +++ b/charts/codefresh/cf-runtime/6.4.0/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/6.4.0/templates/volume-provisioner/cronjob.yaml b/charts/codefresh/cf-runtime/6.4.0/templates/volume-provisioner/cronjob.yaml new file mode 100644 index 000000000..db955bc77 --- /dev/null +++ b/charts/codefresh/cf-runtime/6.4.0/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/6.4.0/templates/volume-provisioner/daemonset.yaml b/charts/codefresh/cf-runtime/6.4.0/templates/volume-provisioner/daemonset.yaml new file mode 100644 index 000000000..39927149e --- /dev/null +++ b/charts/codefresh/cf-runtime/6.4.0/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/6.4.0/templates/volume-provisioner/deployment.yaml b/charts/codefresh/cf-runtime/6.4.0/templates/volume-provisioner/deployment.yaml new file mode 100644 index 000000000..522fa8791 --- /dev/null +++ b/charts/codefresh/cf-runtime/6.4.0/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/6.4.0/templates/volume-provisioner/rbac.yaml b/charts/codefresh/cf-runtime/6.4.0/templates/volume-provisioner/rbac.yaml new file mode 100644 index 000000000..f3ae9609f --- /dev/null +++ b/charts/codefresh/cf-runtime/6.4.0/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/6.4.0/templates/volume-provisioner/secret.yaml b/charts/codefresh/cf-runtime/6.4.0/templates/volume-provisioner/secret.yaml new file mode 100644 index 000000000..accf601d1 --- /dev/null +++ b/charts/codefresh/cf-runtime/6.4.0/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/6.4.0/templates/volume-provisioner/storageclass.yaml b/charts/codefresh/cf-runtime/6.4.0/templates/volume-provisioner/storageclass.yaml new file mode 100644 index 000000000..77a7602da --- /dev/null +++ b/charts/codefresh/cf-runtime/6.4.0/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/6.4.0/values.yaml b/charts/codefresh/cf-runtime/6.4.0/values.yaml new file mode 100644 index 000000000..15d7cc738 --- /dev/null +++ b/charts/codefresh/cf-runtime/6.4.0/values.yaml @@ -0,0 +1,951 @@ +# -- 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: 1.10.2 + + # -- Init container + init: + image: + registry: quay.io + repository: codefresh/cli + tag: 0.85.0-rootless + + 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/codefresh-shell + tag: 0.0.2 + 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.0 + # -- 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.29.4 + podAnnotations: {} + podSecurityContext: + enabled: true + runAsUser: 1000 + fsGroup: 1000 + containerSecurityContext: {} + env: {} + resources: {} + nodeSelector: {} + tolerations: + - key: 'codefresh/dind' + operator: 'Exists' + effect: 'NoSchedule' + volumePermissions: + enabled: true + image: + registry: docker.io + repository: alpine + tag: 3.18 + 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 + 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.7 # use `latest-rootless/rootless/26.1.4-1.28.7-rootless` tags for rootless-dind + pullPolicy: IfNotPresent + # -- 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 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 + + # -- Parameters for Engine pod (aka "pipeline" orchestrator). + engine: + # -- Set image. + image: + registry: quay.io + repository: codefresh/engine + tag: 1.174.12 + pullPolicy: IfNotPresent + # -- 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.28.1-1.5.0 + CONTAINER_LOGGER_IMAGE: quay.io/codefresh/cf-container-logger:1.11.6 + DOCKER_BUILDER_IMAGE: quay.io/codefresh/cf-docker-builder:1.3.13 + DOCKER_PULLER_IMAGE: quay.io/codefresh/cf-docker-puller:8.0.17 + DOCKER_PUSHER_IMAGE: quay.io/codefresh/cf-docker-pusher:6.0.16 + DOCKER_TAG_PUSHER_IMAGE: quay.io/codefresh/cf-docker-tag-pusher:1.3.14 + FS_OPS_IMAGE: quay.io/codefresh/fs-ops:1.2.3 + GIT_CLONE_IMAGE: quay.io/codefresh/cf-git-cloner:10.1.28 + KUBE_DEPLOY: quay.io/codefresh/cf-deploy-kubernetes:16.1.11 + PIPELINE_DEBUGGER_IMAGE: quay.io/codefresh/cf-debugger:1.3.0 + TEMPLATE_ENGINE: quay.io/codefresh/pikolo:0.14.1 + CR_6177_FIXER: 'quay.io/codefresh/alpine:edge' + GC_BUILDER_IMAGE: 'quay.io/codefresh/cf-gc-builder:0.5.3' + COSIGN_IMAGE_SIGNER_IMAGE: 'quay.io/codefresh/cf-cosign-image-signer:2.4.0-cf.2' + # -- 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 + # -- 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.85.0-rootless + 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.28.4 + 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.47 + # -- 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.17 + # -- 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 + # -- 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/jenkins/jenkins/5.6.2/CHANGELOG.md b/charts/jenkins/jenkins/5.6.2/CHANGELOG.md new file mode 100644 index 000000000..22c8b38d9 --- /dev/null +++ b/charts/jenkins/jenkins/5.6.2/CHANGELOG.md @@ -0,0 +1,3118 @@ +# Changelog + +This file documents all notable changes to the Jenkins Helm Chart. +The release numbering uses [semantic versioning](http://semver.org). + +Use the following links to reference issues, PRs, and commits prior to v2.6.0. + +* Issue: `https://github.com/helm/charts/issues/[issue#]` +* PR: `https://github.com/helm/charts/pull/[pr#]` +* Commit: `https://github.com/helm/charts/commit/[commit]/stable/jenkins` + +The changelog until v1.5.7 was auto-generated based on git commits. +Those entries include a reference to the git commit to be able to get more details. + +## 5.6.2 + +Update `kubernetes` to version `4288.v1719f9d0c854` + +## 5.6.1 + +Documentation about OCI installation + +## 5.6.0 + +Helm chart is also now deployed on GitHub packages and can be installed from `oci://ghcr.io/jenkinsci/helm-charts/jenkins` + +## 5.5.16 + +Update `kubernetes` to version `4287.v73451380b_576` + +## 5.5.15 + +Add support for `controller.enableServiceLinks` to disable service links in the controller pod. + +## 5.5.14 + +Update `jenkins/jenkins` to version `2.462.2-jdk17` + +## 5.5.13 + +Update `docker.io/kiwigrid/k8s-sidecar` to version `1.27.6` + +## 5.5.12 + +Update `configuration-as-code` to version `1850.va_a_8c31d3158b_` + +## 5.5.11 + +Update `configuration-as-code` to version `1849.v3a_d20568000a_` + +## 5.5.10 + +Update `git` to version `5.4.1` + +## 5.5.9 + +Update `git` to version `5.4.0` + +## 5.5.8 + +Add `agent.garbageCollection` to support setting [kubernetes plugin garbage collection](https://plugins.jenkins.io/kubernetes/#plugin-content-garbage-collection-beta). + +## 5.5.7 + +Update `kubernetes` to version `4285.v50ed5f624918` + +## 5.5.6 + +Add `agent.useDefaultServiceAccount` to support omitting setting `serviceAccount` in the default pod template from `serviceAgentAccount.name`. +Add `agent.serviceAccount` to support setting the default pod template value. + +## 5.5.5 + +Update `jenkins/inbound-agent` to version `3261.v9c670a_4748a_9-1` + +## 5.5.4 + +Update `jenkins/jenkins` to version `2.462.1-jdk17` + +## 5.5.3 + +Update `git` to version `5.3.0` + +## 5.5.2 + +Update `kubernetes` to version `4280.vd919fa_528c7e` + +## 5.5.1 + +Update `kubernetes` to version `4265.v78b_d4a_1c864a_` + +## 5.5.0 + +Introduce capability of set skipTlsVerify and usageRestricted flags in additionalClouds + + +## 5.4.4 + +Update CHANGELOG.md, README.md, and UPGRADING.md for linting + +## 5.4.3 + +Update `configuration-as-code` to version `1836.vccda_4a_122a_a_e` + +## 5.4.2 + +Update `docker.io/kiwigrid/k8s-sidecar` to version `1.27.5` + +## 5.4.1 + +Update `jenkins/jenkins` to version `2.452.3` + +## 5.4.0 + +Introduce capability of additional mountPaths and logging file paths for config reload container + +## 5.3.6 + +Update `workflow-aggregator` to version `600.vb_57cdd26fdd7` + +## 5.3.5 + +Update `kubernetes` to version `4253.v7700d91739e5` + +## 5.3.4 + +Update `jenkins/jenkins` to version `2.452.3-jdk17` +## 5.3.3 + +Update `jenkins/inbound-agent` to version `3256.v88a_f6e922152-1` + +## 5.3.2 + +Update `kubernetes` to version `4248.vfa_9517757b_b_a_` + +## 5.3.1 + +Fix Tiltfile deprecated value reference + +## 5.3.0 + +Add `controller.topologySpreadConstraints` + +## 5.2.2 + +Update `kubernetes` to version `4246.v5a_12b_1fe120e` + +## 5.2.1 + +Update `jenkins/jenkins` to version `2.452.2-jdk17` + +## 5.2.0 + +Add `agent.inheritYamlMergeStrategy` to allow configuring this setting on the default agent pod template. + +## 5.1.31 + +Update `kubernetes` to version `4245.vf5b_83f1fee6e` + +## 5.1.30 + +Add `controller.JCasC.configMapAnnotations` to allow setting annotations on the JCasC ConfigMaps. + +## 5.1.29 + +Update `docker.io/kiwigrid/k8s-sidecar` to version `1.27.4` + +## 5.1.28 + +Update `docker.io/kiwigrid/k8s-sidecar` to version `1.27.3` + +## 5.1.27 + +Update `kubernetes` to version `4244.v4fb_b_00994a_90` + +## 5.1.26 + +Update `kubernetes` to version `4238.v41b_3ef14a_5d8` + +## 5.1.25 + +Update `kubernetes` to version `4236.vc06f753c3234` + +## 5.1.24 + +Update `kubernetes` to version `4234.vdf3e78112369` + +## 5.1.23 + +Update `kubernetes` to version `4233.vb_67a_0e11a_039` + +## 5.1.22 + +Update `configuration-as-code` to version `1810.v9b_c30a_249a_4c` + +## 5.1.21 + +Update `kubernetes` to version `4231.vb_a_6b_8936497d` + +## 5.1.20 + +Update `kubernetes` to version `4230.vceef11cb_ca_37` + +## 5.1.19 + +Update `docker.io/kiwigrid/k8s-sidecar` to version `1.27.2` + +## 5.1.18 + +Update `configuration-as-code` to version `1807.v0175eda_00a_20` + +## 5.1.17 + +Update `jenkins/inbound-agent` to version `3248.v65ecb_254c298-1` + +## 5.1.16 + +Update `configuration-as-code` to version `1805.v1455f39c04cf` + +## 5.1.15 + +Update `jenkins/jenkins` to version `2.452.1-jdk17` + +## 5.1.14 + +Update `kubernetes` to version `4219.v40ff98cfb_d6f` + +## 5.1.13 + +Update `docker.io/kiwigrid/k8s-sidecar` to version `1.27.1` + +## 5.1.12 + +Update `git` to version `5.2.2` + +## 5.1.11 + +Update `kubernetes` to version `4214.vf10083a_42e70` + +## 5.1.10 + +Update `kubernetes` to version `4211.v08850dd0dfa_3` + +## 5.1.9 + +Update `docker.io/kiwigrid/k8s-sidecar` to version `1.26.2` + +## 5.1.8 + +Update `kubernetes` to version `4209.vc646b_71e5269` + +## 5.1.7 + +Update `kubernetes` to version `4208.v4017b_a_27a_d67` + +## 5.1.6 + +Update `jenkins/jenkins` to version `2.440.3-jdk17` + +## 5.1.5 + +Fix Prometheus controller name. + +## 5.1.4 + +Update `docker.io/bats/bats` to version `1.11.0` + +## 5.1.3 + +Update `jenkins/jenkins` to version `2.440.2-jdk17` + +## 5.1.2 + +Update `kubernetes` to version `4203.v1dd44f5b_1cf9` + +## 5.1.1 + +Update `kubernetes` to version `4199.va_1647c280eb_2` + +## 5.1.0 + +Add `agent.restrictedPssSecurityContext` to automatically inject in the jnlp container a securityContext that is suitable for the use of the restricted Pod Security Standard + +## 5.0.20 + +Update `docker.io/kiwigrid/k8s-sidecar` to version `1.26.1` + +## 5.0.19 + +Introduced helm-docs to automatically generate `values.yaml` documentation. + +## 5.0.18 + +Update `kubernetes` to version `4193.vded98e56cc25` + +## 5.0.17 + +Update `docker.io/kiwigrid/k8s-sidecar` to version `1.26.0` + +## 5.0.16 + +Enable support for deleting plugin configuration files at startup. + +## 5.0.15 + +Fixed changelog entries for previous version bumps + + +## 5.0.14 + +Update `jenkins/jenkins` to version `2.440.1-jdk17` + +## 5.0.13 + +Update `docker.io/kiwigrid/k8s-sidecar` to version `1.25.4` + +## 5.0.12 + +Fix controller.sidecars.additionalSidecarContainers renaming and add tests + +## 5.0.11 + +* Add controller.sidecars.configAutoReload.scheme to specify protocol scheme when connecting Jenkins configuration-as-code reload endpoint +* Add controller.sidecars.configAutoReload.skipTlsVerify to force the k8s-sidecar container to skip TLS verification when connecting to an HTTPS Jenkins configuration-as-code reload endpoint + +## 5.0.10 + +Update `jenkins/inbound-agent` to version `3206.vb_15dcf73f6a_9-3` + +## 5.0.9 + +Update `kubernetes` to version `4186.v1d804571d5d4` + +## 5.0.8 + +Update `configuration-as-code` to version `1775.v810dc950b_514` + +## 5.0.7 + +Update `docker.io/kiwigrid/k8s-sidecar` to version `docker.io/kiwigrid/k8s-sidecar` + +## 5.0.6 + +Removed `docker.io` prefix from inbound-agent image + +## 5.0.5 + +Prefixed artifacthub.io/images with `docker.io` + +## 5.0.4 + +Updated super-linter to v6. Updated README.md and CHANGELOG.md to fix linting issues. + +## 5.0.2 + +Update `git` to version `5.2.1` + +## 5.0.1 + +Update `docker.io/bats/bats` to version `v1.10.0` + +## 5.0.0 + + > [!CAUTION] + > Several fields have been renamed or removed. See [UPGRADING.md](./UPGRADING.md#to-500) + +The Helm Chart is now updated automatically via [Renovate](https://docs.renovatebot.com/) + +## 4.12.1 + +Update Jenkins image and appVersion to jenkins lts release version 2.426.3 + +## 4.12.0 + +Add support for [generic ephemeral storage](https://github.com/jenkinsci/kubernetes-plugin/pull/1489) in `agent.volumes` and `agents.workspaceVolume`. + +| plugin | old version | new version | +|------------|---------------------|--------------------| +| kubernetes | 4029.v5712230ccb_f8 | 4174.v4230d0ccd951 | + +## 4.11.2 + +Fixed documentation for controller.initScripts. + +## 4.11.1 + +Updated helm-unittest and made unittests compatible. + +## 4.11.0 + +Add multi-cloud support. + +## 4.10.0 + +Bumped Jenkins inbound agent from 3107.v665000b_51092-15 to 3192.v713e3b_039fb_e-5. + +## 4.9.2 + +Update Jenkins image and appVersion to jenkins lts release version 2.426.2 + + +Notes about [Artifact Hub](https://artifacthub.io/packages/helm/jenkinsci/jenkins?modal=changelog) changelog processing: +- Remove empty lines +- Keep only ASCII characters (no emojis) +- One change per line +- Remove table(s) (lines starting by "|") +- Backticks aren't rendered on artifacthub.io changelog + +## 4.9.1 + +Restore artifact hub notes location in CHANGELOG.md + +## 4.9.0 + +Update base images from JDK 11 to JDK 17. + +## 4.8.6 + +Proper `artifacthub.io/changes` changelog annotation preprocessing. + +## 4.8.5 + +Fix `artifacthub.io/changes` changelog annotation added to the released chart. + +## 4.8.4 + +Add `artifacthub.io/changes` changelog annotation to the released chart. + +## 4.8.3 + +Update Jenkins image and appVersion to jenkins lts release version 2.426.1 + +## 4.8.2 + +Add the ability to modify `retentionTimeout` and `waitForPodSec` default value in JCasC + +## 4.8.1 + +Reintroduces changes from 4.7.0 (reverted in 4.7.1), with additional fixes: + +- METHOD is now allowed in `env` and is not duplicated anymore +- No calls to JCasC reload endpoint from the init container + +## 4.8.0 + +Adds support for ephemeralStorage request and limit in Kubernetes plugin JCasC template + +## 4.7.4 + +Add the config-init-script checksum into the controller statefullset pod annotations to trigger restart of the pod in case of updated init scripts. + +## 4.7.3 + +Update Jenkins image and appVersion to jenkins lts release version 2.414.3 + +## 4.7.1 + +Changes in 4.7.0 were reverted. + +## 4.7.0 + +Runs `config-reload` as an init container, in addition to the sidecar container, to ensure that JCasC YAMLs are present before the main Jenkins container starts. This should fix some race conditions and crashes on startup. + +## 4.6.7 + +Change jenkins-test image label to match the other jenkins images + +## 4.6.5 + +Update Jenkins image and appVersion to jenkins lts release version 2.414.2 + +## 4.6.4 + +Introducing TPL function on variables related to hostname in `./charts/jenkins/templates/jenkins-controller-ingress.yaml` + +## 4.6.3 + +Add values to documentation + +## 4.6.2 + +Update word from hundreds to over 1800 to align with blurb at . + +## 4.6.1 + +Update `configuration-as-code` plugin to fix dependency issues with `azure-ad` plugin + +## 4.6.0 + +Added `.Values.controller.httpsKeyStore.jenkinsHttpsJksSecretKey` to allow overriding the default secret key containing the JKS file. +Added `.Values.controller.httpsKeyStore.jenkinsHttpsJksPasswordSecretName` to allow getting the JKS password from a different secret. +Added `.Values.controller.httpsKeyStore.jenkinsHttpsJksPasswordSecretKey` to allow overriding the default secret key containing the JKS password. + +## 4.5.1 + +Update Jenkins image and appVersion to jenkins lts release version 2.414.1 + + +## 4.5.0 + +Added `.Values.persistence.dataSource` to allow cloning home PVC from existing dataSource. + +## 4.4.2 + +Update Jenkins image and appVersion to jenkins lts release version 2.401.3 + + +## 4.4.1 + +Added `.Values.agent.jnlpregistry` to allow agents to be configured with private registry. + +## 4.4.0 + +Add config keys for liveness probes on agent containers. + + +## 4.3.30 + +Update Jenkins version in controller test matching LTS version + +## 4.3.29 + +Update Jenkins image and appVersion to jenkins lts release version 2.401.2 + + +## 4.3.28 + +Allow the kubernetes API server URL to be configurable. + +## 4.3.27 + +Bump kiwigrid/k8s-sidecar from 1.23.1 to 1.24.4 and jenkins/inbound-agent from 3107.v665000b_51092-5 to 3107.v665000b_51092-15. + +## 4.3.26 + +Fix various typos in the chart documentation. + +## 4.3.25 + +| plugin | old version | new version | +|-----------------------|----------------------|-----------------------| +| kubernetes | 3900.va_dce992317b_4 | 3937.vd7b_82db_e347b_ | +| configuration-as-code | 1625.v27444588cc3d | 1647.ve39ca_b_829b_42 | +| git | 5.0.0 | 5.1.0 | +| ldap | 671.v2a_9192a_7419d | 682.v7b_544c9d1512 | + +## 4.3.24 + +Update Jenkins image and appVersion to jenkins lts release version 2.401.1 + + +## 4.3.23 + +Update Jenkins image and appVersion to jenkins lts release version 2.387.3 + + +## 4.3.22 + + +Bump chart version. + +## 4.3.21 + + +Document building charts for weekly releases. + +## 4.3.20 + + +Enhance repository appearance and miscellaneous cleanup. + +## 4.3.19 + + +Comply with superlinter rules and address ShellCheck issues. + +## 4.3.18 + + +Bump kiwigrid/k8s-sidecar from 1.15.0 to 1.23.1. + +## 4.3.17 + + +Bump jenkins/inbound-agent from 4.11.2-4 to 3107.v665000b_51092-5. + +## 4.3.16 + + +Update bundled plugins: +- [ldap](https://plugins.jenkins.io/ldap/): From 2.5 to 671.v2a_9192a_7419d +- [kubernetes](https://plugins.jenkins.io/kubernetes/): From 3734.v562b_b_a_627ea_c to 3900.va_dce992317b_4 +- [workflow-aggregator](https://plugins.jenkins.io/workflow-aggregator/): From 590.v6a_d052e5a_a_b_5 to 590.v6a_d052e5a_a_b_5 +- [configuration-as-code](https://plugins.jenkins.io/configuration-as-code/): From 1569.vb_72405b_80249 to 1625.v27444588cc3d + +## 4.3.15 + + +Update bats from 1.2.1 to 1.9.0. + +## 4.3.14 + + +Update various GH actions, typo fixes, and miscellaneous chores. + +## 4.3.13 + + +Bump helm-unittest from 0.2.8 to 0.2.11. + +## 4.3.12 + + +Update wording in values.yml. + +## 4.3.11 + +Update Jenkins image and appVersion to jenkins lts release version 2.387.2 + + +## 4.3.10 + +Correct incorrect env var definition +Disable volume mount if disableSecretMount enabled + +## 4.3.9 + +Document `.Values.agent.directConnection` in readme. +Add default value for `.Values.agent.directConnection` to `values.yaml` + +## 4.3.8 + +Added `.Values.agent.directConnection` to allow agents to be configured to connect direct to the JNLP port on the +controller, preventing the need for an external HTTP endpoint for this purpose. + +## 4.3.7 + +Added `.Values.controller.shareProcessNamespace` and `.Values.controller.httpsKeyStore.disableSecretMount` to enable sourcing TLS certs from external issuers + +## 4.3.6 + +Update Jenkins image and appVersion to jenkins lts release version 2.387.1 + +## 4.3.5 + +Added `.Values.helmtest.bats.image` and `.Values.helmtest.bats.image` to allow unit tests to be configurable. Fixes [https://github.com/jenkinsci/helm-charts/issues/683] + +## 4.3.4 + +Update Jenkins image and appVersion to jenkins lts release version 2.375.3 + + +## 4.3.3 + +Removed hardcoding of chart version in tests to make maintenance easier + +## 4.3.2 + +Added `.Values.serviceAccount.extraLabels` on Service Account +Added `.Values.serviceAccountAgent.extraLabels` on Agent's Service Account + + +## 4.3.0 + +Moved use of `.Values.containerEnv` within `jenkins` Container to top of `env` block to allow for subsequent Environment Variables to reference these additional ones. + +## 4.2.21 + +Update Jenkins image and appVersion to jenkins lts release version 2.375.2 + + +## 4.2.20 + +Fixed the `controller.prometheus.metricRelabelings` being unable to convert the value to the ServiceMonitor. +Added `controller.prometheus.relabelings` to allow relabling before scrape. +Added default values for `controller.prometheus.relabelings` and `controller.prometheus.metricRelabelings`. + +## 4.2.19 + +CronJob API version upgraded to batch/v1 + +## 4.2.18 + +Added option to set secretEnvVars. + +## 4.2.17 + +Update Jenkins image and appVersion to jenkins lts release version 2.375.1 + + +## 4.2.16 + +Fixed chart notes not rendering Jenkins URL with prefix when `controller.jenkinsUriPrefix` is set. +Fixed chart notes not rendering Jenkins URL with `https` when `controller.ingress.tls` or `controller.controller.httpsKeyStore.enable` is set. +Fixed chart notes rendering wrong JCasC URL when not using `controller.ingress`. + +## 4.2.15 + +Update Jenkins image and appVersion to jenkins lts release version 2.361.4 + +## 4.2.14 + +Added option to mount all keys from an existing k8s secret + +## 4.2.13 + +Adding `tpl` to `controller.additionalExistingSecrets` + +## 4.2.12 + +Update Jenkins image and appVersion to jenkins lts release version 2.361.3 + + +## 4.2.11 + +Update default plugin versions + +| plugin | old version | new version | +|-----------------------|-----------------------|------------------------| +| kubernetes | 3706.vdfb_d599579f3 | 3734.v562b_b_a_627ea_c | +| git | 4.11.5 | 4.13.0 | +| configuration-as-code | 1512.vb_79d418d5fc8 | 1569.vb_72405b_80249 | + +## 4.2.10 +Fix grammar and typos + +## 4.2.9 +Update Jenkins image and appVersion to jenkins lts release version 2.361.2 + +## 4.2.8 +Modify the condition to trigger copying jenkins_config files when configAutoReload option is disabled during Jenkins initialization + +## 4.2.7 +Support for remote URL for configuration + +## 4.2.6 +Add option to set hostnetwork for agents + +## 4.2.5 +Add an extra optional argument to extraPorts in order to specify targetPort + +## 4.2.4 +Remove k8s capibility requirements when setting priority class for controller + +## 4.2.3 Update plugin versions + +| plugin | old version | new version | +| --------------------- | --------------------- | --------------------- | +| kubernetes | 3600.v144b_cd192ca_a_ | 3706.vdfb_d599579f3 | +| workflow-aggregator | 581.v0c46fa_697ffd | 590.v6a_d052e5a_a_b_5 | +| configuration-as-code | 1429.v09b_044a_c93de | 1512.vb_79d418d5fc8 | +| git | 4.11.3 | 4.11.5 | + +Resolve version conflict between default install of plugins. + +## 4.2.2 + +Support Google Managed Prometheus + +## 4.2.1 + +Remove option to provide command and args of agent as YAML. This feature was never supported by the Jenkins Kubernetes +plugin. + +## 4.2.0 + +Add option to provide additional containers to agents + +## 4.1.18 + +Update Jenkins image and appVersion to jenkins lts release version 2.361.1 + + +## 4.1.17 + +Update Jenkins casc default settings to allow `security` configs to be provided + + +## 4.1.16 + +Update Jenkins image and appVersion to jenkins lts release version 2.346.3 + + +## 4.1.15 + +`projectNamingStrategy` is configurable in default config. + +## 4.1.14 + +If `installPlugins` is disabled, don't create unused plugins volume. + +## 4.1.13 + +Update Jenkins image and appVersion to jenkins lts release version 2.346.2 + + +## 4.1.12 + +If keystore is defined, it is now also made available in the initContainer. + +## 4.1.11 + +JCasC ConfigMaps now generate their name from the `jenkins.casc.configName` helper + +## 4.1.10 + +Update Jenkins image and appVersion to jenkins lts release version 2.346.1 + + +## 4.1.9 + +Allow setting `imagePullSecret` for backup job via `backup.imagePullSecretName` + +## 4.1.8 + +Fix path of projected secrets from `additionalExistingSecrets`. + +## 4.1.7 + +Update readme with explanation on the required environmental variable `AWS_REGION` in case of using an S3 bucket. + +## 4.1.6 + +project adminSecret, additionalSecrets and additionalExistingSecrets instead of mount with subPath + +## 4.1.5 + +Update readme to fix `JAVA_OPTS` name. + +## 4.1.4 +Update plugins + +## 4.1.3 +Update jenkins-controller-statefulset projected volumes definition + +## 4.1.1 +Added 'controller.prometheus.metricRelabelings' to allow relabling and dropping unused prometheus metrics + +## 4.1.0 + +Added `controller.sidecars.configAutoReload.envFrom`, `controller.initContainerEnvFrom`, `controller.containerEnvFrom` + +## 4.0.1 + +No code changes - CI updated to run unit tests using Helm 3.8.2. + +## 4.0.0 + +Removes automatic `remotingSecurity` setting when using a container tag older than `2.326` (introduced in [`3.11.7`](#3117)). If you're using a version older than `2.326`, you should explicitly set `.controller.legacyRemotingSecurityEnabled` to `true`. + +## 3.12.2 + +Update Jenkins image and appVersion to jenkins lts release version 2.332.3 + +## 3.12.1 + +Make namespace configurable for agents and additional agents. + +## 3.12.0 + +Added a flag for disabling the default Jenkins Agent configuration. + +## 3.11.10 + +Update Jenkins image and appVersion to jenkins lts release version 2.332.2 + +## 3.11.9 Bump configuration-as-code plugin version + +| plugin | old version | new version | +| --------------------- | ----------- | ----------- | +| configuration-as-code | 1.51 | 1414.v878271fc496f | + +## 3.11.8 + +Make [externalTrafficPolicy](https://kubernetes.io/docs/concepts/services-networking/service/#traffic-policies) and `loadBalancerSourceRanges` fields customizable for Agent listener service via `controller.agentListenerExternalTrafficPolicy` and `controller.loadBalancerSourceRanges`. + +## 3.11.7 + +Removed Configuration as Code `remotingSecurity` section for Jenkins 2.326 or newer. See [Documentation](https://www.jenkins.io/redirect/AdminWhitelistRule) to learn more. + +## 3.11.6 + +Update Jenkins image and appVersion to jenkins lts release version 2.332.1 + + +## 3.11.5 + +Change Backup Role name function call to match the RoleDef function call in the Backup RoleBinding + +## 3.11.4 + +Update Jenkins image and appVersion to jenkins lts release version 2.319.3 + + +## 3.11.3 + +Update kiwigrid/k8s-sidecar:1.15.0 +Update jenkins/inbound-agent:4.11.2-4 + +## 3.11.2 + +Improve example for workspaceVolume. Clarify that this is not a list. + +## 3.11.1 + +Update configuration-as-code plugin to 1.55.1 + + +## 3.11.0 + +Update default plugin versions + +| plugin | old version | new version | +| --------------------- | ----------- | ----------- | +| kubernetes | 1.31.1 | 1.31.3 | +| git | 4.10.1 | 4.10.2 | + +## 3.10.3 + +Update Jenkins image and appVersion to jenkins lts release version 2.319.2 + + +## 3.10.2 + +Fix definition of startupProbe when deploying on a Kubernetes cluster < 1.16 + +## 3.10.1 + +correct VALUES_SUMMARY.md for installLatestPlugins + +## 3.10.0 + +Update default plugin versions + +| plugin | old version | new version | +| --------------------- | ----------- | ----------- | +| kubernetes | 1.30.11 | 1.31.1 | +| git | 4.10.0 | 4.10.1 | +| configuration-as-code | 1.54 | 1.55 | + +## 3.9.4 + +Add JAVA_OPTIONS to the readme so proxy settings get picked by jenkins-plugin-cli + +## 3.9.3 + +Fix config reload request URL when httpsKeystore in use + +## 3.9.2 + +Update Jenkins image and appVersion to jenkins lts release version 2.319.1 +Update following plugins: + +* kubernetes:1.30.11 +* git:4.10.0 +* configuration-as-code:1.54 + +## 3.9.1 + +Adding `tpl` to `controller.overrideArgs` + +## 3.9.0 + +Added containerSecurityContext + +## 3.8.9 + +Fix mounting of HTTPS keystore secret when httpsKeyStore is enabled + +## 3.8.8 + +Update Jenkins image and appVersion to jenkins lts release version 2.303.3 + +## 3.8.7 + +Adding `tpl` to `initScripts` + +## 3.8.6 + +Add `controller.tagLabel` to specify the label for the image tag, for example `jdk11` or `alpine` + +## 3.8.5 + +Move jenkins web root outside of home dir + +## 3.8.4 + +Add `controller.initConfigMap` to pass pre-existing `init.groovy.d` ConfigMaps to the controller + +## 3.8.3 + +Update missed reference to jenkins/inbound-agent:4.11-1 + +## 3.8.2 + +Update jenkins/inbound-agent:4.11-1 + +## 3.8.1 + +Update jenkins/inbound-agent:4.10-3 + +## 3.8.0 + +Update kiwigrid/k8s-sidecar:1.14.2 + +## 3.7.1 + +Update git and casc plugins versions + +## 3.7.0 + +Added the option to create AWS SecurityGroupPolicy resources + +## 3.6.2 + +Fix httpsKeyStore mount when `controller.httpsKeyStore.enable` is `true` + +## 3.6.1 + +Update Jenkins image and appVersion to jenkins lts release version 2.303.2 + + +## 3.6.0 +Support custom agent pod labels + +## 3.5.20 +Disallow ingress on port 50000 when agent listener is disabled + +## 3.5.19 +Add support for specifying termination-log behaviour for Jenkins controller + +## 3.5.18 +Add support for creating a Pod Disruption Budget for Jenkins controller + +## 3.5.17 +Update workdingDir to `/home/jenkins/agent` + +## 3.5.16 +Update location of icon (wiki.jenkins.io is down) + +## 3.5.15 +Add support for adding labels to the Jenkins home Persistent Volume Claim (pvc) + +## 3.5.14 + +* Updated versions of default plugins +* Use verbose logging during plugin installation +* download the latest version of all plugin dependencies (Fixes #442) + +## 3.5.13 + +Update Jenkins image and appVersion to jenkins lts release version 2.303.1 + +## 3.5.12 + +Added extended documentation for Backup and Restore. + +## 3.5.11 + +Sanitized the Jenkins Label + +## 3.5.10 + +Fixed `controller.customJenkinsLabels` not getting templated into the controller `labelString:` field in JCasC + +## 3.5.9 + +Update Jenkins image and appVersion to jenkins lts release version 2.289.3 + + +## 3.5.8 + +Add parameter `backup.serviceAccount.create` to disable service account creation for backup service and `backup.serviceAccount.name` to allow change of the SA name. +`backup.annotations` was moved to `backup.serviceAccount.annotations` + +## 3.5.7 + +Enable setting `controller.serviceExternalTrafficPolicy` to set [the standard Service option](https://kubernetes.io/docs/tasks/access-application-cluster/create-external-load-balancer/#preserving-the-client-source-ip). `externalTrafficPolicy` denotes if this Service desires to route external traffic to node-local or cluster-wide endpoints. + +## 3.5.6 + +Add optional `controller.initContainerResources`, if set, it will change resources allocation for init controller, overwise the `controller.resources` will be used + +## 3.5.5 + +Allow to configure nodeUsageMode via `agent.nodeUsageMode` + +## 3.5.4 + +Update tests to work with unittest 0.2.6 + +## 3.5.3 + +Update Jenkins image and appVersion to jenkins lts release version 2.289.2 + +## 3.5.2 + +Enable setting `controller.installLatestSpecifiedPlugins` to set whether to download the latest dependencies of any plugin that is requested to have the latest version. + +## 3.5.1 +Fix activeDeadlineSeconds wrong type bug in jenkins-backup-cronjob template + +## 3.5.0 + +Allow `controller.podAnnotations` to be render as a template + +## 3.4.1 + +Allow showRawYaml for the default agent's pod template to be customized. + +## 3.4.0 + +configAutoReload container updated from `kiwigrid/k8s-sidecar:0.1.275` to `kiwigrid/k8s-sidecar:1.12.2` + +## 3.3.23 + +Make `controller.ingress.resourceRootUrl` compatible with API version networking.k8s.io/v1 on k8s >= 1.19.x + +## 3.3.22 + +Update Jenkins image and appVersion to jenkins lts release version 2.289.1 + +## 3.3.21 +`persistence.mounts` additionally mount to init container to allow custom CA certificate keystore + +## 3.3.18 +Added `controller.overrideArgs` so any cli argument can be passed to the WAR. + +## 3.3.17 +Correct docs on disabling plugin installation + +## 3.3.16 +Support generating `SecretClaim` resources in order to read secrets from HashiCorp Vault into Kubernetes using `kube-vault-controller`. + +## 3.3.15 +Prevent `controller.httpsKeyStore` from improperly being quoted, leading to an invalid location on disk + +## 3.3.14 +Correct docs on disabling plugin installation + +## 3.3.13 +Update plugins + +## 3.3.12 +Add `controller.additionalExistingSecrets` property + +## 3.3.11 +Add support for disabling the Agent listener service via `controller.agentListenerEnabled`. + +## 3.3.10 +Update Jenkins image and appVersion to jenkins lts release version 2.277.4 + +## 3.3.9 +* Change helper template so user defined `agent.jenkinsUrl` value will always be used, if set +* Simplify logic for `jenkinsUrl` and `jenkinsTunnel` generation: always use fully qualified address + +## 3.3.8 +Update Jenkins image and appVersion to jenkins lts release version 2.277.3 + +## 3.3.7 +fix controller-ingress line feed bug + +## 3.3.6 + +Update Git plugin version to v4.7.1 +Update ldap plugin version to v2.5 + +## 3.3.5 + +Use tpl function for environment vars. Fixes [https://github.com/jenkinsci/helm-charts/issues/324] + +## 3.3.4 + +Update Jenkins image and appVersion to jenkins lts release version 2.277.2 + + +## 3.3.3 + +Enable setting `controller.installLatestPlugins` to set whether to download the minimum required version of all dependencies. + +## 3.3.2 + +Add `controller.additionalSecrets` documentation + +## 3.3.1 + +Add `controller.additionalSecrets` property + +## 3.3.0 + +Change default Jenkins image to `jdk11` variant + +## 3.2.6 + +Add missing `controller.jenkinsUrlProtocol` property + +## 3.2.5 + +Add additional metadata `artifacthub.io/images` for artifacthub + +## 3.2.4 +Update Jenkins image and appVersion to jenkins lts release version 2.277.1 +Update Git plugin version to v4.6.0 +Update kubernetes plugin version to v1.29.2 + +## 3.2.3 + +Fix rendering `controller.ingress.path` + +## 3.2.2 + +Added description for `controller.jenkinsUrl` value + +## 3.2.1 + +Enable setting ImagePullSecrets to controller and agent service accounts. + +## 3.2.0 + +Calculate consistent unique agent IDs to be used in pod templates. Fixes [https://github.com/jenkinsci/helm-charts/issues/270] + +## 3.1.15 + +Fix documentation for the kubernetes probes + +## 3.1.14 + +Typo in documentation + +## 3.1.13 + +Update Jenkins image and appVersion to jenkins lts release version 2.263.4 + +## 3.1.12 + +Added GitHub Action to automate the updating of LTS releases. + +## 3.1.11 + +Enable setting controller.updateStrategy to change the update strategy for StatefulSet + +## 3.1.10 + +Fixed issue for the AgentListener where it was not possible to attribute a NodePort + +## 3.1.9 + +Upgrade kubernetes plugin to 1.29.0 and CasC plugin to 1.47 + +## 3.1.8 + +Fix init scripts config map name + +## 3.1.7 + +Fix missing newline when `httpsKeyStore` is enabled + +## 3.1.6 + +Mount controller init scripts from ConfigMap + +## 3.1.5 + +Fix `namespaceOverride` not applied when loading JCasC + +## 3.1.4 + +Update Git plugin version to v4.5.2 + +## 3.1.3 + +Update Jenkins image and appVersion to jenkins lts release version 2.263.3 + +## 3.1.2 + +Enable setting maxRequestsPerHostStr to change the max concurrent connections to Kubernetes API + +## 3.1.1 + +Update Jenkins image and appVersion to jenkins lts release version 2.263.2 + +## 3.1.0 + +* Added `.Values.controller.podSecurityContextOverride` and `.Values.backup.podSecurityContextOverride`. +* Added simple default values tests for `jenkins-backup-cronjob.yaml`. + +## 3.0.14 + +Enable to only backup job folder instead of whole jenkins + +## 3.0.13 + +Improve Documentation around JCasc and Custom Image + +## 3.0.12 + +Added GitHub Action testing on Kind 1.16, 1.17, 1.18, 1.19 & 1.20 + +## 3.0.11 + +Fixes & unit tests for Ingress resources on Kubernetes 1.19 and above + +## 3.0.10 + +Ingress resources on Kubernetes 1.19 (or above) are created with the version `networking.k8s.io/v1` + +## 3.0.9 + +Added support for backing up to Azure Blob Storage. + +## 3.0.8 + +* Typo in documentation + +## 3.0.7 + +* Add support for setting default agent workspaceVolume + +## 3.0.6 + +Use 2.263.1 image + +## 3.0.5 + +* Update appVersion to reflect new jenkins lts release version 2.263.1 + +## 3.0.4 + +* Fix documentation for additional secret mounts + +## 3.0.3 + +* Update `README.md` with explanation on how to mount additional secrets + +## 3.0.2 + +* Fix `.Values.controller.tolerations` and `.Values.controller.nodeSelector` variable names in templates\jenkins-backup-cronjob.yaml + +## 3.0.1 + +* added 'runAsNonroot' to security context + +## 3.0.0 + +* Chart uses StatefulSet instead of Deployment +* XML configuration was removed in favor of JCasC +* chart migrated to helm 3.0.0 (apiVersion v2) +* offending terms have been removed +* values have been renamed and re-ordered to make it easier to use +* already deprecated items have been removed +* componentName for the controller is now `jenkins-controller` +* componentName for the agent is now `jenkins-agent` +* container names are now + * `init` for the init container which downloads Jenkins plugins + * `jenkins` for the Jenkins controller + * `config-reload` for the sidecar container which automatically reloads JCasC +* Updated UI tests to use official `bats/bats` image instead of `dduportal/bats` + +For migration instructions from previous versions and additional information check README.md. + +## 2.19.0 + +* Use lts version 2.249.3 +* Update kubernetes, workflow-aggregator, git and configuration-as-code plugins. +* Fail apply_config.sh script if an error occurs. + +## 2.18.2 + +Fix: `master.javaOpts` issue with quoted values + +## 2.18.1 + +Recommend installing plugins in custom image + +## 2.18.0 + +Removed /tmp volume. Making /tmp a volume causes permission issues with jmap/jstack on certain Kubernetes clusters + +## 2.17.1 + +Fix location of jenkins.war file. +It is located in `/usr/share/jenkins/jenkins.war` and can be fonfigured via `master.jenkinsWar`. + +## 2.17.0 + +Add support for plugin-installation-manager-tool + +## 2.16.0 + +Added Startup probe for Jenkins pod when Kubernetes cluster is 1.16 or newer + +## 2.15.5 + +scriptApproval is taken into account when enableXmlConfig is false. + +## 2.15.4 + +Add Tilt support for easier helm chart development. + +## 2.15.3 + +Fix error on missing `ingress.paths` value + +## 2.15.2 + +Added documentation for ingress and jenkins URL + +## 2.15.1 + +Fix priorityClassName entry in values.yaml file + +## 2.15.0 + +Added support for disabling the helm.sh/chart annotation + +## 2.14.0 + +Added support for annotations in podTemplates + +## 2.13.2 + +Add nodeSelector in the backup pod +Fix tolerations in the backup pod + +## 2.13.1 + +Update list of maintainers + +## 2.13.0 + +Added Support for websockets in the default Jcasc config +Added trailing slash to JENKINS_URL env var + +## 2.12.2 + +Added unit tests for most resources in the Helm chart. + +## 2.12.1 + +Helm chart readme update + +## 2.12.0 + +Add option to configure securityContext capabilities + +## 2.11.0 + +Added configurable security context for jenkins backup CronJob and annotations to its serviceaccount. + +## 2.10.0 + +Make activeDeadlineSeconds for backup job configurable + +## 2.9.0 + +Make namespace of PrometheusRule configurable + +## 2.8.2 + +Bumped configuration-as-code plugin version from 1.41 to 1.43. +See [configuration-as-code plugin issue #1478](https://github.com/jenkinsci/configuration-as-code-plugin/issues/1478) + +## 2.8.1 + +Fix indentation of JAVA_OPTS + +## 2.8.0 + +Add support for helm unittest and include first tests + +## 2.7.2 + +Target port of container `jenkins-sc-config` taken the value from values.yaml. + +## 2.7.0 + +Add a secondary ingress template for those who want a second ingress with different labels or annotations or whatever else. + +Example: You want /github-webhook to be on a public ingress, while the main Jenkins intance to be on a private locked down ingress. + +## 2.6.5 + +Update configScripts example + +## 2.6.4 + +Add timja as a maintainer + +## 2.6.3 + +Update k8s-sidecar image to 0.1.193 + +## 2.6.2 + +Only mount empty dir secrets-dir if either `master.enableXmlConfig` or `master.secretsFilesSecret` is set +Fixes #19 + +## 2.6.1 Do not render empty JCasC templates + +## 2.6.0 First release in jenkinsci GitHub org + +Updated readme for new location + +## 2.5.2 + +Fix as per JENKINS-47112 + +## 2.5.1 + +Support Jenkins Resource Root URL + +## 2.5.0 + +Add an option to specify that Jenkins master should be initialized only once, during first install. + +## 2.4.1 + +Reorder readme parameters into sections to facilitate chart usage and maintenance + +## 2.4.0 Update default agent image + +`jenkins/jnlp-slave` is deprected and `jenkins/inbound-agent` should be used instead. +Also updated it to newest version (4.3-4). + +## 2.3.3 correct templating of master.slaveJenkinsUrl + +Fixes #22708 + +## 2.3.2 Fix wrong value for overwritePluginsFromImage + +Fixes #23003 +Fixes #22633 + +Also fixes indentation for #23114 + +## 2.3.1 + +Always mount {{ .Values.master.jenkinsRef }}/secrets/ directory. Previous it +was mounted only when `master.enableXmlConfig` was enabled. + +## 2.3.0 + +Add an option to specify pod based on labels that can connect to master if NetworkPolicy is enabled + +## 2.2.0 increase retry for config auto reload + +Configure `REQ_RETRY_CONNECT` to `10` to give Jenkins more time to start up. + + +Value can be configured via `master.sidecars.configAutoReload.reqRetryConnect` + +## 2.1.2 updated readme + +## 2.1.1 update credentials-binding plugin to 1.23 + +## 2.1.0 + +Add support to set `runAsUser` and `runAsGroup` for `agent`. + +## 2.0.1 + +Only render authorizationStrategy and securityRealm when values are set. + +## 2.0.0 Configuration as Code now default + container does not run as root anymore + +The readme contains more details for this update. +Please note that the updated values contain breaking changes. + +## 1.27.0 Update plugin versions & sidecar container + +| plugin | old version | new version | +| --------------------- | ----------- | ----------- | +| kubernetes | 1.25.3 | 1.25.7 | +| workflow-job | 2.38 | 2.39 | +| credentials-binding | 1.21 | 1.22 | +| configuration-as-code | 1.39 | 1.41 | + +configAutoReload container updated from `kiwigrid/k8s-sidecar:0.1.132` to `kiwigrid/k8s-sidecar:0.1.144` + +## 1.26.0 + +Add support to override `workingDir` for default pod template + +## 1.25.0 + +Add support for installing plugins in addition to the chart's default plugins via `master.additionalPlugins` + +## 1.24.0 + +Allow configuration of yamlMergeStrategy via `agent.yamlMergeStrategy` + +## 1.23.2 + +In the `jenkins.xml.podTemplate` helper function, allow templating of all string values under `agent.volumes` except `type` by rendering them with the `tpl` function + +## 1.23.1 + +Added auto detection for Ingress API version + +## 1.23.0 + +Allow to use an existing secret for the jenkins admin credentials + +## 1.22.0 + +Add support for UI security in the default JCasC via `master.JCasC.securityRealm` and `master.JCasC.authorizationStrategy` which deny anonymous access by default + +## 1.21.3 + +Render `agent.envVars` in kubernetes pod template JCasC + +## 1.21.2 + +Cleanup `agent.yamlTemplate` rendering in kubernetes pod template XML configuration + +## 1.21.1 + +Render `agent.nodeSelector` in the kubernetes pod template JCasC + +## 1.21.0 + +Add support for overriding Ingress paths via `master.ingress.paths` + +## 1.20.0 + +Add the following options for configuring the Kubernetes plugin. + +- master.slaveDefaultsProviderTemplate +- master.slaveJenkinsUrl +- master.slaveJenkinsTunnel +- master.slaveConnectTimeout +- master.slaveReadTimeout + +## 1.19.0 + +Add support for disabling remember me via `master.disableRememberMe` +Add support for using a different markup formatter via `master.markupFormatter` + +## 1.18.1 + +Add support for executor mode configuraton with `master.executorMode`. + +## 1.18.0 Make installation of configuration-as-code plugin explicit + +Instead of configuring the configuration-as-code plugin version via +`master.JCasC.pluginVersion` it is now installed via `master.installPlugins` + +## 1.17.2 + +Allow templating of `serviceAccount.annotations` and `serviceAccountAgent.annotations` by rendering them with the `tpl` function + +## 1.17.1 + +Add support for Persistent Volume Claim (PVC) in `agent.volumes` + +## 1.17.0 + +Render `agent.volumes` in kubernetes pod template JCasC + +## 1.16.2 + +Reverts 1.16.1 as it introduced an error #22047 + +## 1.16.1 + +Fixed a bug with master.runAsUser variable due to use wrong type for comparison. + +## 1.16.0 + +Add `master.overwritePluginsFromImage` to allow support for jenkins plugins installed in the master image to persist. + +## 1.15.0 Update plugin versions & sidecar container + +| plugin | old version | new version | +| --------------------- | ----------- | ----------- | +| kubernetes | 1.25.1 | 1.25.3 | +| workflow-job | 2.36 | 2.38 | +| git | 4.2.0 | 4.2.2 | +| configuration-as-code | 1.36 | 1.39 | + +configAutoReload container updated from `kiwigrid/k8s-sidecar:0.1.20` to `kiwigrid/k8s-sidecar:0.1.132` + +## 1.14.0 + +support auto-reload container environment variables configuration + +## 1.13.3 + +Fix wrong indent in tolerations + +## 1.13.2 + +Add support for custom ClusterIP + +## 1.13.1 + +Fix `agent.yamlTemplate` rendering in kubernetes pod template JCasC + +## 1.13.0 + +Add `master.networkPolicy.internalAgents` and `master.networkPolicy.externalAgents` stanzas to fine grained controls over where internal/external agents can connect from. Internal ones are allowed based on pod labels and (optionally) namespaces, and external ones are allowed based on IP ranges. + +## 1.12.0 Support additional agents + +Add support for easy configuration of additional agents which inherit values from `agent`. + +## 1.11.3 + +Update the kubernetes plugin from 1.24.1 to 1.25.1 and grant 'watch' permission to 'events' which is required since this plugin version. + +## 1.11.2 Configure agent.args in values.yaml + +## 1.11.1 Support for master.additionalConfig + +Fixed a bug with jenkinsHome variable in range block when master.additionalConfig is set - Helm cannot evaluate field Values in type interface {}. + +## 1.11.0 Add support for configuring custom pod templates + +Add `agent.podTemplates` option for declaring custom pod templates in the default configured kubernetes cloud. + +## 1.10.1 Only copy JCasC files if there are any + +The chart always tried to copy Configuration as Code configs even if there are none. That resulted in an error which is resolved with this. + +## 1.10.0 Remove configuration-as-code-support plugins + +In recent version of configuration-as-code-plugin this is no longer necessary. + +## 1.9.24 + +Update JCasC auto-reload docs and remove stale SSH key references from version "1.8.0 JCasC auto reload works without SSH keys" + +## 1.9.23 Support jenkinsUriPrefix when JCasC is enabled + +Fixed a bug in the configuration as code reload URL, where it wouldn't work with a jenkinsUriPrefix set. + +## 1.9.22 + +Add `master.jenkinsHome` and `master.jenkinsRef` options to use docker images derivates from Jenkins + +## 1.9.21 + +Add `master.terminationGracePeriodSeconds` option + +## 1.9.20 + +Update default plugins + +- kubernetes:1.24.1 +- workflow-job:2.36 +- workflow-aggregator:2.6 +- credentials-binding:1.21 +- git:4.2.0 +- configuration-as-code:1.36 + +## 1.9.19 + +Update docs for Helm 3 + +## 1.9.18 + +Make `jenkins-home` attachable to Azure Disks without pvc + +```yaml + volumes: + - name: jenkins-home + azureDisk: + kind: Managed + diskName: myAKSDisk + diskURI: /subscriptions//resourceGroups/MC_myAKSCluster_myAKSCluster_eastus/providers/Microsoft.Compute/disks/myAKSDisk +``` + +## 1.9.16 + +Fix PodLabel for NetworkPolicy to work if enabled + +## 1.9.14 + +Properly fix case sense in `Values.master.overwriteConfig` in `config.yaml` + +## 1.9.13 + +Fix case sense in `Values.master.overwriteConfig` in `config.yaml` + +## 1.9.12 + +Scriptapprovals are overwritten when overwriteConfig is enabled + +## 1.9.10 + +Added documentation for `persistence.storageClass`. + +## 1.9.9 +Make `master.deploymentAnnotation` configurable. + +## 1.9.8 + +Make `agent.slaveConnectTimeout` configurable: by increasing this value Jenkins will not cancel&ask k8s for a pod again, while it's on `ContainerCreating`. Useful when you have big images or autoscaling takes some time. + +## 1.9.7 Update plugin versions + +| plugin | old version | new version | +|-----------------------|-------------|-------------| +| kubernetes | 1.18.2 | 1.21.2 | +| workflow-job | 2.33 | 2.36 | +| credentials-binding | 1.19 | 1.20 | +| git | 3.11.0 | 4.0.0 | +| configuration-as-code | 1.27 | 1.32 | + +## 1.9.6 + +Enables jenkins to use keystore inorder to have native ssl support #17790 + +## 1.9.5 Enable remoting security + +`Manage Jenkins` -> `Configure Global Security` -> `Enable Agent → Master Access Control` is now enabled via configuration as code plugin + +## 1.9.4 Option to set existing secret with Google Application Default Credentials + +Google application credentials are kept in a file, which has to be mounted to a pod. You can set `gcpcredentials` in `existingSecret` as follows: + +```yaml + existingSecret: + jenkins-service-account: + gcpcredentials: application_default_credentials.json +``` + +Helm template then creates the necessary volume mounts and `GOOGLE_APPLICATION_CREDENTIALS` environmental variable. + +## 1.9.3 Fix `JAVA_OPTS` when config auto-reload is enabled + +## 1.9.2 Add support for kubernetes-credentials-provider-plugin + +[kubernetes-credentials-provider-plugin](https://jenkinsci.github.io/kubernetes-credentials-provider-plugin/) needs permissions to get/watch/list kubernetes secrets in the namespaces where Jenkins is running. + +The necessary role binding can be created using `rbac.readSecrets` when `rbac.create` is `true`. + +To quote from the plugin documentation: + +> Because granting these permissions for secrets is not something that should be done lightly it is highly advised for security reasons that you both create a unique service account to run Jenkins as, and run Jenkins in a unique namespace. + +Therefor this is disabled by default. + +## 1.9.1 Update kubernetes plugin URL + +## 1.9.0 Change default serviceType to ClusterIP + +## 1.8.2 + +Revert fix in `1.7.10` since direct connection is now disabled by default. + +## 1.8.1 + +Add `master.schedulerName` to allow setting a Kubernetes custom scheduler + +## 1.8.0 JCasC auto reload works without SSH keys + +We make use of the fact that the Jenkins Configuration as Code Plugin can be triggered via http `POST` to `JENKINS_URL/configuration-as-code/reload`and a pre-shared key. +The sidecar container responsible for reloading config changes is now `kiwigrid/k8s-sidecar:0.1.20` instead of it's fork `shadwell/k8s-sidecar`. + +References: + +- [Triggering Configuration Reload](https://github.com/jenkinsci/configuration-as-code-plugin/blob/master/docs/features/configurationReload.md) +- [kiwigrid/k8s-sidecar](https://hub.docker.com/r/kiwigrid/k8s-sidecar) + +`master.sidecars.configAutoReload.enabled` now works using `casc.reload.token` + +## 1.7.10 + +Disable direct connection in default configuration (when kubernetes plugin version >= 1.20.2). +Note: In case direct connection is going to be used `jenkins/jnlp-slave` needs to be version `3.35-5` or newer. + +## 1.7.9 + +Prevented Jenkins Setup Wizard on new installations + +## 1.7.8 + +Extend extraPorts to be opened on the Service object, not just the container. + +## 1.7.7 + +Add persistentvolumeclaim permission to the role to support new dynamic pvc workspaces. + +## 1.7.6 + +Updated `master.slaveKubernetesNamespace` to parse helm templates. +Defined an sensible empty value to the following variables, to silence invalid warnings: + +- master.extraPorts +- master.scriptApproval +- master.initScripts +- master.JCasC.configScripts +- master.sidecars.other +- agent.envVars +- agent.volumes + +## 1.7.5 + +Fixed an issue where the JCasC won't run if JCasC auto-reload is enabled [issue #17135](https://github.com/helm/charts/issues/17135) + +## 1.7.4 + +Comments out JCasC example of jenkins.systemMessage so that it can be used by end users. Previously, an attempt to set systemMessage causes Jenkins to startup, citing duplicate JCasC settings for systemMessage [issue #13333](https://github.com/helm/charts/issues/13333) + +## 1.7.2 + +Update kubernetes-plugin to version 1.18.2 which fixes frequently encountered [JENKINS-59000](https://issues.jenkins-ci.org/plugins/servlet/mobile#issue/JENKINS-59000) + +## 1.7.1 + +Update the default requirements for jenkins-agent to 512Mi which fixes frequently encountered [issue #3723](https://github.com/helm/charts/issues/3723) + +## 1.7.0 + +[Jenkins Configuration as Code Plugin](https://github.com/jenkinsci/configuration-as-code-plugin) default configuration can now be enabled via `master.JCasC.defaultConfig`. + +JCasC default configuration includes: + +- Jenkins URL +- Admin email `master.jenkinsAdminEmail` +- crumbIssuer +- disableRememberMe: false +- mode: NORMAL +- numExecutors: {{ .Values.master.numExecutors }} +- projectNamingStrategy: "standard" +- kubernetes plugin + - containerCapStr via `agent.containerCap` + - jenkinsTunnel + - jenkinsUrl + - maxRequestsPerHostStr: "32" + - name: "kubernetes" + - namespace + - serverUrl: `"https://kubernetes.default"` + - template + - containers + - alwaysPullImage: `agent.alwaysPullImage` + - args + - command + - envVars + - image: `agent.image:agent.imageTag` + - name: `.agent.sideContainerName` + - privileged: `.agent.privileged` + - resourceLimitCpu: `agent.resources.limits.cpu` + - resourceLimitMemory: `agent.resources.limits.memory` + - resourceRequestCpu: `agent.resources.requests.cpu` + - resourceRequestMemory: `agent.resources.requests.memory` + - ttyEnabled: `agent.TTYEnabled` + - workingDir: "/home/jenkins" + - idleMinutes: `agent.idleMinutes` + - instanceCap: 2147483647 + - imagePullSecrets: + - name: `.agent.imagePullSecretName` + - label + - name + - nodeUsageMode: "NORMAL" + - podRetention: `agent.podRetention` + - serviceAccount + - showRawYaml: true + - slaveConnectTimeoutStr: "100" + - yaml: `agent.yamlTemplate` + - yamlMergeStrategy: "override" +- security: + - apiToken: + - creationOfLegacyTokenEnabled: false + - tokenGenerationOnCreationEnabled: false + - usageStatisticsEnabled: true + +Example `values.yaml` which enables JCasC, it's default config and configAutoReload: + +```yaml +master: + JCasC: + enabled: true + defaultConfig: true + sidecars: + configAutoReload: + enabled: true +``` + +add master.JCasC.defaultConfig and configure location + +- JCasC configuration is stored in template `jenkins.casc.defaults` + so that it can be used in `config.yaml` and `jcasc-config.yaml` + depending on if configAutoReload is enabled or not + +- Jenkins Location (URL) is configured to provide a startin point + for the config + +## 1.6.1 + +Print error message when `master.sidecars.configAutoReload.enabled` is `true`, but the admin user can't be found to configure the SSH key. + +## 1.6.0 + +Add support for Google Cloud Storage for backup CronJob (migrating from nuvo/kube-tasks to maorfr/kube-tasks) + +## 1.5.9 + +Fixed a warning when sidecar resources are provided through a parent chart or override values + +## 1.5.8 + +Fixed an issue when master.enableXmlConfig is set to false: Always mount jenkins-secrets volume if secretsFilesSecret is set (#16512) + +## 1.5.7 + +added initial changelog (#16324) +commit: cee2ebf98 + +## 1.5.6 + +enable xml config misspelling (#16477) +commit: a125b99f9 + +## 1.5.5 + +Jenkins master label (#16469) +commit: 4802d14c9 + +## 1.5.4 + +add option enableXmlConfig (#16346) +commit: 387d97a4c + +## 1.5.3 + +extracted "jenkins.URL" into template (#16347) +commit: f2fdf5332 + +## 1.5.2 + +Fix backups when deployment has custom name (#16279) +commit: 16b89bfff + +## 1.5.1 + +Ability to set custom namespace for ServiceMonitor (#16145) +commit: 18ee6cf01 + +## 1.5.0 + +update Jenkins plugins to fix security issue (#16069) +commit: 603cf2d2b + +## 1.4.3 + +Use fixed container name (#16068) +commit: b3e4b4a49 + +## 1.4.2 + +Provide default job value (#15963) +commit: c462e2017 + +## 1.4.1 + +Add Jenkins backendconfig values (#15471) +commit: 7cc9b54c7 + +## 1.4.0 + +Change the value name for docker image tags - standartise to helm preferred value name - tag; this also allows auto-deployments using weaveworks flux (#15565) +commit: 5c3d920e7 + +## 1.3.6 + +jenkins deployment port should be target port (#15503) +commit: 83909ebe3 + +## 1.3.5 + +Add support for namespace specification (#15202) +commit: e773201a6 + +## 1.3.4 + +Adding sub-path option for scraping (#14833) +commit: e04021154 + +## 1.3.3 + +Add existingSecret to Jenkins backup AWS credentials (#13392) +commit: d9374f57d + +## 1.3.2 + +Fix JCasC version (#14992) +commit: 26a6d2b99 + +## 1.3.1 + +Update affinity for a backup cronjob (#14886) +commit: c21ed8331 + +## 1.3.0 + +only install casc support plugin when needed (#14862) +commit: a56fc0540 + +## 1.2.2 + +DNS Zone customization (#14775) +commit: da2910073 + +## 1.2.1 + +only render comment if configAutoReload is enabled (#14754) +commit: e07ead283 + +## 1.2.0 + +update plugins to latest version (#14744) +commit: 84336558e + +## 1.1.24 + +add example for EmptyDir volume (#14499) +commit: cafb60209 + +## 1.1.23 + +check if installPlugins is set before using it (#14168) +commit: 1218f0359 + +## 1.1.22 + +Support servicemonitor and alerting rules (#14124) +commit: e15a27f48 + +## 1.1.21 + +Fix: healthProbe timeouts mapping to initial delay (#13875) +commit: 825b32ece + +## 1.1.20 + +Properly handle overwrite config for additional configs (#13915) +commit: 18ce9b558 + +## 1.1.18 + +update maintainer (#13897) +commit: 223002b27 + +## 1.1.17 + +add apiVersion (#13795) +commit: cd1e5c35a + +## 1.1.16 + +allow changing of the target port to support TLS termination sidecar (#13576) +commit: a34d3bbcc + +## 1.1.15 + +fix wrong pod selector in jenkins-backup (#13542) +commit: b5df4fd7e + +## 1.1.14 + +allow templating of customInitContainers (#13536) +commit: d1e1421f4 + +## 1.1.13 + +fix #13467 (wrong deprecation message) (#13511) +commit: fbe28fa1c + +## 1.1.12 + +Correct customInitContainers Name example. (#13405) +commit: 6c6e40405 + +## 1.1.11 + +fix master.runAsUser, master.fsGroup examples (#13389) +commit: 2d7e5bf72 + +## 1.1.10 + +Ability to specify raw yaml template (#13319) +commit: 77aaa9a5f + +## 1.1.9 + +correct NOTES.txt - use master.ingress.hostname (#13318) +commit: b08ef6280 + +## 1.1.8 + +explain how to upgrade major versions (#13273) +commit: e7617a97e + +## 1.1.7 + +Add support for idleMinutes and serviceAccount (#13263) +commit: 4595ee033 + +## 1.1.6 + +Use same JENKINS_URL no matter if slaves use different namespace (#12564) +commit: 94c90339f + +## 1.1.5 + +fix deprecation checks (#13224) +commit: c7d2f8105 + +## 1.1.4 + +Fix issue introduced in #13136 (#13232) +commit: 0dbcded2e + +## 1.1.3 + +fix chart errors (#13197) +commit: 692a1e3da + +## 1.1.2 + +correct selector for jenkins pod (#13200) +commit: 4537e7fda + +## 1.1.1 + +Fix rendering of customInitContainers and lifecycle for Jenkins helm chart (#13189) +commit: e8f6b0ada + +## 1.1.0 + +Add support for openshift route in jenkins (#12973) +commit: 48c58a430 + +## 1.0.0 + +helm chart best practices (#13136) +commit: b02ae3f48 + +### Breaking changes + +- values have been renamed to follow helm chart best practices for naming conventions so + that all variables start with a lowercase letter and words are separated with camelcase + +- all resources are now using recommended standard labels + + +As a result of the label changes also the selectors of the deployment have been updated. +Those are immutable so trying an updated will cause an error like: + +```text +Error: Deployment.apps "jenkins" is invalid: spec.selector: Invalid value: v1.LabelSelector{MatchLabels:map[string]string{"app.kubernetes.io/component":"jenkins-master", "app.kubernetes.io/instance":"jenkins"}, MatchExpressions:[]v1.LabelSelectorRequirement(nil)}: field is immutable +``` + +In order to upgrade, delete the Jenkins Deployment before upgrading: + +```console +kubectl delete deploy jenkins +``` + +## 0.40.0 + +Allow to override jenkins location protocol (#12257) +commit: 18a830626 + +## 0.39.0 + +Add possibility to add custom init-container and lifecycle for master-container (#13062) +commit: 14d043593 + +## 0.38.0 + +Support `priorityClassName` on Master Deployment (#13069) +commit: e896c62bc + +## 0.37.3 + +Add support for service account annotations in jenkins (#12969) +commit: b22774e2f + +## 0.37.2 + +fix: add hostName to ingress in values.yaml (#12946) +commit: 041045e9b + +## 0.37.1 + +Update to match actual defaults in value.yaml (#12904) +commit: 73b6d37eb + +## 0.37.0 + +Support multiple Jenkins instances in same namespace (#12748) +commit: 32ff2f343 + +## 0.36.5 + +Fix wrong comment in values.yaml (#12761) +commit: 9db8ced23 + +## 0.36.4 + +Re-add value for Ingress API Version (#12753) +commit: ecb7791b5 + +## 0.36.3 + +allow templating of volumes (#12734) +commit: adbda2ca6 + +## 0.36.2 + +Fix self-introduced whitespace bug (#12528) +commit: eec1678eb + +## 0.36.1 + +Add flag to overwrite jobs definition from values.yaml (#12427) +commit: fd349b2fc + +## 0.36.0 + +Replace OwnSshKey with AdminSshKey (#12140) (#12466) +commit: 80a8c9eb6 + +## 0.35.2 + +add note for breaking changes (#12203) +commit: e779c5a54 + +## 0.35.1 + +Allow Jenkins to run with READONLYROOTFS psp (#12338) +commit: 7c419e191 + +## 0.35.0 + +Jenkins OverwriteConfig setting also overwrites init scripts (#9468) +commit: 501335b76 + +## 0.34.1 + +Fix typo on hostname variable (#12156) +commit: 3d337d8dd + +## 0.34.0 + +Allow ingress without host rule (#11960) +commit: ddc966d1e + +## 0.33.2 + +Improve documentation - clarify that rbac is needed for autoreload (#11739) +commit: 9d75a5c34 + +## 0.33.1 + +use object for rollingUpdate (#11909) +commit: cb9cf21e8 + +## 0.33.0 + +Add hostAliases (#11701) +commit: 0b89e1094 + +## 0.32.10 + +Fix slave jnlp port always being reset when container is restarted (#11685) +commit: d7d51797b + +## 0.32.9 + +add ingress Hostname an ApiVersion to docs (#11576) +commit: 4d3e77137 + +## 0.32.8 + +Support custom master pod labels in deployment (#9714) (#11511) +commit: 9de96faa0 + +## 0.32.7 + +Fix Markdown syntax in readme (#11496) +commit: a32221a95 + +## 0.32.6 + +Added custom labels on jenkins ingress (#11466) +commit: c875d2b9b + +## 0.32.5 + +fix typo in default jenkins agent image fixes #11356 (#11463) +commit: 30adb9a91 + +## 0.32.4 + +fix incorrect Deployment when using sidecars (#11413) +commit: 362b4cef8 + +## 0.32.3 + +[]: #10131 (#11411) +commit: 49cb72055 + +## 0.32.2 + +Option to expose the slave listener port as host port (#11187) +commit: 2f85a9663 + +## 0.32.1 + +Updating Jenkins deployment fails appears rollingUpdate needs to be (#11166) +commit: 07fc9dbde + +## 0.32.0 + +Merge Sidecard configs (#11339) +commit: 3696090b9 + +## 0.31.0 + +Add option to overwrite plugins (#11231) +commit: 0e9aa00a5 + +## 0.30.0 + +Added slave Pod env vars (#8743) +commit: 1499f6608 + +## 0.29.3 + +revert indentation to previous working version (#11293) +commit: 61662f17a + +## 0.29.2 + +allow running sidecar containers for Jenkins master (#10950) +commit: 9084ce54a + +## 0.29.1 + +Indent lines related to EnableRawHtmlMarkupFormatter (#11252) +commit: 20b310c08 + +## 0.29.0 + +Jenkins Configuration as Code (#9057) +commit: c3e8c0b17 + +## 0.28.11 + +Allow to enable OWASP Markup Formatter Plugin (#10851) +commit: 9486e5ddf + +## 0.28.10 + +Fixes #1341 -- update Jenkins chart documentation (#10290) +commit: 411c81cd0 + +## 0.28.9 + +Quoted JavaOpts values (#10671) +commit: 926a843a8 + +## 0.28.8 + +Support custom labels in deployment (#9714) (#10533) +commit: 3e00b47fa + +## 0.28.7 + +separate test resources (#10597) +commit: 7b7ae2d11 + +## 0.28.6 + +allow customizing livenessProbe periodSeconds (#10534) +commit: 3c94d250d + +## 0.28.5 + +Add role kind option (#8498) +commit: e791ad124 + +## 0.28.4 + +workaround for busybox's cp (Closes: #10471) (#10497) +commit: 0d51a4187 + +## 0.28.3 + +fix parsing java options (#10140) +commit: 9448d0293 + +## 0.28.2 + +Fix job definitions in standard values.yaml (#10184) +commit: 6b6355ae7 + +## 0.28.1 + +add numExecutors as a variable in values file (#10236) +commit: d5ea2050f + +## 0.28.0 + +various (#10223) +commit: e17d2a65d + +## 0.27.0 + +add backup cronjob (#10095) +commit: 863ead8db + +## 0.26.2 + +add namespace flag for port-forwarding in jenkins notes (#10399) +commit: 846b589a9 + +## 0.26.1 + +- fixes #10267 when executed with helm template - otherwise produces an invalid template. (#10403) + commit: 266f9d839 + +## 0.26.0 + +Add subPath for jenkins-home mount (#9671) +commit: a9c76ac9b + +## 0.25.1 + +update readme to indicate the correct image that is used by default (#9915) +commit: 6aba9631c + +## 0.25.0 + +Add ability to manually set Jenkins URL (#7405) +commit: a0178fcb4 + +## 0.24.0 + +Make AuthorizationStrategy configurable (#9567) +commit: 06545b226 + +## 0.23.0 + +Update Jenkins public chart (#9296) +commit: 4e5f5918b + +## 0.22.0 + +allow to override jobs (#9004) +commit: dca9f9ab9 + +## 0.21.0 + +Simple implementation of the option to define the ingress path to the jenkins service (#8101) +commit: 013159609 + +## 0.20.2 + +Cosmetic change to remove necessity of changing "appVersion" for every new LTS release (#8866) +commit: f52af042a + +## 0.20.1 + +Added ExtraPorts to open in the master pod (#7759) +commit: 78858a2fb + +## 0.19.1 + +Fix component label in NOTES.txt ... (#8300) +commit: c5494dbfe + +## 0.19.0 + +Kubernetes 1.9 support as well as automatic apiVersion detection (#7988) +commit: 6853ad364 + +## 0.18.1 + +Respect SlaveListenerPort value in config.xml (#7220) +commit: 0a5ddac35 + +## 0.18.0 + +Allow replacement of Jenkins config with configMap. (#7450) +commit: c766da3de + +## 0.17.0 + +Add option to allow host networking (#7530) +commit: dc2eeff32 + +## 0.16.25 + +add custom jenkins labels to the build agent (#7167) +commit: 3ecde5dbf + +## 0.16.24 + +Move kubernetes and job plugins to latest versions (#7438) +commit: 019e39456 + +## 0.16.23 + +Add different Deployment Strategies based on persistence (#6132) +commit: e0a20b0b9 + +## 0.16.22 + +avoid linting errors when adding Values.Ingress.Annotations (#7425) +commit: 99eacc854 + +## 0.16.21 + +bump appVersion to reflect new jenkins lts release version 2.121.3 (#7217) +commit: 296df165d + +## 0.16.20 + +Configure kubernetes plugin for including namespace value (#7164) +commit: c0dc6cc48 + +## 0.16.19 + +make pod retention policy setting configurable (#6962) +commit: e614c1033 + +## 0.16.18 + +Update plugins version (#6988) +commit: bf8180018 + +## 0.16.17 + +Add Master.AdminPassword in readme (#6987) +commit: 13e754ad7 + +## 0.16.16 + +Added jenkins location configuration (#6573) +commit: 79de7026c + +## 0.16.15 + +use generic env var, not oracle specific env var (#6116) +commit: 6084ab4a4 + +## 0.16.14 + +Allow to specify resource requests and limits on initContainers (#6723) +commit: 942a33b1a + +## 0.16.13 + +Added support for NodePort service type for jenkens agent svc (#6571) +commit: 89a213c2b + +## 0.16.12 + +Added ability to configure multiple LoadBalancerSourceRanges (#6243) +commit: 01604ddbc + +## 0.16.11 + +Removing ContainerPort configuration as at the moment it does not work when you change this setting (#6411) +commit: e1c0468bd + +## 0.16.9 + +Fix jobs parsing for configmap by adding toYaml to jobs.yaml template (#3747) +commit: b2542a123 + +## 0.16.8 + +add jenkinsuriprefix in healthprobes (#5737) +commit: 435d7a7b9 + +## 0.16.7 + +Added the ability to switch from ClusterRoleBinding to RoleBinding. (#6190) +commit: dde03ede0 + +## 0.16.6 + +Make jenkins master pod security context optional (#6122) +commit: 63653fd59 + +## 0.16.5 + +Rework resources requests and limits (#6077) (#6077) +commit: e738f99d0 + +## 0.16.4 + +Add jenkins master pod annotations (#6313) +commit: 5e7325721 + +## 0.16.3 + +Split Jenkins readiness and liveness probe periods (#5704) +commit: fc6100c38 + +## 0.16.1 + +fix typo in jenkins readme (#5228) +commit: 3cd3f4b8b + +## 0.16.0 + +Inherit existing plugins from Jenkins image (#5409) +commit: fd93bff82 + +## 0.15.1 + +Allow NetworkPolicy.ApiVersion and Master.Ingress.ApiVersion to Differ (#5103) +commit: 78ee4ba15 + +## 0.15.0 + +Secure Defaults (#5026) +commit: 0fe90b520 + +## 0.14.6 + +Wait for up to 2 minutes before failing liveness check (#5161) +commit: 2cd3fc481 + +## 0.14.5 + +correct ImageTag setting (#4371) +commit: 8ea04174d + +## 0.14.4 + +Update jenkins/README.md (#4559) +commit: d4e6352dd + +## 0.14.3 + +Bump appVersion (#4177) +commit: 605d3d441 + +## 0.14.2 + +Master.InitContainerEnv: Init Container Env Vars (#3495) +commit: c64abe27d + +## 0.14.1 + +Allow more configuration of Jenkins agent service (#4028) +commit: fc82f39b2 + +## 0.14.0 + +Add affinity settings (#3839) +commit: 64e82fa6a + +## 0.13.5 + +bump test timeouts (#3886) +commit: cd05dd99c + +## 0.13.4 + +Add OWNERS to jenkins chart (#3881) +commit: 1c106b9c8 + +## 0.13.3 + +Add fullnameOverride support (#3705) +commit: ec8080839 + +## 0.13.2 + +Update README.md (#3638) +commit: f6d274c37 + +## 0.13.1 + +Lower initial healthcheck delay (#3463) +commit: 9b99db67c + +## 0.13.0 + +Provision credentials.xml, secrets files and jobs (#3316) +commit: d305c5961 + +## 0.12.1 + +fix the default value for nodeUsageMode. (#3299) +commit: b68d19516 + +## 0.12.0 + +Recreate pods when CustomConfigMap is true and there are changes to the ConfigMap (which is how the vanilla chart works) (#3181) +commit: 86d29f804 + +## 0.11.1 + +Optionally adds liveness and readiness probes to jenkins (#3245) +commit: 8b9aa73ee + +## 0.11.0 + +Feature/run jenkins as non root user (#2899) +commit: 8918f4175 + +## 0.10.3 + +template the version to keep them synced (#3084) +commit: 35e7fa49a + +## 0.10.2 + +Update Chart.yaml +commit: e3e617a0b + +## 0.10.1 + +Merge branch 'master' into jenkins-test-timeout +commit: 9a230a6b1 + +Double retry count for Jenkins test +commit: 129c8e824 + +Jenkins: Update readme | Master.ServiceAnnotations (#2757) +commit: 6571810bc + +## 0.10.0 + +Update Jenkins images and plugins (#2496) +commit: 2e2622682 + +## 0.9.4 + +Updating to remove the `.lock` directory as well (#2747) +commit: 6e676808f + +## 0.9.3 + +Use variable for service port when testing (#2666) +commit: d044f99be + +## 0.9.2 + +Review jenkins networkpolicy docs (#2618) +commit: 49911e458 + +Add image pull secrets to jenkins templates (#1389) +commit: 4dfae21fd + +## 0.9.1 + +Added persistent volume claim annotations (#2619) +commit: ac9e5306e + +Fix failing CI lint (#2758) +commit: 26f709f0e + +## 0.9.0 + +namespace defined templates with chart name (#2140) +commit: 408ae0b3f + +## 0.8.9 + +added useSecurity and adminUser to params (#1903) +commit: 39d2a03cd + +Use storageClassName for jenkins. (#1997) +commit: 802f6449b + +## 0.8.8 + +Remove old plugin locks before installing plugins (#1746) +commit: 6cd7b8ff4 + +promote initContainrs to podspec (#1740) +commit: fecc804fc + +## 0.8.7 + +add optional LoadBalancerIP option. (#1568) +commit: d39f11408 + +## 0.8.6 + +Fix bad key in values.yaml (#1633) +commit: dc27e5af3 + +## 0.8.5 + +Update Jenkins to support node selectors for agents. (#1532) +commit: 4af5810ff + +## 0.8.4 + +Add support for supplying JENKINS_OPTS and/or URI prefix (#1405) +commit: 6a331901a + +## 0.8.3 + +Add serviceAccountName to deployment (#1477) +commit: 0dc349b44 + +## 0.8.2 + +Remove path from ingress specification to allow other paths (#1599) +commit: e727f6b32 + +Update git plugin to 3.4.0 for CVE-2017-1000084 (#1505) +commit: 03482f995 + +## 0.8.1 + +Use consistent whitespace in template placeholders (#1437) +commit: 912f50c71 + +add configurable service annotations #1234 (#1244) +commit: 286861ca8 + +## 0.8.0 + +Jenkins v0.8.0 (#1385) +commit: 0009a2393 + +## 0.7.4 + +Use imageTag as version in config map (#1333) +commit: e8bb6ebb4 + +## 0.7.3 + +Add NetworkPolicy to Jenkins (#1228) +commit: 572b36c6d + +## 0.7.2 + +- Workflow plugin pin (#1178) + commit: ac3a0c7bc + +## 0.7.1 + +copy over plugins.txt in case of update (#1222) +commit: 75b5b1174 + +## 0.7.0 + +add jmx option (#964) +commit: 6ae8d1945 + +## 0.6.4 + +update jenkins to latest LTS 2.46.3 (#1182) +commit: ad90b4c27 + +## 0.6.3 + +Update chart maints to gh u/n (#1107) +commit: f357b77ed + +## 0.6.2 + +Add Agent.Privileged option (#957) +commit: 2cf4aced2 + +## 0.6.1 + +Upgrade jenkins to 2.46.2 (#971) +commit: 41bd742b4 + +## 0.6.0 + +Smoke test for Jenkins Chart (#944) +commit: 110441054 + +## 0.5.1 + +removed extra space from hardcoded password (#925) +commit: 85a9b9123 + +## 0.5.0 + +move config to init-container allowing use of upstream containers (#921) +commit: 1803c3d33 + +## 0.4.1 + +add ability to toggle jnlp-agent podTemplate generation (#918) +commit: accd53203 + +## 0.4.0 + +Jenkins add script approval (#916) +commit: c1746656e + +## 0.3.1 + +Update Jenkins to Latest LTS fixes #731 (#733) +commit: e9a3aed8b + +## 0.3.0 + +Added option to add Jenkins init scripts (#617) +commit: b889623d0 + +## 0.2.0 + +Add existing PVC (#716) +commit: 05271f145 + +## 0.1.15 + +use Master.ServicePort in config.xml (#769) +commit: f351f4b16 + +## 0.1.14 + +Added option to disable security on master node (#403) +commit: 3a6113d18 + +## 0.1.13 + +Added: extra mount points support for jenkins master (#474) +commit: fab0f7eb1 + +## 0.1.12 + +fix storageclass config typo (#548) +commit: 6fc0ff242 + +## 0.1.10 + +Changed default value of Kubernetes Cloud name to match one in kubernetes plugin (#404) +commit: 68351304a + +Add support for overriding the Jenkins ConfigMap (#524) +commit: f97ca53b1 + +## 0.1.9 + +Added jenkins-master ingress support (#402) +commit: d76a09588 + +## 0.1.8 + +Change description (#553) +commit: 91f5c24e1 + +Removed default Persistence.StorageClass: generic (#530) +commit: c87494c10 + +Update to the recommended pvc patterns. (#448) +commit: a7fc595aa + +Remove helm.sh/created annotations (#505) +commit: f380da2fb + +## 0.1.7 + +add support for explicit NodePort on jenkins chart (#342) +commit: f63c188da + +Add configurable loadBalancerSourceRanges for jenkins chart (#360) +commit: 44007c50e + +Update Jenkins version to current LTS (2.19.4) and Kubernetes Plugin to 0.10 (#341) +commit: 6c8678167 + +## 0.1.6 + +Add imagePullPolicy to init container (#295) +commit: 103ee1952 + +## 0.1.5 + +bump chart version with PVC metadata label additions +commit: 4aa9cf5b1 + +## 0.1.4 + +removed `*` from `jenkins/templates/NOTES.txt` +commit: 76212230b + +apply standard metadata labels to PVC's +commit: 58b730836 + +specify namespace in `kubectl get svc` commands in NOTES.txt +commit: 7d3287e81 + +Update Jenkins version to current LTS (#194) +commit: 2c0404049 + +## 0.1.1 + +escape fixed +commit: 2026e1d15 + +.status.loadBalancer.ingress[0].ip is empty in AWS +commit: 1810e37f4 + +.status.loadBalancer.ingress[0].ip is empty in AWS +commit: 3cbd3ced6 + +Remove 'Getting Started:' from various NOTES.txt. (#181) +commit: 2f63fd524 + +docs(\*): update readmes to reference chart repos (#119) +commit: c7d1bff05 + +## 0.1.0 + +Move first batch of PVC charts to stable +commit: d745f4879 diff --git a/charts/jenkins/jenkins/5.6.2/Chart.yaml b/charts/jenkins/jenkins/5.6.2/Chart.yaml new file mode 100644 index 000000000..b3322310b --- /dev/null +++ b/charts/jenkins/jenkins/5.6.2/Chart.yaml @@ -0,0 +1,54 @@ +annotations: + artifacthub.io/category: integration-delivery + artifacthub.io/changes: | + - Update `kubernetes` to version `4288.v1719f9d0c854` + artifacthub.io/images: | + - name: jenkins + image: docker.io/jenkins/jenkins:2.462.2-jdk17 + - name: k8s-sidecar + image: docker.io/kiwigrid/k8s-sidecar:1.27.6 + - name: inbound-agent + image: jenkins/inbound-agent:3261.v9c670a_4748a_9-1 + artifacthub.io/license: Apache-2.0 + artifacthub.io/links: | + - name: Chart Source + url: https://github.com/jenkinsci/helm-charts/tree/main/charts/jenkins + - name: Jenkins + url: https://www.jenkins.io/ + - name: support + url: https://github.com/jenkinsci/helm-charts/issues + catalog.cattle.io/certified: partner + catalog.cattle.io/display-name: Jenkins + catalog.cattle.io/kube-version: '>=1.14-0' + catalog.cattle.io/release-name: jenkins +apiVersion: v2 +appVersion: 2.462.2 +description: 'Jenkins - Build great things at any scale! As the leading open source + automation server, Jenkins provides over 1800 plugins to support building, deploying + and automating any project. ' +home: https://www.jenkins.io/ +icon: file://assets/icons/jenkins.svg +keywords: +- jenkins +- ci +- devops +kubeVersion: '>=1.14-0' +maintainers: +- email: maor.friedman@redhat.com + name: maorfr +- email: mail@torstenwalter.de + name: torstenwalter +- email: garridomota@gmail.com + name: mogaal +- email: wmcdona89@gmail.com + name: wmcdona89 +- email: timjacomb1@gmail.com + name: timja +name: jenkins +sources: +- https://github.com/jenkinsci/jenkins +- https://github.com/jenkinsci/docker-inbound-agent +- https://github.com/maorfr/kube-tasks +- https://github.com/jenkinsci/configuration-as-code-plugin +type: application +version: 5.6.2 diff --git a/charts/jenkins/jenkins/5.6.2/README.md b/charts/jenkins/jenkins/5.6.2/README.md new file mode 100644 index 000000000..7a7c7b5a7 --- /dev/null +++ b/charts/jenkins/jenkins/5.6.2/README.md @@ -0,0 +1,711 @@ +# Jenkins + +[![Artifact Hub](https://img.shields.io/endpoint?url=https://artifacthub.io/badge/repository/jenkins)](https://artifacthub.io/packages/helm/jenkinsci/jenkins) +[![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0) +[![Releases downloads](https://img.shields.io/github/downloads/jenkinsci/helm-charts/total.svg)](https://github.com/jenkinsci/helm-charts/releases) +[![Join the chat at https://app.gitter.im/#/room/#jenkins-ci:matrix.org](https://badges.gitter.im/badge.svg)](https://app.gitter.im/#/room/#jenkins-ci:matrix.org) + +[Jenkins](https://www.jenkins.io/) is the leading open source automation server, Jenkins provides over 1800 plugins to support building, deploying and automating any project. + +This chart installs a Jenkins server which spawns agents on [Kubernetes](http://kubernetes.io) utilizing the [Jenkins Kubernetes plugin](https://plugins.jenkins.io/kubernetes/). + +Inspired by the awesome work of [Carlos Sanchez](https://github.com/carlossg). + +## Get Repository Info + +```console +helm repo add jenkins https://charts.jenkins.io +helm repo update +``` + +_See [`helm repo`](https://helm.sh/docs/helm/helm_repo/) for command documentation._ + +## Install Chart + +```console +helm install [RELEASE_NAME] jenkins/jenkins [flags] +``` + +Since version `5.6.0` the chart is available as an OCI image and can be installed using: + +```console +helm install [RELEASE_NAME] oci://ghcr.io/jenkinsci/helm-charts/jenkins [flags] +``` + +_See [configuration](#configuration) below._ + +_See [helm install](https://helm.sh/docs/helm/helm_install/) for command documentation._ + +## Uninstall Chart + +```console +# Helm 3 +$ 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._ + +## Upgrade Chart + +```console +# Helm 3 +$ helm upgrade [RELEASE_NAME] jenkins/jenkins [flags] +``` + +_See [helm upgrade](https://helm.sh/docs/helm/helm_upgrade/) for command documentation._ + +Visit the chart's [CHANGELOG](https://github.com/jenkinsci/helm-charts/blob/main/charts/jenkins/CHANGELOG.md) to view the chart's release history. +For migration between major version check [migration guide](#migration-guide). + +## Building weekly releases + +The default charts target Long-Term-Support (LTS) releases of Jenkins. +To use other versions the easiest way is to update the image tag to the version you want. +You can also rebuild the chart if you want the `appVersion` field to match. + +## 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](https://github.com/jenkinsci/helm-charts/blob/main/charts/jenkins/values.yaml), or run these configuration commands: + +```console +# Helm 3 +$ helm show values jenkins/jenkins +``` + +For a summary of all configurable options, see [VALUES_SUMMARY.md](https://github.com/jenkinsci/helm-charts/blob/main/charts/jenkins/VALUES_SUMMARY.md). + +### Configure Security Realm and Authorization Strategy + +This chart configured a `securityRealm` and `authorizationStrategy` as shown below: + +```yaml +controller: + JCasC: + securityRealm: |- + local: + allowsSignup: false + enableCaptcha: false + users: + - id: "${chart-admin-username}" + name: "Jenkins Admin" + password: "${chart-admin-password}" + authorizationStrategy: |- + loggedInUsersCanDoAnything: + allowAnonymousRead: false +``` + +With the configuration above there is only a single user. +This is fine for getting started quickly, but it needs to be adjusted for any serious environment. + +So you should adjust this to suite your needs. +That could be using LDAP / OIDC / .. as authorization strategy and use globalMatrix as authorization strategy to configure more fine-grained permissions. + +### Consider using a custom image + +This chart allows the user to specify plugins which should be installed. However, for production use cases one should consider to build a custom Jenkins image which has all required plugins pre-installed. +This way you can be sure which plugins Jenkins is using when starting up and you avoid trouble in case of connectivity issues to the Jenkins update site. + +The [docker repository](https://github.com/jenkinsci/docker) for the Jenkins image contains [documentation](https://github.com/jenkinsci/docker#preinstalling-plugins) how to do it. + +Here is an example how that can be done: + +```Dockerfile +FROM jenkins/jenkins:lts +RUN jenkins-plugin-cli --plugins kubernetes workflow-aggregator git configuration-as-code +``` + +NOTE: If you want a reproducible build then you should specify a non-floating tag for the image `jenkins/jenkins:2.249.3` and specify plugin versions. + +Once you built the image and pushed it to your registry you can specify it in your values file like this: + +```yaml +controller: + image: "registry/my-jenkins" + tag: "v1.2.3" + installPlugins: false +``` + +Notice: `installPlugins` is set to false to disable plugin download. In this case, the image `registry/my-jenkins:v1.2.3` must have the plugins specified as default value for [the `controller.installPlugins` directive](https://github.com/jenkinsci/helm-charts/blob/main/charts/jenkins/VALUES_SUMMARY.md#jenkins-plugins) to ensure that the configuration side-car system works as expected. + +In case you are using a private registry you can use 'imagePullSecretName' to specify the name of the secret to use when pulling the image: + +```yaml +controller: + image: "registry/my-jenkins" + tag: "v1.2.3" + imagePullSecretName: registry-secret + installPlugins: false +``` + +### External URL Configuration + +If you are using the ingress definitions provided by this chart via the `controller.ingress` block the configured hostname will be the ingress hostname starting with `https://` or `http://` depending on the `tls` configuration. +The Protocol can be overwritten by specifying `controller.jenkinsUrlProtocol`. + +If you are not using the provided ingress you can specify `controller.jenkinsUrl` to change the URL definition. + +### Configuration as Code + +Jenkins Configuration as Code (JCasC) is now a standard component in the Jenkins project. +To allow JCasC's configuration from the helm values, the plugin [`configuration-as-code`](https://plugins.jenkins.io/configuration-as-code/) must be installed in the Jenkins Controller's Docker image (which is the case by default as specified by the [default value of the directive `controller.installPlugins`](https://github.com/jenkinsci/helm-charts/blob/main/charts/jenkins/VALUES_SUMMARY.md#jenkins-plugins)). + +JCasc configuration is passed through Helm values under the key `controller.JCasC`. +The section ["Jenkins Configuration as Code (JCasC)" of the page "VALUES_SUMMARY.md"](https://github.com/jenkinsci/helm-charts/blob/main/charts/jenkins/VALUES_SUMMARY.md#jenkins-configuration-as-code-jcasc) lists all the possible directives. + +In particular, you may specify custom JCasC scripts by adding sub-key under the `controller.JCasC.configScripts` for each configuration area where each corresponds to a plugin or section of the UI. + +The sub-keys (prior to `|` character) are only labels used to give the section a meaningful name. +The only restriction is they must conform to RFC 1123 definition of a DNS label, so they may only contain lowercase letters, numbers, and hyphens. + +Each key will become the name of a configuration yaml file on the controller in `/var/jenkins_home/casc_configs` (by default) and will be processed by the Configuration as Code Plugin during Jenkins startup. + +The lines after each `|` become the content of the configuration yaml file. + +The first line after this is a JCasC root element, e.g. jenkins, credentials, etc. + +Best reference is the Documentation link here: `https:///configuration-as-code`. + +The example below sets custom systemMessage: + +```yaml +controller: + JCasC: + configScripts: + welcome-message: | + jenkins: + systemMessage: Welcome to our CI\CD server. +``` + +More complex example that creates ldap settings: + +```yaml +controller: + JCasC: + configScripts: + ldap-settings: | + jenkins: + securityRealm: + ldap: + configurations: + - server: ldap.acme.com + rootDN: dc=acme,dc=uk + managerPasswordSecret: ${LDAP_PASSWORD} + groupMembershipStrategy: + fromUserRecord: + attributeName: "memberOf" +``` + +Keep in mind that default configuration file already contains some values that you won't be able to override under configScripts section. + +For example, you can not configure Jenkins URL and System Admin email address like this because of conflicting configuration error. + +Incorrect: + +```yaml +controller: + JCasC: + configScripts: + jenkins-url: | + unclassified: + location: + url: https://example.com/jenkins + adminAddress: example@mail.com +``` + +Correct: + +```yaml +controller: + jenkinsUrl: https://example.com/jenkins + jenkinsAdminEmail: example@mail.com +``` + +Further JCasC examples can be found [here](https://github.com/jenkinsci/configuration-as-code-plugin/tree/master/demos). + +#### Breaking out large Config as Code scripts + +Jenkins Config as Code scripts can become quite large, and maintaining all of your scripts within one yaml file can be difficult. The Config as Code plugin itself suggests updating the `CASC_JENKINS_CONFIG` environment variable to be a comma separated list of paths for the plugin to traverse, picking up the yaml files as needed. +However, under the Jenkins helm chart, this `CASC_JENKINS_CONFIG` value is maintained through the templates. A better solution is to split your `controller.JCasC.configScripts` into separate values files, and provide each file during the helm install. + +For example, you can have a values file (e.g values_main.yaml) that defines the values described in the `VALUES_SUMMARY.md` for your Jenkins configuration: + +```yaml +jenkins: + controller: + jenkinsUrlProtocol: https + installPlugins: false + ... +``` + +In a second file (e.g values_jenkins_casc.yaml), you can define a section of your config scripts: + +```yaml +jenkins: + controller: + JCasC: + configScripts: + jenkinsCasc: | + jenkins: + disableRememberMe: false + mode: NORMAL + ... +``` + +And keep extending your config scripts by creating more files (so not all config scripts are located in one yaml file for better maintenance): + +values_jenkins_unclassified.yaml + +```yaml +jenkins: + controller: + JCasC: + configScripts: + unclassifiedCasc: | + unclassified: + ... +``` + +When installing, you provide all relevant yaml files (e.g `helm install -f values_main.yaml -f values_jenkins_casc.yaml -f values_jenkins_unclassified.yaml ...`). Instead of updating the `CASC_JENKINS_CONFIG` environment variable to include multiple paths, multiple CasC yaml files will be created in the same path `var/jenkins_home/casc_configs`. + +#### Config as Code With or Without Auto-Reload + +Config as Code changes (to `controller.JCasC.configScripts`) can either force a new pod to be created and only be applied at next startup, or can be auto-reloaded on-the-fly. +If you set `controller.sidecars.configAutoReload.enabled` to `true`, a second, auxiliary container will be installed into the Jenkins controller pod, known as a "sidecar". +This watches for changes to configScripts, copies the content onto the Jenkins file-system and issues a POST to `http:///reload-configuration-as-code` with a pre-shared key. +You can monitor this sidecar's logs using command `kubectl logs -c config-reload -f`. +If you want to enable auto-reload then you also need to configure rbac as the container which triggers the reload needs to watch the config maps: + +```yaml +controller: + sidecars: + configAutoReload: + enabled: true +rbac: + create: true +``` + +### Allow Limited HTML Markup in User-Submitted Text + +Some third-party systems (e.g. GitHub) use HTML-formatted data in their payload sent to a Jenkins webhook (e.g. URL of a pull-request being built). +To display such data as processed HTML instead of raw text set `controller.enableRawHtmlMarkupFormatter` to true. +This option requires installation of the [OWASP Markup Formatter Plugin (antisamy-markup-formatter)](https://plugins.jenkins.io/antisamy-markup-formatter/). +This plugin is **not** installed by default but may be added to `controller.additionalPlugins`. + +### Change max connections to Kubernetes API +When using agents with containers other than JNLP, The kubernetes plugin will communicate with those containers using the Kubernetes API. this changes the maximum concurrent connections +```yaml +agent: + maxRequestsPerHostStr: "32" +``` +This will change the configuration of the kubernetes "cloud" (as called by jenkins) that is created automatically as part of this helm chart. + +### Change container cleanup timeout API +For tasks that use very large images, this timeout can be increased to avoid early termination of the task while the Kubernetes pod is still deploying. +```yaml +agent: + retentionTimeout: "32" +``` +This will change the configuration of the kubernetes "cloud" (as called by jenkins) that is created automatically as part of this helm chart. + +### Change seconds to wait for pod to be running +This will change how long Jenkins will wait (seconds) for pod to be in running state. +```yaml +agent: + waitForPodSec: "32" +``` +This will change the configuration of the kubernetes "cloud" (as called by jenkins) that is created automatically as part of this helm chart. + +### Mounting Volumes into Agent Pods + +Your Jenkins Agents will run as pods, and it's possible to inject volumes where needed: + +```yaml +agent: + volumes: + - type: Secret + secretName: jenkins-mysecrets + mountPath: /var/run/secrets/jenkins-mysecrets +``` + +The supported volume types are: `ConfigMap`, `EmptyDir`, `HostPath`, `Nfs`, `PVC`, `Secret`. +Each type supports a different set of configurable attributes, defined by [the corresponding Java class](https://github.com/jenkinsci/kubernetes-plugin/tree/master/src/main/java/org/csanchez/jenkins/plugins/kubernetes/volumes). + +### NetworkPolicy + +To make use of the NetworkPolicy resources created by default, install [a networking plugin that implements the Kubernetes NetworkPolicy spec](https://kubernetes.io/docs/tasks/administer-cluster/declare-network-policy#before-you-begin). + +[Install](#install-chart) helm chart with network policy enabled by setting `networkPolicy.enabled` to `true`. + +You can use `controller.networkPolicy.internalAgents` and `controller.networkPolicy.externalAgents` stanzas for fine-grained controls over where internal/external agents can connect from. +Internal ones are allowed based on pod labels and (optionally) namespaces, and external ones are allowed based on IP ranges. + +### Script approval list + +`controller.scriptApproval` allows to pass function signatures that will be allowed in pipelines. +Example: + +```yaml +controller: + scriptApproval: + - "method java.util.Base64$Decoder decode java.lang.String" + - "new java.lang.String byte[]" + - "staticMethod java.util.Base64 getDecoder" +``` + +### Custom Labels + +`controller.serviceLabels` can be used to add custom labels in `jenkins-controller-svc.yaml`. +For example: + +```yaml +ServiceLabels: + expose: true +``` + +### Persistence + +The Jenkins image stores persistence under `/var/jenkins_home` path of the container. +A dynamically managed Persistent Volume Claim is used to keep the data across deployments, by default. +This is known to work in GCE, AWS, and minikube. Alternatively, a previously configured Persistent Volume Claim can be used. + +It is possible to mount several volumes using `persistence.volumes` and `persistence.mounts` parameters. +See additional `persistence` values using [configuration commands](#configuration). + +#### Existing PersistentVolumeClaim + +1. Create the PersistentVolume +2. Create the PersistentVolumeClaim +3. [Install](#install-chart) the chart, setting `persistence.existingClaim` to `PVC_NAME` + +#### Long Volume Attach/Mount Times + +Certain volume type and filesystem format combinations may experience long +attach/mount times, [10 or more minutes][K8S_VOLUME_TIMEOUT], when using +`fsGroup`. This issue may result in the following entries in the pod's event +history: + +```console +Warning FailedMount 38m kubelet, aks-default-41587790-2 Unable to attach or mount volumes: unmounted volumes=[jenkins-home], unattached volumes=[plugins plugin-dir jenkins-token-rmq2g sc-config-volume tmp jenkins-home jenkins-config secrets-dir]: timed out waiting for the condition +``` + +In these cases, experiment with replacing `fsGroup` with +`supplementalGroups` in the pod's `securityContext`. This can be achieved by +setting the `controller.podSecurityContextOverride` Helm chart value to +something like: + +```yaml +controller: + podSecurityContextOverride: + runAsNonRoot: true + runAsUser: 1000 + supplementalGroups: [1000] +``` + +This issue has been reported on [azureDisk with ext4][K8S_VOLUME_TIMEOUT] and +on [Alibaba cloud][K8S_VOLUME_TIMEOUT_ALIBABA]. + +[K8S_VOLUME_TIMEOUT]: https://github.com/kubernetes/kubernetes/issues/67014 +[K8S_VOLUME_TIMEOUT_ALIBABA]: https://github.com/kubernetes/kubernetes/issues/67014#issuecomment-698770511 + +#### Storage Class + +It is possible to define which storage class to use, by setting `persistence.storageClass` to `[customStorageClass]`. +If set to a dash (`-`), dynamic provisioning is disabled. +If the storage class is set to null or left undefined (`""`), the default provisioner is used (gp2 on AWS, standard on GKE, AWS & OpenStack). + +### Additional Secrets + +Additional secrets and Additional Existing Secrets, +can be mounted into the Jenkins controller through the chart or created using `controller.additionalSecrets` or `controller.additionalExistingSecrets`. +A common use case might be identity provider credentials if using an external LDAP or OIDC-based identity provider. +The secret may then be referenced in JCasC configuration (see [JCasC configuration](#configuration-as-code)). + +`values.yaml` controller section, referencing mounted secrets: +```yaml +controller: + # the 'name' and 'keyName' are concatenated with a '-' in between, so for example: + # an existing secret "secret-credentials" and a key inside it named "github-password" should be used in Jcasc as ${secret-credentials-github-password} + # 'name' and 'keyName' must be lowercase RFC 1123 label must consist of lower case alphanumeric characters or '-', + # and must start and end with an alphanumeric character (e.g. 'my-name', or '123-abc') + # existingSecret existing secret "secret-credentials" and a key inside it named "github-username" should be used in Jcasc as ${github-username} + # When using existingSecret no need to specify the keyName under additionalExistingSecrets. + existingSecret: secret-credentials + + additionalExistingSecrets: + - name: secret-credentials + keyName: github-username + - name: secret-credentials + keyName: github-password + - name: secret-credentials + keyName: token + + additionalSecrets: + - name: client_id + value: abc123 + - name: client_secret + value: xyz999 + JCasC: + securityRealm: | + oic: + clientId: ${client_id} + clientSecret: ${client_secret} + ... + configScripts: + jenkins-casc-configs: | + credentials: + system: + domainCredentials: + - credentials: + - string: + description: "github access token" + id: "github_app_token" + scope: GLOBAL + secret: ${secret-credentials-token} + - usernamePassword: + description: "github access username password" + id: "github_username_pass" + password: ${secret-credentials-github-password} + scope: GLOBAL + username: ${secret-credentials-github-username} +``` + +For more information, see [JCasC documentation](https://github.com/jenkinsci/configuration-as-code-plugin/blob/master/docs/features/secrets.adoc#kubernetes-secrets). + +### Secret Claims from HashiCorp Vault + +It's possible for this chart to generate `SecretClaim` resources in order to automatically create and maintain Kubernetes `Secrets` from HashiCorp [Vault](https://www.vaultproject.io/) via [`kube-vault-controller`](https://github.com/roboll/kube-vault-controller) + +These `Secrets` can then be referenced in the same manner as Additional Secrets above. + +This can be achieved by defining required Secret Claims within `controller.secretClaims`, as follows: +```yaml +controller: + secretClaims: + - name: jenkins-secret + path: secret/path + - name: jenkins-short-ttl + path: secret/short-ttl-path + renew: 60 +``` + +### RBAC + +RBAC is enabled by default. If you want to disable it you will need to set `rbac.create` to `false`. + +### Adding Custom Pod Templates + +It is possible to add custom pod templates for the default configured kubernetes cloud. +Add a key under `agent.podTemplates` for each pod template. Each key (prior to `|` character) is just a label, and can be any value. +Keys are only used to give the pod template a meaningful name. The only restriction is they may only contain RFC 1123 \ DNS label characters: lowercase letters, numbers, and hyphens. Each pod template can contain multiple containers. +There's no need to add the _jnlp_ container since the kubernetes plugin will automatically inject it into the pod. +For this pod templates configuration to be loaded the following values must be set: + +```yaml +controller.JCasC.defaultConfig: true +``` + +The example below creates a python pod template in the kubernetes cloud: + +```yaml +agent: + podTemplates: + python: | + - name: python + label: jenkins-python + serviceAccount: jenkins + containers: + - name: python + image: python:3 + command: "/bin/sh -c" + args: "cat" + ttyEnabled: true + privileged: true + resourceRequestCpu: "400m" + resourceRequestMemory: "512Mi" + resourceLimitCpu: "1" + resourceLimitMemory: "1024Mi" +``` + +Best reference is `https:///configuration-as-code/reference#Cloud-kubernetes`. + +### Adding Pod Templates Using additionalAgents + +`additionalAgents` may be used to configure additional kubernetes pod templates. +Each additional agent corresponds to `agent` in terms of the configurable values and inherits all values from `agent` so you only need to specify values which differ. +For example: + +```yaml +agent: + podName: default + customJenkinsLabels: default + # set resources for additional agents to inherit + resources: + limits: + cpu: "1" + memory: "2048Mi" + +additionalAgents: + maven: + podName: maven + customJenkinsLabels: maven + # An example of overriding the jnlp container + # sideContainerName: jnlp + image: jenkins/jnlp-agent-maven + tag: latest + python: + podName: python + customJenkinsLabels: python + sideContainerName: python + image: python + tag: "3" + command: "/bin/sh -c" + args: "cat" + TTYEnabled: true +``` + +### Ingress Configuration + +This chart provides ingress resources configurable via the `controller.ingress` block. + +The simplest configuration looks like the following: + +```yaml +controller: + ingress: + enabled: true + paths: [] + apiVersion: "extensions/v1beta1" + hostName: jenkins.example.com +``` + +This snippet configures an ingress rule for exposing jenkins at `jenkins.example.com` + +You can define labels and annotations via `controller.ingress.labels` and `controller.ingress.annotations` respectively. +Additionally, you can configure the ingress tls via `controller.ingress.tls`. +By default, this ingress rule exposes all paths. +If needed this can be overwritten by specifying the wanted paths in `controller.ingress.paths` + +If you want to configure a secondary ingress e.g. you don't want the jenkins instance exposed but still want to receive webhooks you can configure `controller.secondaryingress`. +The secondaryingress doesn't expose anything by default and has to be configured via `controller.secondaryingress.paths`: + +```yaml +controller: + ingress: + enabled: true + apiVersion: "extensions/v1beta1" + hostName: "jenkins.internal.example.com" + annotations: + kubernetes.io/ingress.class: "internal" + secondaryingress: + enabled: true + apiVersion: "extensions/v1beta1" + hostName: "jenkins-scm.example.com" + annotations: + kubernetes.io/ingress.class: "public" + paths: + - /github-webhook +``` + +## Prometheus Metrics + +If you want to expose Prometheus metrics you need to install the [Jenkins Prometheus Metrics Plugin](https://github.com/jenkinsci/prometheus-plugin). +It will expose an endpoint (default `/prometheus`) with metrics where a Prometheus Server can scrape. + +If you have implemented [Prometheus Operator](https://github.com/prometheus-operator/prometheus-operator), you can set `controller.prometheus.enabled` to `true` to configure a `ServiceMonitor` and `PrometheusRule`. +If you want to further adjust alerting rules you can do so by configuring `controller.prometheus.alertingrules` + +If you have implemented Prometheus without using the operator, you can leave `controller.prometheus.enabled` set to `false`. + +### Running Behind a Forward Proxy + +The controller pod uses an Init Container to install plugins etc. If you are behind a corporate proxy it may be useful to set `controller.initContainerEnv` to add environment variables such as `http_proxy`, so that these can be downloaded. + +Additionally, you may want to add env vars for the init container, the Jenkins container, and the JVM (`controller.javaOpts`): + +```yaml +controller: + initContainerEnv: + - name: http_proxy + value: "http://192.168.64.1:3128" + - name: https_proxy + value: "http://192.168.64.1:3128" + - name: no_proxy + value: "" + - name: JAVA_OPTS + value: "-Dhttps.proxyHost=proxy_host_name_without_protocol -Dhttps.proxyPort=3128" + containerEnv: + - name: http_proxy + value: "http://192.168.64.1:3128" + - name: https_proxy + value: "http://192.168.64.1:3128" + javaOpts: >- + -Dhttp.proxyHost=192.168.64.1 + -Dhttp.proxyPort=3128 + -Dhttps.proxyHost=192.168.64.1 + -Dhttps.proxyPort=3128 +``` + +### HTTPS Keystore Configuration + +[This configuration](https://wiki.jenkins.io/pages/viewpage.action?pageId=135468777) enables jenkins to use keystore in order to serve HTTPS. +Here is the [value file section](https://wiki.jenkins.io/pages/viewpage.action?pageId=135468777#RunningJenkinswithnativeSSL/HTTPS-ConfigureJenkinstouseHTTPSandtheJKSkeystore) related to keystore configuration. +Keystore itself should be placed in front of `jenkinsKeyStoreBase64Encoded` key and in base64 encoded format. To achieve that after having `keystore.jks` file simply do this: `cat keystore.jks | base64` and paste the output in front of `jenkinsKeyStoreBase64Encoded`. +After enabling `httpsKeyStore.enable` make sure that `httpPort` and `targetPort` are not the same, as `targetPort` will serve HTTPS. +Do not set `controller.httpsKeyStore.httpPort` to `-1` because it will cause readiness and liveliness prob to fail. +If you already have a kubernetes secret that has keystore and its password you can specify its' name in front of `jenkinsHttpsJksSecretName`, You need to remember that your secret should have proper data key names `jenkins-jks-file` (or override the key name using `jenkinsHttpsJksSecretKey`) +and `https-jks-password` (or override the key name using `jenkinsHttpsJksPasswordSecretKey`; additionally you can make it get the password from a different secret using `jenkinsHttpsJksPasswordSecretName`). Example: + +```yaml +controller: + httpsKeyStore: + enable: true + jenkinsHttpsJksSecretName: '' + httpPort: 8081 + path: "/var/jenkins_keystore" + fileName: "keystore.jks" + password: "changeit" + jenkinsKeyStoreBase64Encoded: '' +``` +### AWS Security Group Policies + +To create SecurityGroupPolicies set `awsSecurityGroupPolicies.enabled` to true and add your policies. Each policy requires a `name`, array of `securityGroupIds` and a `podSelector`. Example: + +```yaml +awsSecurityGroupPolicies: + enabled: true + policies: + - name: "jenkins-controller" + securityGroupIds: + - sg-123456789 + podSelector: + matchExpressions: + - key: app.kubernetes.io/component + operator: In + values: + - jenkins-controller +``` + +### Agent Direct Connection + +Set `directConnection` to `true` to allow agents to connect directly to a given TCP port without having to negotiate a HTTP(S) connection. This can allow you to have agent connections without an external HTTP(S) port. Example: + +```yaml +agent: + jenkinsTunnel: "jenkinsci-agent:50000" + directConnection: true +``` + +## Migration Guide + +### From stable repository + +Upgrade an existing release from `stable/jenkins` to `jenkins/jenkins` seamlessly by ensuring you have the latest [repository info](#get-repository-info) and running the [upgrade commands](#upgrade-chart) specifying the `jenkins/jenkins` chart. + +### Major Version Upgrades + +Chart release versions follow [SemVer](../../CONTRIBUTING.md#versioning), where a MAJOR version change (example `1.0.0` -> `2.0.0`) indicates an incompatible breaking change needing manual actions. + +See [UPGRADING.md](./UPGRADING.md) for a list of breaking changes diff --git a/charts/jenkins/jenkins/5.6.2/UPGRADING.md b/charts/jenkins/jenkins/5.6.2/UPGRADING.md new file mode 100644 index 000000000..0ff90112d --- /dev/null +++ b/charts/jenkins/jenkins/5.6.2/UPGRADING.md @@ -0,0 +1,148 @@ +# Upgrade Notes + +## To 5.0.0 +- `controller.image`, `controller.tag`, and `controller.tagLabel` have been removed. If you want to overwrite the image you now need to configure any or all of: + - `controller.image.registry` + - `controller.image.repository` + - `controller.image.tag` + - `controller.image.tagLabel` +- `controller.imagePullPolicy` has been removed. If you want to overwrite the pull policy you now need to configure `controller.image.pullPolicy`. +- `controller.sidecars.configAutoReload.image` has been removed. If you want to overwrite the configAutoReload image you now need to configure any or all of: + - `controller.sidecars.configAutoReload.image.registry` + - `controller.sidecars.configAutoReload.image.repository` + - `controller.sidecars.configAutoReload.image.tag` +- `controller.sidecars.other` has been renamed to `controller.sidecars.additionalSidecarContainers`. +- `agent.image` and `agent.tag` have been removed. If you want to overwrite the agent image you now need to configure any or all of: + - `agent.image.repository` + - `agent.image.tag` + - The registry can still be overwritten by `agent.jnlpregistry` +- `agent.additionalContainers[*].image` has been renamed to `agent.additionalContainers[*].image.repository` +- `agent.additionalContainers[*].tag` has been renamed to `agent.additionalContainers[*].image.tag` +- `additionalAgents.*.image` has been renamed to `additionalAgents.*.image.repository` +- `additionalAgents.*.tag` has been renamed to `additionalAgents.*.image.tag` +- `additionalClouds.*.additionalAgents.*.image` has been renamed to `additionalClouds.*.additionalAgents.*.image.repository` +- `additionalClouds.*.additionalAgents.*.tag` has been renamed to `additionalClouds.*.additionalAgents.*.image.tag` +- `helmtest.bats.image` has been split up to: + - `helmtest.bats.image.registry` + - `helmtest.bats.image.repository` + - `helmtest.bats.image.tag` +- `controller.adminUsername` and `controller.adminPassword` have been renamed to `controller.admin.username` and `controller.admin.password` respectively +- `controller.adminSecret` has been renamed to `controller.admin.createSecret` +- `backup.*` was unmaintained and has thus been removed. See the following page for alternatives: [Kubernetes Backup and Migrations](https://nubenetes.com/kubernetes-backup-migrations/). + +## To 4.0.0 +Removes automatic `remotingSecurity` setting when using a container tag older than `2.326` (introduced in [`3.11.7`](./CHANGELOG.md#3117)). If you're using a version older than `2.326`, you should explicitly set `.controller.legacyRemotingSecurityEnabled` to `true`. + +## To 3.0.0 + +* Check `securityRealm` and `authorizationStrategy` and adjust it. + Otherwise, your configured users and permissions will be overridden. +* You need to use helm version 3 as the `Chart.yaml` uses `apiVersion: v2`. +* All XML configuration options have been removed. + In case those are still in use you need to migrate to configuration as code. + Upgrade guide to 2.0.0 contains pointers how to do that. +* Jenkins is now using a `StatefulSet` instead of a `Deployment` +* terminology has been adjusted that's also reflected in values.yaml + The following values from `values.yaml` have been renamed: + + * `master` => `controller` + * `master.useSecurity` => `controller.adminSecret` + * `master.slaveListenerPort` => `controller.agentListenerPort` + * `master.slaveHostPort` => `controller.agentListenerHostPort` + * `master.slaveKubernetesNamespace` => `agent.namespace` + * `master.slaveDefaultsProviderTemplate` => `agent.defaultsProviderTemplate` + * `master.slaveJenkinsUrl` => `agent.jenkinsUrl` + * `master.slaveJenkinsTunnel` => `agent.jenkinsTunnel` + * `master.slaveConnectTimeout` => `agent.kubernetesConnectTimeout` + * `master.slaveReadTimeout` => `agent.kubernetesReadTimeout` + * `master.slaveListenerServiceAnnotations` => `controller.agentListenerServiceAnnotations` + * `master.slaveListenerServiceType` => `controller.agentListenerServiceType` + * `master.slaveListenerLoadBalancerIP` => `controller.agentListenerLoadBalancerIP` + * `agent.slaveConnectTimeout` => `agent.connectTimeout` +* Removed values: + + * `master.imageTag`: use `controller.image` and `controller.tag` instead + * `slave.imageTag`: use `agent.image` and `agent.tag` instead + +## To 2.0.0 + +Configuration as Code is now default + container does not run as root anymore. + +### Configuration as Code new default + +Configuration is done via [Jenkins Configuration as Code Plugin](https://github.com/jenkinsci/configuration-as-code-plugin) by default. +That means that changes in values which result in a configuration change are always applied. +In contrast, the XML configuration was only applied during the first start and never altered. + +:exclamation::exclamation::exclamation: +Attention: +This also means if you manually altered configuration then this will most likely be reset to what was configured by default. +It also applies to `securityRealm` and `authorizationStrategy` as they are also configured using configuration as code. +:exclamation::exclamation::exclamation: + +### Image does not run as root anymore + +It's not recommended to run containers in Kubernetes as `root`. + +❗Attention: If you had not configured a different user before then you need to ensure that your image supports the user and group ID configured and also manually change permissions of all files so that Jenkins is still able to use them. + +### Summary of updated values + +As version 2.0.0 only updates default values and nothing else it's still possible to migrate to this version and opt out of some or all new defaults. +All you have to do is ensure the old values are set in your installation. + +Here we show which values have changed and the previous default values: + +```yaml +controller: + runAsUser: 1000 # was unset before + fsGroup: 1000 # was unset before + JCasC: + enabled: true # was false + defaultConfig: true # was false + sidecars: + configAutoReload: + enabled: true # was false +``` + +### Migration steps + +Migration instructions heavily depend on your current setup. +So think of the list below more as a general guideline of what should be done. + +- Ensure that the Jenkins image you are using contains a user with ID 1000 and a group with the same ID. + That's the case for `jenkins/jenkins:lts` image, which the chart uses by default +- Make a backup of your existing installation especially the persistent volume +- Ensure that you have the configuration as code plugin installed +- Export your current settings via the plugin: + `Manage Jenkins` -> `Configuration as Code` -> `Download Configuration` +- prepare your values file for the update e.g. add additional configuration as code setting that you need. + The export taken from above might be a good starting point for this. + In addition, the [demos](https://github.com/jenkinsci/configuration-as-code-plugin/tree/master/demos) from the plugin itself are quite useful. +- Test drive those setting on a separate installation +- Put Jenkins to Quiet Down mode so that it does not accept new jobs + `/quietDown` +- Change permissions of all files and folders to the new user and group ID: + + ```console + kubectl exec -it -c jenkins /bin/bash + chown -R 1000:1000 /var/jenkins_home + ``` + +- Update Jenkins + +## To 1.0.0 + +Breaking changes: + +- Values have been renamed to follow [helm recommended naming conventions](https://helm.sh/docs/chart_best_practices/#naming-conventions) so that all variables start with a lowercase letter and words are separated with camelcase +- All resources are now using [helm recommended standard labels](https://helm.sh/docs/chart_best_practices/#standard-labels) + +As a result of the label changes also the selectors of the deployment have been updated. +Those are immutable so trying an updated will cause an error like: + +```console +Error: Deployment.apps "jenkins" is invalid: spec.selector: Invalid value: v1.LabelSelector{MatchLabels:map[string]string{"app.kubernetes.io/component":"jenkins-controller", "app.kubernetes.io/instance":"jenkins"}, MatchExpressions:[]v1.LabelSelectorRequirement(nil)}: field is immutable +``` + +In order to upgrade, [uninstall](./README.md#uninstall-chart) the Jenkins Deployment before upgrading: diff --git a/charts/jenkins/jenkins/5.6.2/VALUES.md b/charts/jenkins/jenkins/5.6.2/VALUES.md new file mode 100644 index 000000000..1d7b40661 --- /dev/null +++ b/charts/jenkins/jenkins/5.6.2/VALUES.md @@ -0,0 +1,317 @@ +# Jenkins + +## Configuration + +The following tables list the configurable parameters of the Jenkins chart and their default values. + +## Values + +| Key | Type | Description | Default | +|:----|:-----|:---------|:------------| +| [additionalAgents](./values.yaml#L1195) | object | Configure additional | `{}` | +| [additionalClouds](./values.yaml#L1220) | object | | `{}` | +| [agent.TTYEnabled](./values.yaml#L1101) | bool | Allocate pseudo tty to the side container | `false` | +| [agent.additionalContainers](./values.yaml#L1148) | list | Add additional containers to the agents | `[]` | +| [agent.alwaysPullImage](./values.yaml#L994) | bool | Always pull agent container image before build | `false` | +| [agent.annotations](./values.yaml#L1144) | object | Annotations to apply to the pod | `{}` | +| [agent.args](./values.yaml#L1095) | string | Arguments passed to command to execute | `"${computer.jnlpmac} ${computer.name}"` | +| [agent.command](./values.yaml#L1093) | string | Command to execute when side container starts | `nil` | +| [agent.componentName](./values.yaml#L962) | string | | `"jenkins-agent"` | +| [agent.connectTimeout](./values.yaml#L1142) | int | Timeout in seconds for an agent to be online | `100` | +| [agent.containerCap](./values.yaml#L1103) | int | Max number of agents to launch | `10` | +| [agent.customJenkinsLabels](./values.yaml#L959) | list | Append Jenkins labels to the agent | `[]` | +| [agent.defaultsProviderTemplate](./values.yaml#L913) | string | The name of the pod template to use for providing default values | `""` | +| [agent.directConnection](./values.yaml#L965) | bool | | `false` | +| [agent.disableDefaultAgent](./values.yaml#L1166) | bool | Disable the default Jenkins Agent configuration | `false` | +| [agent.enabled](./values.yaml#L911) | bool | Enable Kubernetes plugin jnlp-agent podTemplate | `true` | +| [agent.envVars](./values.yaml#L1076) | list | Environment variables for the agent Pod | `[]` | +| [agent.garbageCollection.enabled](./values.yaml#L1110) | bool | When enabled, Jenkins will periodically check for orphan pods that have not been touched for the given timeout period and delete them. | `false` | +| [agent.garbageCollection.namespaces](./values.yaml#L1112) | string | Namespaces to look at for garbage collection, in addition to the default namespace defined for the cloud. One namespace per line. | `""` | +| [agent.garbageCollection.timeout](./values.yaml#L1117) | int | Timeout value for orphaned pods | `300` | +| [agent.hostNetworking](./values.yaml#L973) | bool | Enables the agent to use the host network | `false` | +| [agent.idleMinutes](./values.yaml#L1120) | int | Allows the Pod to remain active for reuse until the configured number of minutes has passed since the last step was executed on it | `0` | +| [agent.image.repository](./values.yaml#L952) | string | Repository to pull the agent jnlp image from | `"jenkins/inbound-agent"` | +| [agent.image.tag](./values.yaml#L954) | string | Tag of the image to pull | `"3261.v9c670a_4748a_9-1"` | +| [agent.imagePullSecretName](./values.yaml#L961) | string | Name of the secret to be used to pull the image | `nil` | +| [agent.inheritYamlMergeStrategy](./values.yaml#L1140) | bool | Controls whether the defined yaml merge strategy will be inherited if another defined pod template is configured to inherit from the current one | `false` | +| [agent.jenkinsTunnel](./values.yaml#L929) | string | Overrides the Kubernetes Jenkins tunnel | `nil` | +| [agent.jenkinsUrl](./values.yaml#L925) | string | Overrides the Kubernetes Jenkins URL | `nil` | +| [agent.jnlpregistry](./values.yaml#L949) | string | Custom registry used to pull the agent jnlp image from | `nil` | +| [agent.kubernetesConnectTimeout](./values.yaml#L935) | int | The connection timeout in seconds for connections to Kubernetes API. The minimum value is 5 | `5` | +| [agent.kubernetesReadTimeout](./values.yaml#L937) | int | The read timeout in seconds for connections to Kubernetes API. The minimum value is 15 | `15` | +| [agent.livenessProbe](./values.yaml#L984) | object | | `{}` | +| [agent.maxRequestsPerHostStr](./values.yaml#L939) | string | The maximum concurrent connections to Kubernetes API | `"32"` | +| [agent.namespace](./values.yaml#L945) | string | Namespace in which the Kubernetes agents should be launched | `nil` | +| [agent.nodeSelector](./values.yaml#L1087) | object | Node labels for pod assignment | `{}` | +| [agent.nodeUsageMode](./values.yaml#L957) | string | | `"NORMAL"` | +| [agent.podLabels](./values.yaml#L947) | object | Custom Pod labels (an object with `label-key: label-value` pairs) | `{}` | +| [agent.podName](./values.yaml#L1105) | string | Agent Pod base name | `"default"` | +| [agent.podRetention](./values.yaml#L1003) | string | | `"Never"` | +| [agent.podTemplates](./values.yaml#L1176) | object | Configures extra pod templates for the default kubernetes cloud | `{}` | +| [agent.privileged](./values.yaml#L967) | bool | Agent privileged container | `false` | +| [agent.resources](./values.yaml#L975) | object | Resources allocation (Requests and Limits) | `{"limits":{"cpu":"512m","memory":"512Mi"},"requests":{"cpu":"512m","memory":"512Mi"}}` | +| [agent.restrictedPssSecurityContext](./values.yaml#L1000) | bool | Set a restricted securityContext on jnlp containers | `false` | +| [agent.retentionTimeout](./values.yaml#L941) | int | Time in minutes after which the Kubernetes cloud plugin will clean up an idle worker that has not already terminated | `5` | +| [agent.runAsGroup](./values.yaml#L971) | string | Configure container group | `nil` | +| [agent.runAsUser](./values.yaml#L969) | string | Configure container user | `nil` | +| [agent.secretEnvVars](./values.yaml#L1080) | list | Mount a secret as environment variable | `[]` | +| [agent.serviceAccount](./values.yaml#L921) | string | Override the default service account | `serviceAccountAgent.name` if `agent.useDefaultServiceAccount` is `true` | +| [agent.showRawYaml](./values.yaml#L1007) | bool | | `true` | +| [agent.sideContainerName](./values.yaml#L1097) | string | Side container name | `"jnlp"` | +| [agent.skipTlsVerify](./values.yaml#L931) | bool | Disables the verification of the controller certificate on remote connection. This flag correspond to the "Disable https certificate check" flag in kubernetes plugin UI | `false` | +| [agent.usageRestricted](./values.yaml#L933) | bool | Enable the possibility to restrict the usage of this agent to specific folder. This flag correspond to the "Restrict pipeline support to authorized folders" flag in kubernetes plugin UI | `false` | +| [agent.useDefaultServiceAccount](./values.yaml#L917) | bool | Use `serviceAccountAgent.name` as the default value for defaults template `serviceAccount` | `true` | +| [agent.volumes](./values.yaml#L1014) | list | Additional volumes | `[]` | +| [agent.waitForPodSec](./values.yaml#L943) | int | Seconds to wait for pod to be running | `600` | +| [agent.websocket](./values.yaml#L964) | bool | Enables agent communication via websockets | `false` | +| [agent.workingDir](./values.yaml#L956) | string | Configure working directory for default agent | `"/home/jenkins/agent"` | +| [agent.workspaceVolume](./values.yaml#L1049) | object | Workspace volume (defaults to EmptyDir) | `{}` | +| [agent.yamlMergeStrategy](./values.yaml#L1138) | string | Defines how the raw yaml field gets merged with yaml definitions from inherited pod templates. Possible values: "merge" or "override" | `"override"` | +| [agent.yamlTemplate](./values.yaml#L1127) | string | The raw yaml of a Pod API Object to merge into the agent spec | `""` | +| [awsSecurityGroupPolicies.enabled](./values.yaml#L1346) | bool | | `false` | +| [awsSecurityGroupPolicies.policies[0].name](./values.yaml#L1348) | string | | `""` | +| [awsSecurityGroupPolicies.policies[0].podSelector](./values.yaml#L1350) | object | | `{}` | +| [awsSecurityGroupPolicies.policies[0].securityGroupIds](./values.yaml#L1349) | list | | `[]` | +| [checkDeprecation](./values.yaml#L1343) | bool | Checks if any deprecated values are used | `true` | +| [clusterZone](./values.yaml#L21) | string | Override the cluster name for FQDN resolving | `"cluster.local"` | +| [controller.JCasC.authorizationStrategy](./values.yaml#L539) | string | Jenkins Config as Code Authorization Strategy-section | `"loggedInUsersCanDoAnything:\n allowAnonymousRead: false"` | +| [controller.JCasC.configMapAnnotations](./values.yaml#L544) | object | Annotations for the JCasC ConfigMap | `{}` | +| [controller.JCasC.configScripts](./values.yaml#L513) | object | List of Jenkins Config as Code scripts | `{}` | +| [controller.JCasC.configUrls](./values.yaml#L510) | list | Remote URLs for configuration files. | `[]` | +| [controller.JCasC.defaultConfig](./values.yaml#L504) | bool | Enables default Jenkins configuration via configuration as code plugin | `true` | +| [controller.JCasC.overwriteConfiguration](./values.yaml#L508) | bool | Whether Jenkins Config as Code should overwrite any existing configuration | `false` | +| [controller.JCasC.security](./values.yaml#L520) | object | Jenkins Config as Code security-section | `{"apiToken":{"creationOfLegacyTokenEnabled":false,"tokenGenerationOnCreationEnabled":false,"usageStatisticsEnabled":true}}` | +| [controller.JCasC.securityRealm](./values.yaml#L528) | string | Jenkins Config as Code Security Realm-section | `"local:\n allowsSignup: false\n enableCaptcha: false\n users:\n - id: \"${chart-admin-username}\"\n name: \"Jenkins Admin\"\n password: \"${chart-admin-password}\""` | +| [controller.additionalExistingSecrets](./values.yaml#L465) | list | List of additional existing secrets to mount | `[]` | +| [controller.additionalPlugins](./values.yaml#L415) | list | List of plugins to install in addition to those listed in controller.installPlugins | `[]` | +| [controller.additionalSecrets](./values.yaml#L474) | list | List of additional secrets to create and mount | `[]` | +| [controller.admin.createSecret](./values.yaml#L91) | bool | Create secret for admin user | `true` | +| [controller.admin.existingSecret](./values.yaml#L94) | string | The name of an existing secret containing the admin credentials | `""` | +| [controller.admin.password](./values.yaml#L81) | string | Admin password created as a secret if `controller.admin.createSecret` is true | `` | +| [controller.admin.passwordKey](./values.yaml#L86) | string | The key in the existing admin secret containing the password | `"jenkins-admin-password"` | +| [controller.admin.userKey](./values.yaml#L84) | string | The key in the existing admin secret containing the username | `"jenkins-admin-user"` | +| [controller.admin.username](./values.yaml#L78) | string | Admin username created as a secret if `controller.admin.createSecret` is true | `"admin"` | +| [controller.affinity](./values.yaml#L666) | object | Affinity settings | `{}` | +| [controller.agentListenerEnabled](./values.yaml#L324) | bool | Create Agent listener service | `true` | +| [controller.agentListenerExternalTrafficPolicy](./values.yaml#L334) | string | Traffic Policy of for the agentListener service | `nil` | +| [controller.agentListenerHostPort](./values.yaml#L328) | string | Host port to listen for agents | `nil` | +| [controller.agentListenerLoadBalancerIP](./values.yaml#L364) | string | Static IP for the agentListener LoadBalancer | `nil` | +| [controller.agentListenerLoadBalancerSourceRanges](./values.yaml#L336) | list | Allowed inbound IP for the agentListener service | `["0.0.0.0/0"]` | +| [controller.agentListenerNodePort](./values.yaml#L330) | string | Node port to listen for agents | `nil` | +| [controller.agentListenerPort](./values.yaml#L326) | int | Listening port for agents | `50000` | +| [controller.agentListenerServiceAnnotations](./values.yaml#L359) | object | Annotations for the agentListener service | `{}` | +| [controller.agentListenerServiceType](./values.yaml#L356) | string | Defines how to expose the agentListener service | `"ClusterIP"` | +| [controller.backendconfig.annotations](./values.yaml#L769) | object | backendconfig annotations | `{}` | +| [controller.backendconfig.apiVersion](./values.yaml#L763) | string | backendconfig API version | `"extensions/v1beta1"` | +| [controller.backendconfig.enabled](./values.yaml#L761) | bool | Enables backendconfig | `false` | +| [controller.backendconfig.labels](./values.yaml#L767) | object | backendconfig labels | `{}` | +| [controller.backendconfig.name](./values.yaml#L765) | string | backendconfig name | `nil` | +| [controller.backendconfig.spec](./values.yaml#L771) | object | backendconfig spec | `{}` | +| [controller.cloudName](./values.yaml#L493) | string | Name of default cloud configuration. | `"kubernetes"` | +| [controller.clusterIp](./values.yaml#L223) | string | k8s service clusterIP. Only used if serviceType is ClusterIP | `nil` | +| [controller.componentName](./values.yaml#L34) | string | Used for label app.kubernetes.io/component | `"jenkins-controller"` | +| [controller.containerEnv](./values.yaml#L156) | list | Environment variables for Jenkins Container | `[]` | +| [controller.containerEnvFrom](./values.yaml#L153) | list | Environment variable sources for Jenkins Container | `[]` | +| [controller.containerSecurityContext](./values.yaml#L211) | object | Allow controlling the securityContext for the jenkins container | `{"allowPrivilegeEscalation":false,"readOnlyRootFilesystem":true,"runAsGroup":1000,"runAsUser":1000}` | +| [controller.csrf.defaultCrumbIssuer.enabled](./values.yaml#L345) | bool | Enable the default CSRF Crumb issuer | `true` | +| [controller.csrf.defaultCrumbIssuer.proxyCompatability](./values.yaml#L347) | bool | Enable proxy compatibility | `true` | +| [controller.customInitContainers](./values.yaml#L547) | list | Custom init-container specification in raw-yaml format | `[]` | +| [controller.customJenkinsLabels](./values.yaml#L68) | list | Append Jenkins labels to the controller | `[]` | +| [controller.disableRememberMe](./values.yaml#L59) | bool | Disable use of remember me | `false` | +| [controller.disabledAgentProtocols](./values.yaml#L339) | list | Disabled agent protocols | `["JNLP-connect","JNLP2-connect"]` | +| [controller.enableRawHtmlMarkupFormatter](./values.yaml#L435) | bool | Enable HTML parsing using OWASP Markup Formatter Plugin (antisamy-markup-formatter) | `false` | +| [controller.enableServiceLinks](./values.yaml#L130) | bool | | `false` | +| [controller.executorMode](./values.yaml#L65) | string | Sets the executor mode of the Jenkins node. Possible values are "NORMAL" or "EXCLUSIVE" | `"NORMAL"` | +| [controller.existingSecret](./values.yaml#L462) | string | | `nil` | +| [controller.extraPorts](./values.yaml#L394) | list | Optionally configure other ports to expose in the controller container | `[]` | +| [controller.fsGroup](./values.yaml#L192) | int | Deprecated in favor of `controller.podSecurityContextOverride`. uid that will be used for persistent volume. | `1000` | +| [controller.googlePodMonitor.enabled](./values.yaml#L832) | bool | | `false` | +| [controller.googlePodMonitor.scrapeEndpoint](./values.yaml#L837) | string | | `"/prometheus"` | +| [controller.googlePodMonitor.scrapeInterval](./values.yaml#L835) | string | | `"60s"` | +| [controller.healthProbes](./values.yaml#L254) | bool | Enable Kubernetes Probes configuration configured in `controller.probes` | `true` | +| [controller.hostAliases](./values.yaml#L785) | list | Allows for adding entries to Pod /etc/hosts | `[]` | +| [controller.hostNetworking](./values.yaml#L70) | bool | | `false` | +| [controller.httpsKeyStore.disableSecretMount](./values.yaml#L853) | bool | | `false` | +| [controller.httpsKeyStore.enable](./values.yaml#L844) | bool | Enables HTTPS keystore on jenkins controller | `false` | +| [controller.httpsKeyStore.fileName](./values.yaml#L861) | string | Jenkins keystore filename which will appear under controller.httpsKeyStore.path | `"keystore.jks"` | +| [controller.httpsKeyStore.httpPort](./values.yaml#L857) | int | HTTP Port that Jenkins should listen to along with HTTPS, it also serves as the liveness and readiness probes port. | `8081` | +| [controller.httpsKeyStore.jenkinsHttpsJksPasswordSecretKey](./values.yaml#L852) | string | Name of the key in the secret that contains the JKS password | `"https-jks-password"` | +| [controller.httpsKeyStore.jenkinsHttpsJksPasswordSecretName](./values.yaml#L850) | string | Name of the secret that contains the JKS password, if it is not in the same secret as the JKS file | `""` | +| [controller.httpsKeyStore.jenkinsHttpsJksSecretKey](./values.yaml#L848) | string | Name of the key in the secret that already has ssl keystore | `"jenkins-jks-file"` | +| [controller.httpsKeyStore.jenkinsHttpsJksSecretName](./values.yaml#L846) | string | Name of the secret that already has ssl keystore | `""` | +| [controller.httpsKeyStore.jenkinsKeyStoreBase64Encoded](./values.yaml#L866) | string | Base64 encoded Keystore content. Keystore must be converted to base64 then being pasted here | `nil` | +| [controller.httpsKeyStore.password](./values.yaml#L863) | string | Jenkins keystore password | `"password"` | +| [controller.httpsKeyStore.path](./values.yaml#L859) | string | Path of HTTPS keystore file | `"/var/jenkins_keystore"` | +| [controller.image.pullPolicy](./values.yaml#L47) | string | Controller image pull policy | `"Always"` | +| [controller.image.registry](./values.yaml#L37) | string | Controller image registry | `"docker.io"` | +| [controller.image.repository](./values.yaml#L39) | string | Controller image repository | `"jenkins/jenkins"` | +| [controller.image.tag](./values.yaml#L42) | string | Controller image tag override; i.e., tag: "2.440.1-jdk17" | `nil` | +| [controller.image.tagLabel](./values.yaml#L45) | string | Controller image tag label | `"jdk17"` | +| [controller.imagePullSecretName](./values.yaml#L49) | string | Controller image pull secret | `nil` | +| [controller.ingress.annotations](./values.yaml#L708) | object | Ingress annotations | `{}` | +| [controller.ingress.apiVersion](./values.yaml#L704) | string | Ingress API version | `"extensions/v1beta1"` | +| [controller.ingress.enabled](./values.yaml#L687) | bool | Enables ingress | `false` | +| [controller.ingress.hostName](./values.yaml#L721) | string | Ingress hostname | `nil` | +| [controller.ingress.labels](./values.yaml#L706) | object | Ingress labels | `{}` | +| [controller.ingress.path](./values.yaml#L717) | string | Ingress path | `nil` | +| [controller.ingress.paths](./values.yaml#L691) | list | Override for the default Ingress paths | `[]` | +| [controller.ingress.resourceRootUrl](./values.yaml#L723) | string | Hostname to serve assets from | `nil` | +| [controller.ingress.tls](./values.yaml#L725) | list | Ingress TLS configuration | `[]` | +| [controller.initConfigMap](./values.yaml#L452) | string | Name of the existing ConfigMap that contains init scripts | `nil` | +| [controller.initContainerEnv](./values.yaml#L147) | list | Environment variables for Init Container | `[]` | +| [controller.initContainerEnvFrom](./values.yaml#L143) | list | Environment variable sources for Init Container | `[]` | +| [controller.initContainerResources](./values.yaml#L134) | object | Resources allocation (Requests and Limits) for Init Container | `{}` | +| [controller.initScripts](./values.yaml#L448) | object | Map of groovy init scripts to be executed during Jenkins controller start | `{}` | +| [controller.initializeOnce](./values.yaml#L420) | bool | Initialize only on first installation. Ensures plugins do not get updated inadvertently. Requires `persistence.enabled` to be set to `true` | `false` | +| [controller.installLatestPlugins](./values.yaml#L409) | bool | Download the minimum required version or latest version of all dependencies | `true` | +| [controller.installLatestSpecifiedPlugins](./values.yaml#L412) | bool | Set to true to download the latest version of any plugin that is requested to have the latest version | `false` | +| [controller.installPlugins](./values.yaml#L401) | list | List of Jenkins plugins to install. If you don't want to install plugins, set it to `false` | `["kubernetes:4288.v1719f9d0c854","workflow-aggregator:600.vb_57cdd26fdd7","git:5.4.1","configuration-as-code:1850.va_a_8c31d3158b_"]` | +| [controller.javaOpts](./values.yaml#L162) | string | Append to `JAVA_OPTS` env var | `nil` | +| [controller.jenkinsAdminEmail](./values.yaml#L96) | string | Email address for the administrator of the Jenkins instance | `nil` | +| [controller.jenkinsHome](./values.yaml#L101) | string | Custom Jenkins home path | `"/var/jenkins_home"` | +| [controller.jenkinsOpts](./values.yaml#L164) | string | Append to `JENKINS_OPTS` env var | `nil` | +| [controller.jenkinsRef](./values.yaml#L106) | string | Custom Jenkins reference path | `"/usr/share/jenkins/ref"` | +| [controller.jenkinsUriPrefix](./values.yaml#L179) | string | Root URI Jenkins will be served on | `nil` | +| [controller.jenkinsUrl](./values.yaml#L174) | string | Set Jenkins URL if you are not using the ingress definitions provided by the chart | `nil` | +| [controller.jenkinsUrlProtocol](./values.yaml#L171) | string | Set protocol for Jenkins URL; `https` if `controller.ingress.tls`, `http` otherwise | `nil` | +| [controller.jenkinsWar](./values.yaml#L109) | string | | `"/usr/share/jenkins/jenkins.war"` | +| [controller.jmxPort](./values.yaml#L391) | string | Open a port, for JMX stats | `nil` | +| [controller.legacyRemotingSecurityEnabled](./values.yaml#L367) | bool | Whether legacy remoting security should be enabled | `false` | +| [controller.lifecycle](./values.yaml#L51) | object | Lifecycle specification for controller-container | `{}` | +| [controller.loadBalancerIP](./values.yaml#L382) | string | Optionally assign a known public LB IP | `nil` | +| [controller.loadBalancerSourceRanges](./values.yaml#L378) | list | Allowed inbound IP addresses | `["0.0.0.0/0"]` | +| [controller.markupFormatter](./values.yaml#L439) | string | Yaml of the markup formatter to use | `"plainText"` | +| [controller.nodePort](./values.yaml#L229) | string | k8s node port. Only used if serviceType is NodePort | `nil` | +| [controller.nodeSelector](./values.yaml#L653) | object | Node labels for pod assignment | `{}` | +| [controller.numExecutors](./values.yaml#L62) | int | Set Number of executors | `0` | +| [controller.overwritePlugins](./values.yaml#L424) | bool | Overwrite installed plugins on start | `false` | +| [controller.overwritePluginsFromImage](./values.yaml#L428) | bool | Overwrite plugins that are already installed in the controller image | `true` | +| [controller.podAnnotations](./values.yaml#L674) | object | Annotations for controller pod | `{}` | +| [controller.podDisruptionBudget.annotations](./values.yaml#L318) | object | | `{}` | +| [controller.podDisruptionBudget.apiVersion](./values.yaml#L316) | string | Policy API version | `"policy/v1beta1"` | +| [controller.podDisruptionBudget.enabled](./values.yaml#L311) | bool | Enable Kubernetes Pod Disruption Budget configuration | `false` | +| [controller.podDisruptionBudget.labels](./values.yaml#L319) | object | | `{}` | +| [controller.podDisruptionBudget.maxUnavailable](./values.yaml#L321) | string | Number of pods that can be unavailable. Either an absolute number or a percentage | `"0"` | +| [controller.podLabels](./values.yaml#L247) | object | Custom Pod labels (an object with `label-key: label-value` pairs) | `{}` | +| [controller.podSecurityContextOverride](./values.yaml#L208) | string | Completely overwrites the contents of the pod security context, ignoring the values provided for `runAsUser`, `fsGroup`, and `securityContextCapabilities` | `nil` | +| [controller.priorityClassName](./values.yaml#L671) | string | The name of a `priorityClass` to apply to the controller pod | `nil` | +| [controller.probes.livenessProbe.failureThreshold](./values.yaml#L272) | int | Set the failure threshold for the liveness probe | `5` | +| [controller.probes.livenessProbe.httpGet.path](./values.yaml#L275) | string | Set the Pod's HTTP path for the liveness probe | `"{{ default \"\" .Values.controller.jenkinsUriPrefix }}/login"` | +| [controller.probes.livenessProbe.httpGet.port](./values.yaml#L277) | string | Set the Pod's HTTP port to use for the liveness probe | `"http"` | +| [controller.probes.livenessProbe.initialDelaySeconds](./values.yaml#L286) | string | Set the initial delay for the liveness probe in seconds | `nil` | +| [controller.probes.livenessProbe.periodSeconds](./values.yaml#L279) | int | Set the time interval between two liveness probes executions in seconds | `10` | +| [controller.probes.livenessProbe.timeoutSeconds](./values.yaml#L281) | int | Set the timeout for the liveness probe in seconds | `5` | +| [controller.probes.readinessProbe.failureThreshold](./values.yaml#L290) | int | Set the failure threshold for the readiness probe | `3` | +| [controller.probes.readinessProbe.httpGet.path](./values.yaml#L293) | string | Set the Pod's HTTP path for the liveness probe | `"{{ default \"\" .Values.controller.jenkinsUriPrefix }}/login"` | +| [controller.probes.readinessProbe.httpGet.port](./values.yaml#L295) | string | Set the Pod's HTTP port to use for the readiness probe | `"http"` | +| [controller.probes.readinessProbe.initialDelaySeconds](./values.yaml#L304) | string | Set the initial delay for the readiness probe in seconds | `nil` | +| [controller.probes.readinessProbe.periodSeconds](./values.yaml#L297) | int | Set the time interval between two readiness probes executions in seconds | `10` | +| [controller.probes.readinessProbe.timeoutSeconds](./values.yaml#L299) | int | Set the timeout for the readiness probe in seconds | `5` | +| [controller.probes.startupProbe.failureThreshold](./values.yaml#L259) | int | Set the failure threshold for the startup probe | `12` | +| [controller.probes.startupProbe.httpGet.path](./values.yaml#L262) | string | Set the Pod's HTTP path for the startup probe | `"{{ default \"\" .Values.controller.jenkinsUriPrefix }}/login"` | +| [controller.probes.startupProbe.httpGet.port](./values.yaml#L264) | string | Set the Pod's HTTP port to use for the startup probe | `"http"` | +| [controller.probes.startupProbe.periodSeconds](./values.yaml#L266) | int | Set the time interval between two startup probes executions in seconds | `10` | +| [controller.probes.startupProbe.timeoutSeconds](./values.yaml#L268) | int | Set the timeout for the startup probe in seconds | `5` | +| [controller.projectNamingStrategy](./values.yaml#L431) | string | | `"standard"` | +| [controller.prometheus.alertingRulesAdditionalLabels](./values.yaml#L818) | object | Additional labels to add to the PrometheusRule object | `{}` | +| [controller.prometheus.alertingrules](./values.yaml#L816) | list | Array of prometheus alerting rules | `[]` | +| [controller.prometheus.enabled](./values.yaml#L801) | bool | Enables prometheus service monitor | `false` | +| [controller.prometheus.metricRelabelings](./values.yaml#L828) | list | | `[]` | +| [controller.prometheus.prometheusRuleNamespace](./values.yaml#L820) | string | Set a custom namespace where to deploy PrometheusRule resource | `""` | +| [controller.prometheus.relabelings](./values.yaml#L826) | list | | `[]` | +| [controller.prometheus.scrapeEndpoint](./values.yaml#L811) | string | The endpoint prometheus should get metrics from | `"/prometheus"` | +| [controller.prometheus.scrapeInterval](./values.yaml#L807) | string | How often prometheus should scrape metrics | `"60s"` | +| [controller.prometheus.serviceMonitorAdditionalLabels](./values.yaml#L803) | object | Additional labels to add to the service monitor object | `{}` | +| [controller.prometheus.serviceMonitorNamespace](./values.yaml#L805) | string | Set a custom namespace where to deploy ServiceMonitor resource | `nil` | +| [controller.resources](./values.yaml#L115) | object | Resource allocation (Requests and Limits) | `{"limits":{"cpu":"2000m","memory":"4096Mi"},"requests":{"cpu":"50m","memory":"256Mi"}}` | +| [controller.route.annotations](./values.yaml#L780) | object | Route annotations | `{}` | +| [controller.route.enabled](./values.yaml#L776) | bool | Enables openshift route | `false` | +| [controller.route.labels](./values.yaml#L778) | object | Route labels | `{}` | +| [controller.route.path](./values.yaml#L782) | string | Route path | `nil` | +| [controller.runAsUser](./values.yaml#L189) | int | Deprecated in favor of `controller.podSecurityContextOverride`. uid that jenkins runs with. | `1000` | +| [controller.schedulerName](./values.yaml#L649) | string | Name of the Kubernetes scheduler to use | `""` | +| [controller.scriptApproval](./values.yaml#L443) | list | List of groovy functions to approve | `[]` | +| [controller.secondaryingress.annotations](./values.yaml#L743) | object | | `{}` | +| [controller.secondaryingress.apiVersion](./values.yaml#L741) | string | | `"extensions/v1beta1"` | +| [controller.secondaryingress.enabled](./values.yaml#L735) | bool | | `false` | +| [controller.secondaryingress.hostName](./values.yaml#L750) | string | | `nil` | +| [controller.secondaryingress.labels](./values.yaml#L742) | object | | `{}` | +| [controller.secondaryingress.paths](./values.yaml#L738) | list | | `[]` | +| [controller.secondaryingress.tls](./values.yaml#L751) | string | | `nil` | +| [controller.secretClaims](./values.yaml#L486) | list | List of `SecretClaim` resources to create | `[]` | +| [controller.securityContextCapabilities](./values.yaml#L198) | object | | `{}` | +| [controller.serviceAnnotations](./values.yaml#L236) | object | Jenkins controller service annotations | `{}` | +| [controller.serviceExternalTrafficPolicy](./values.yaml#L233) | string | | `nil` | +| [controller.serviceLabels](./values.yaml#L242) | object | Labels for the Jenkins controller-service | `{}` | +| [controller.servicePort](./values.yaml#L225) | int | k8s service port | `8080` | +| [controller.serviceType](./values.yaml#L220) | string | k8s service type | `"ClusterIP"` | +| [controller.shareProcessNamespace](./values.yaml#L124) | bool | | `false` | +| [controller.sidecars.additionalSidecarContainers](./values.yaml#L631) | list | Configures additional sidecar container(s) for the Jenkins controller | `[]` | +| [controller.sidecars.configAutoReload.additionalVolumeMounts](./values.yaml#L577) | list | Enables additional volume mounts for the config auto-reload container | `[]` | +| [controller.sidecars.configAutoReload.containerSecurityContext](./values.yaml#L626) | object | Enable container security context | `{"allowPrivilegeEscalation":false,"readOnlyRootFilesystem":true}` | +| [controller.sidecars.configAutoReload.enabled](./values.yaml#L560) | bool | Enables Jenkins Config as Code auto-reload | `true` | +| [controller.sidecars.configAutoReload.env](./values.yaml#L608) | object | Environment variables for the Jenkins Config as Code auto-reload container | `{}` | +| [controller.sidecars.configAutoReload.envFrom](./values.yaml#L606) | list | Environment variable sources for the Jenkins Config as Code auto-reload container | `[]` | +| [controller.sidecars.configAutoReload.folder](./values.yaml#L619) | string | | `"/var/jenkins_home/casc_configs"` | +| [controller.sidecars.configAutoReload.image.registry](./values.yaml#L563) | string | Registry for the image that triggers the reload | `"docker.io"` | +| [controller.sidecars.configAutoReload.image.repository](./values.yaml#L565) | string | Repository of the image that triggers the reload | `"kiwigrid/k8s-sidecar"` | +| [controller.sidecars.configAutoReload.image.tag](./values.yaml#L567) | string | Tag for the image that triggers the reload | `"1.27.6"` | +| [controller.sidecars.configAutoReload.imagePullPolicy](./values.yaml#L568) | string | | `"IfNotPresent"` | +| [controller.sidecars.configAutoReload.logging](./values.yaml#L583) | object | Config auto-reload logging settings | `{"configuration":{"backupCount":3,"formatter":"JSON","logLevel":"INFO","logToConsole":true,"logToFile":false,"maxBytes":1024,"override":false}}` | +| [controller.sidecars.configAutoReload.logging.configuration.override](./values.yaml#L587) | bool | Enables custom log config utilizing using the settings below. | `false` | +| [controller.sidecars.configAutoReload.reqRetryConnect](./values.yaml#L601) | int | How many connection-related errors to retry on | `10` | +| [controller.sidecars.configAutoReload.resources](./values.yaml#L569) | object | | `{}` | +| [controller.sidecars.configAutoReload.scheme](./values.yaml#L596) | string | The scheme to use when connecting to the Jenkins configuration as code endpoint | `"http"` | +| [controller.sidecars.configAutoReload.skipTlsVerify](./values.yaml#L598) | bool | Skip TLS verification when connecting to the Jenkins configuration as code endpoint | `false` | +| [controller.sidecars.configAutoReload.sleepTime](./values.yaml#L603) | string | How many seconds to wait before updating config-maps/secrets (sets METHOD=SLEEP on the sidecar) | `nil` | +| [controller.sidecars.configAutoReload.sshTcpPort](./values.yaml#L617) | int | | `1044` | +| [controller.statefulSetAnnotations](./values.yaml#L676) | object | Annotations for controller StatefulSet | `{}` | +| [controller.statefulSetLabels](./values.yaml#L238) | object | Jenkins controller custom labels for the StatefulSet | `{}` | +| [controller.targetPort](./values.yaml#L227) | int | k8s target port | `8080` | +| [controller.terminationGracePeriodSeconds](./values.yaml#L659) | string | Set TerminationGracePeriodSeconds | `nil` | +| [controller.terminationMessagePath](./values.yaml#L661) | string | Set the termination message path | `nil` | +| [controller.terminationMessagePolicy](./values.yaml#L663) | string | Set the termination message policy | `nil` | +| [controller.testEnabled](./values.yaml#L840) | bool | Can be used to disable rendering controller test resources when using helm template | `true` | +| [controller.tolerations](./values.yaml#L657) | list | Toleration labels for pod assignment | `[]` | +| [controller.topologySpreadConstraints](./values.yaml#L683) | object | Topology spread constraints | `{}` | +| [controller.updateStrategy](./values.yaml#L680) | object | Update strategy for StatefulSet | `{}` | +| [controller.usePodSecurityContext](./values.yaml#L182) | bool | Enable pod security context (must be `true` if podSecurityContextOverride, runAsUser or fsGroup are set) | `true` | +| [credentialsId](./values.yaml#L27) | string | The Jenkins credentials to access the Kubernetes API server. For the default cluster it is not needed. | `nil` | +| [fullnameOverride](./values.yaml#L13) | string | Override the full resource names | `jenkins-(release-name)` or `jenkins` if the release-name is `jenkins` | +| [helmtest.bats.image.registry](./values.yaml#L1359) | string | Registry of the image used to test the framework | `"docker.io"` | +| [helmtest.bats.image.repository](./values.yaml#L1361) | string | Repository of the image used to test the framework | `"bats/bats"` | +| [helmtest.bats.image.tag](./values.yaml#L1363) | string | Tag of the image to test the framework | `"1.11.0"` | +| [kubernetesURL](./values.yaml#L24) | string | The URL of the Kubernetes API server | `"https://kubernetes.default"` | +| [nameOverride](./values.yaml#L10) | string | Override the resource name prefix | `Chart.Name` | +| [namespaceOverride](./values.yaml#L16) | string | Override the deployment namespace | `Release.Namespace` | +| [networkPolicy.apiVersion](./values.yaml#L1289) | string | NetworkPolicy ApiVersion | `"networking.k8s.io/v1"` | +| [networkPolicy.enabled](./values.yaml#L1284) | bool | Enable the creation of NetworkPolicy resources | `false` | +| [networkPolicy.externalAgents.except](./values.yaml#L1303) | list | A list of IP sub-ranges to be excluded from the allowlisted IP range | `[]` | +| [networkPolicy.externalAgents.ipCIDR](./values.yaml#L1301) | string | The IP range from which external agents are allowed to connect to controller, i.e., 172.17.0.0/16 | `nil` | +| [networkPolicy.internalAgents.allowed](./values.yaml#L1293) | bool | Allow internal agents (from the same cluster) to connect to controller. Agent pods will be filtered based on PodLabels | `true` | +| [networkPolicy.internalAgents.namespaceLabels](./values.yaml#L1297) | object | A map of labels (keys/values) that agents namespaces must have to be able to connect to controller | `{}` | +| [networkPolicy.internalAgents.podLabels](./values.yaml#L1295) | object | A map of labels (keys/values) that agent pods must have to be able to connect to controller | `{}` | +| [persistence.accessMode](./values.yaml#L1259) | string | The PVC access mode | `"ReadWriteOnce"` | +| [persistence.annotations](./values.yaml#L1255) | object | Annotations for the PVC | `{}` | +| [persistence.dataSource](./values.yaml#L1265) | object | Existing data source to clone PVC from | `{}` | +| [persistence.enabled](./values.yaml#L1239) | bool | Enable the use of a Jenkins PVC | `true` | +| [persistence.existingClaim](./values.yaml#L1245) | string | Provide the name of a PVC | `nil` | +| [persistence.labels](./values.yaml#L1257) | object | Labels for the PVC | `{}` | +| [persistence.mounts](./values.yaml#L1277) | list | Additional mounts | `[]` | +| [persistence.size](./values.yaml#L1261) | string | The size of the PVC | `"8Gi"` | +| [persistence.storageClass](./values.yaml#L1253) | string | Storage class for the PVC | `nil` | +| [persistence.subPath](./values.yaml#L1270) | string | SubPath for jenkins-home mount | `nil` | +| [persistence.volumes](./values.yaml#L1272) | list | Additional volumes | `[]` | +| [rbac.create](./values.yaml#L1309) | bool | Whether RBAC resources are created | `true` | +| [rbac.readSecrets](./values.yaml#L1311) | bool | Whether the Jenkins service account should be able to read Kubernetes secrets | `false` | +| [renderHelmLabels](./values.yaml#L30) | bool | Enables rendering of the helm.sh/chart label to the annotations | `true` | +| [serviceAccount.annotations](./values.yaml#L1321) | object | Configures annotations for the ServiceAccount | `{}` | +| [serviceAccount.create](./values.yaml#L1315) | bool | Configures if a ServiceAccount with this name should be created | `true` | +| [serviceAccount.extraLabels](./values.yaml#L1323) | object | Configures extra labels for the ServiceAccount | `{}` | +| [serviceAccount.imagePullSecretName](./values.yaml#L1325) | string | Controller ServiceAccount image pull secret | `nil` | +| [serviceAccount.name](./values.yaml#L1319) | string | | `nil` | +| [serviceAccountAgent.annotations](./values.yaml#L1336) | object | Configures annotations for the agent ServiceAccount | `{}` | +| [serviceAccountAgent.create](./values.yaml#L1330) | bool | Configures if an agent ServiceAccount should be created | `false` | +| [serviceAccountAgent.extraLabels](./values.yaml#L1338) | object | Configures extra labels for the agent ServiceAccount | `{}` | +| [serviceAccountAgent.imagePullSecretName](./values.yaml#L1340) | string | Agent ServiceAccount image pull secret | `nil` | +| [serviceAccountAgent.name](./values.yaml#L1334) | string | The name of the agent ServiceAccount to be used by access-controlled resources | `nil` | diff --git a/charts/jenkins/jenkins/5.6.2/VALUES.md.gotmpl b/charts/jenkins/jenkins/5.6.2/VALUES.md.gotmpl new file mode 100644 index 000000000..21080e35a --- /dev/null +++ b/charts/jenkins/jenkins/5.6.2/VALUES.md.gotmpl @@ -0,0 +1,28 @@ +# Jenkins + +## Configuration + +The following tables list the configurable parameters of the Jenkins chart and their default values. + +{{- define "chart.valueDefaultColumnRender" -}} +{{- $defaultValue := (trimAll "`" (default .Default .AutoDefault) | replace "\n" "") -}} +`{{- $defaultValue | replace "\n" "" -}}` +{{- end -}} + +{{- define "chart.typeColumnRender" -}} +{{- .Type -}} +{{- end -}} + +{{- define "chart.valueDescription" -}} +{{- default .Description .AutoDescription }} +{{- end -}} + +{{- define "chart.valuesTable" -}} +| Key | Type | Description | Default | +|:----|:-----|:---------|:------------| +{{- range .Values }} +| [{{ .Key }}](./values.yaml#L{{ .LineNumber }}) | {{ template "chart.typeColumnRender" . }} | {{ template "chart.valueDescription" . }} | {{ template "chart.valueDefaultColumnRender" . }} | +{{- end }} +{{- end }} + +{{ template "chart.valuesSection" . }} diff --git a/charts/jenkins/jenkins/5.6.2/templates/NOTES.txt b/charts/jenkins/jenkins/5.6.2/templates/NOTES.txt new file mode 100644 index 000000000..953dd2606 --- /dev/null +++ b/charts/jenkins/jenkins/5.6.2/templates/NOTES.txt @@ -0,0 +1,68 @@ +{{- $prefix := .Values.controller.jenkinsUriPrefix | default "" -}} +{{- $url := "" -}} +1. Get your '{{ .Values.controller.admin.username }}' user password by running: + kubectl exec --namespace {{ template "jenkins.namespace" . }} -it svc/{{ template "jenkins.fullname" . }} -c jenkins -- /bin/cat /run/secrets/additional/chart-admin-password && echo +{{- if .Values.controller.ingress.hostName -}} +{{- if .Values.controller.ingress.tls -}} +{{- $url = print "https://" .Values.controller.ingress.hostName $prefix -}} +{{- else -}} +{{- $url = print "http://" .Values.controller.ingress.hostName $prefix -}} +{{- end }} +2. Visit {{ $url }} +{{- else }} +2. Get the Jenkins URL to visit by running these commands in the same shell: +{{- if contains "NodePort" .Values.controller.serviceType }} + export NODE_PORT=$(kubectl get --namespace {{ template "jenkins.namespace" . }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ template "jenkins.fullname" . }}) + export NODE_IP=$(kubectl get nodes --namespace {{ template "jenkins.namespace" . }} -o jsonpath="{.items[0].status.addresses[0].address}") +{{- if .Values.controller.httpsKeyStore.enable -}} +{{- $url = print "https://$NODE_IP:$NODE_PORT" $prefix -}} +{{- else -}} +{{- $url = print "http://$NODE_IP:$NODE_PORT" $prefix -}} +{{- end }} + echo {{ $url }} + +{{- else if contains "LoadBalancer" .Values.controller.serviceType }} + 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 {{ template "jenkins.namespace" . }} -w {{ template "jenkins.fullname" . }}' + export SERVICE_IP=$(kubectl get svc --namespace {{ template "jenkins.namespace" . }} {{ template "jenkins.fullname" . }} --template "{{ "{{ range (index .status.loadBalancer.ingress 0) }}{{ . }}{{ end }}" }}") +{{- if .Values.controller.httpsKeyStore.enable -}} +{{- $url = print "https://$SERVICE_IP:" .Values.controller.servicePort $prefix -}} +{{- else -}} +{{- $url = print "http://$SERVICE_IP:" .Values.controller.servicePort $prefix -}} +{{- end }} + echo {{ $url }} + +{{- else if contains "ClusterIP" .Values.controller.serviceType -}} +{{- if .Values.controller.httpsKeyStore.enable -}} +{{- $url = print "https://127.0.0.1:" .Values.controller.servicePort $prefix -}} +{{- else -}} +{{- $url = print "http://127.0.0.1:" .Values.controller.servicePort $prefix -}} +{{- end }} + echo {{ $url }} + kubectl --namespace {{ template "jenkins.namespace" . }} port-forward svc/{{template "jenkins.fullname" . }} {{ .Values.controller.servicePort }}:{{ .Values.controller.servicePort }} +{{- end }} +{{- end }} + +3. Login with the password from step 1 and the username: {{ .Values.controller.admin.username }} +4. Configure security realm and authorization strategy +5. Use Jenkins Configuration as Code by specifying configScripts in your values.yaml file, see documentation: {{ $url }}/configuration-as-code and examples: https://github.com/jenkinsci/configuration-as-code-plugin/tree/master/demos + +For more information on running Jenkins on Kubernetes, visit: +https://cloud.google.com/solutions/jenkins-on-container-engine + +For more information about Jenkins Configuration as Code, visit: +https://jenkins.io/projects/jcasc/ + +{{ if and (eq .Values.controller.image.repository "jenkins/jenkins") (eq .Values.controller.image.registry "docker.io") }} +NOTE: Consider using a custom image with pre-installed plugins +{{- else if .Values.controller.installPlugins }} +NOTE: Consider disabling `installPlugins` if your image already contains plugins. +{{- end }} + +{{- if .Values.persistence.enabled }} +{{- else }} +################################################################################# +###### WARNING: Persistence is disabled!!! You will lose your data when ##### +###### the Jenkins pod is terminated. ##### +################################################################################# +{{- end }} diff --git a/charts/jenkins/jenkins/5.6.2/templates/_helpers.tpl b/charts/jenkins/jenkins/5.6.2/templates/_helpers.tpl new file mode 100644 index 000000000..dd3895b65 --- /dev/null +++ b/charts/jenkins/jenkins/5.6.2/templates/_helpers.tpl @@ -0,0 +1,684 @@ +{{/* vim: set filetype=mustache: */}} +{{/* +Expand the name of the chart. +*/}} +{{- define "jenkins.name" -}} +{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}} +{{- end -}} + +{{/* +Expand the label of the chart. +*/}} +{{- define "jenkins.label" -}} +{{- printf "%s-%s" (include "jenkins.name" .) .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}} +{{- end -}} + + +{{/* +Allow the release namespace to be overridden for multi-namespace deployments in combined charts. +*/}} +{{- define "jenkins.namespace" -}} + {{- if .Values.namespaceOverride -}} + {{- .Values.namespaceOverride -}} + {{- else -}} + {{- .Release.Namespace -}} + {{- end -}} +{{- end -}} + +{{- define "jenkins.agent.namespace" -}} + {{- if .Values.agent.namespace -}} + {{- tpl .Values.agent.namespace . -}} + {{- else -}} + {{- if .Values.namespaceOverride -}} + {{- .Values.namespaceOverride -}} + {{- else -}} + {{- .Release.Namespace -}} + {{- 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 "jenkins.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 -}} + +{{/* +Returns the admin password +https://github.com/helm/charts/issues/5167#issuecomment-619137759 +*/}} +{{- define "jenkins.password" -}} + {{- if .Values.controller.admin.password -}} + {{- .Values.controller.admin.password | b64enc | quote }} + {{- else -}} + {{- $secret := (lookup "v1" "Secret" .Release.Namespace (include "jenkins.fullname" .)).data -}} + {{- if $secret -}} + {{/* + Reusing current password since secret exists + */}} + {{- index $secret ( .Values.controller.admin.passwordKey | default "jenkins-admin-password" ) -}} + {{- else -}} + {{/* + Generate new password + */}} + {{- randAlphaNum 22 | b64enc | quote }} + {{- end -}} + {{- end -}} +{{- end -}} + +{{/* +Returns the Jenkins URL +*/}} +{{- define "jenkins.url" -}} +{{- if .Values.controller.jenkinsUrl }} + {{- .Values.controller.jenkinsUrl }} +{{- else }} + {{- if .Values.controller.ingress.hostName }} + {{- if .Values.controller.ingress.tls }} + {{- default "https" .Values.controller.jenkinsUrlProtocol }}://{{ tpl .Values.controller.ingress.hostName $ }}{{ default "" .Values.controller.jenkinsUriPrefix }} + {{- else }} + {{- default "http" .Values.controller.jenkinsUrlProtocol }}://{{ tpl .Values.controller.ingress.hostName $ }}{{ default "" .Values.controller.jenkinsUriPrefix }} + {{- end }} + {{- else }} + {{- default "http" .Values.controller.jenkinsUrlProtocol }}://{{ template "jenkins.fullname" . }}:{{.Values.controller.servicePort}}{{ default "" .Values.controller.jenkinsUriPrefix }} + {{- end}} +{{- end}} +{{- end -}} + +{{/* +Returns configuration as code default config +*/}} +{{- define "jenkins.casc.defaults" -}} +jenkins: + {{- $configScripts := toYaml .Values.controller.JCasC.configScripts }} + {{- if and (.Values.controller.JCasC.authorizationStrategy) (not (contains "authorizationStrategy:" $configScripts)) }} + authorizationStrategy: + {{- tpl .Values.controller.JCasC.authorizationStrategy . | nindent 4 }} + {{- end }} + {{- if and (.Values.controller.JCasC.securityRealm) (not (contains "securityRealm:" $configScripts)) }} + securityRealm: + {{- tpl .Values.controller.JCasC.securityRealm . | nindent 4 }} + {{- end }} + disableRememberMe: {{ .Values.controller.disableRememberMe }} + {{- if .Values.controller.legacyRemotingSecurityEnabled }} + remotingSecurity: + enabled: true + {{- end }} + mode: {{ .Values.controller.executorMode }} + numExecutors: {{ .Values.controller.numExecutors }} + {{- if not (kindIs "invalid" .Values.controller.customJenkinsLabels) }} + labelString: "{{ join " " .Values.controller.customJenkinsLabels }}" + {{- end }} + {{- if .Values.controller.projectNamingStrategy }} + {{- if kindIs "string" .Values.controller.projectNamingStrategy }} + projectNamingStrategy: "{{ .Values.controller.projectNamingStrategy }}" + {{- else }} + projectNamingStrategy: + {{- toYaml .Values.controller.projectNamingStrategy | nindent 4 }} + {{- end }} + {{- end }} + markupFormatter: + {{- if .Values.controller.enableRawHtmlMarkupFormatter }} + rawHtml: + disableSyntaxHighlighting: true + {{- else }} + {{- toYaml .Values.controller.markupFormatter | nindent 4 }} + {{- end }} + clouds: + - kubernetes: + containerCapStr: "{{ .Values.agent.containerCap }}" + {{- if .Values.agent.garbageCollection.enabled }} + garbageCollection: + {{- if .Values.agent.garbageCollection.namespaces }} + namespaces: |- + {{- .Values.agent.garbageCollection.namespaces | nindent 10 }} + {{- end }} + timeout: "{{ .Values.agent.garbageCollection.timeout }}" + {{- end }} + {{- if .Values.agent.jnlpregistry }} + jnlpregistry: "{{ .Values.agent.jnlpregistry }}" + {{- end }} + defaultsProviderTemplate: "{{ .Values.agent.defaultsProviderTemplate }}" + connectTimeout: "{{ .Values.agent.kubernetesConnectTimeout }}" + readTimeout: "{{ .Values.agent.kubernetesReadTimeout }}" + {{- if .Values.agent.directConnection }} + directConnection: true + {{- else }} + {{- if .Values.agent.jenkinsUrl }} + jenkinsUrl: "{{ tpl .Values.agent.jenkinsUrl . }}" + {{- else }} + jenkinsUrl: "http://{{ template "jenkins.fullname" . }}.{{ template "jenkins.namespace" . }}.svc.{{.Values.clusterZone}}:{{.Values.controller.servicePort}}{{ default "" .Values.controller.jenkinsUriPrefix }}" + {{- end }} + {{- if not .Values.agent.websocket }} + {{- if .Values.agent.jenkinsTunnel }} + jenkinsTunnel: "{{ tpl .Values.agent.jenkinsTunnel . }}" + {{- else }} + jenkinsTunnel: "{{ template "jenkins.fullname" . }}-agent.{{ template "jenkins.namespace" . }}.svc.{{.Values.clusterZone}}:{{ .Values.controller.agentListenerPort }}" + {{- end }} + {{- else }} + webSocket: true + {{- end }} + {{- end }} + skipTlsVerify: {{ .Values.agent.skipTlsVerify | default false}} + usageRestricted: {{ .Values.agent.usageRestricted | default false}} + maxRequestsPerHostStr: {{ .Values.agent.maxRequestsPerHostStr | quote }} + retentionTimeout: {{ .Values.agent.retentionTimeout | quote }} + waitForPodSec: {{ .Values.agent.waitForPodSec | quote }} + name: "{{ .Values.controller.cloudName }}" + namespace: "{{ template "jenkins.agent.namespace" . }}" + restrictedPssSecurityContext: {{ .Values.agent.restrictedPssSecurityContext }} + serverUrl: "{{ .Values.kubernetesURL }}" + credentialsId: "{{ .Values.credentialsId }}" + {{- if .Values.agent.enabled }} + podLabels: + - key: "jenkins/{{ .Release.Name }}-{{ .Values.agent.componentName }}" + value: "true" + {{- range $key, $val := .Values.agent.podLabels }} + - key: {{ $key | quote }} + value: {{ $val | quote }} + {{- end }} + templates: + {{- if not .Values.agent.disableDefaultAgent }} + {{- include "jenkins.casc.podTemplate" . | nindent 8 }} + {{- end }} + {{- if .Values.additionalAgents }} + {{- /* save .Values.agent */}} + {{- $agent := .Values.agent }} + {{- range $name, $additionalAgent := .Values.additionalAgents }} + {{- $additionalContainersEmpty := and (hasKey $additionalAgent "additionalContainers") (empty $additionalAgent.additionalContainers) }} + {{- /* merge original .Values.agent into additional agent to ensure it at least has the default values */}} + {{- $additionalAgent := merge $additionalAgent $agent }} + {{- /* clear list of additional containers in case it is configured empty for this agent (merge might have overwritten that) */}} + {{- if $additionalContainersEmpty }} + {{- $_ := set $additionalAgent "additionalContainers" list }} + {{- end }} + {{- /* set .Values.agent to $additionalAgent */}} + {{- $_ := set $.Values "agent" $additionalAgent }} + {{- include "jenkins.casc.podTemplate" $ | nindent 8 }} + {{- end }} + {{- /* restore .Values.agent */}} + {{- $_ := set .Values "agent" $agent }} + {{- end }} + {{- if .Values.agent.podTemplates }} + {{- range $key, $val := .Values.agent.podTemplates }} + {{- tpl $val $ | nindent 8 }} + {{- end }} + {{- end }} + {{- end }} + {{- if .Values.additionalClouds }} + {{- /* save root */}} + {{- $oldRoot := deepCopy $ }} + {{- range $name, $additionalCloud := .Values.additionalClouds }} + {{- $newRoot := deepCopy $ }} + {{- /* clear additionalAgents from the copy if override set to `true` */}} + {{- if .additionalAgentsOverride }} + {{- $_ := set $newRoot.Values "additionalAgents" list}} + {{- end}} + {{- $newValues := merge $additionalCloud $newRoot.Values }} + {{- $_ := set $newRoot "Values" $newValues }} + {{- /* clear additionalClouds from the copy */}} + {{- $_ := set $newRoot.Values "additionalClouds" list }} + {{- with $newRoot}} + - kubernetes: + containerCapStr: "{{ .Values.agent.containerCap }}" + {{- if .Values.agent.jnlpregistry }} + jnlpregistry: "{{ .Values.agent.jnlpregistry }}" + {{- end }} + defaultsProviderTemplate: "{{ .Values.agent.defaultsProviderTemplate }}" + connectTimeout: "{{ .Values.agent.kubernetesConnectTimeout }}" + readTimeout: "{{ .Values.agent.kubernetesReadTimeout }}" + {{- if .Values.agent.directConnection }} + directConnection: true + {{- else }} + {{- if .Values.agent.jenkinsUrl }} + jenkinsUrl: "{{ tpl .Values.agent.jenkinsUrl . }}" + {{- else }} + jenkinsUrl: "http://{{ template "jenkins.fullname" . }}.{{ template "jenkins.namespace" . }}.svc.{{.Values.clusterZone}}:{{.Values.controller.servicePort}}{{ default "" .Values.controller.jenkinsUriPrefix }}" + {{- end }} + {{- if not .Values.agent.websocket }} + {{- if .Values.agent.jenkinsTunnel }} + jenkinsTunnel: "{{ tpl .Values.agent.jenkinsTunnel . }}" + {{- else }} + jenkinsTunnel: "{{ template "jenkins.fullname" . }}-agent.{{ template "jenkins.namespace" . }}.svc.{{.Values.clusterZone}}:{{ .Values.controller.agentListenerPort }}" + {{- end }} + {{- else }} + webSocket: true + {{- end }} + {{- end }} + skipTlsVerify: {{ .Values.agent.skipTlsVerify | default false}} + usageRestricted: {{ .Values.agent.usageRestricted | default false}} + maxRequestsPerHostStr: {{ .Values.agent.maxRequestsPerHostStr | quote }} + retentionTimeout: {{ .Values.agent.retentionTimeout | quote }} + waitForPodSec: {{ .Values.agent.waitForPodSec | quote }} + name: {{ $name | quote }} + namespace: "{{ template "jenkins.agent.namespace" . }}" + restrictedPssSecurityContext: {{ .Values.agent.restrictedPssSecurityContext }} + serverUrl: "{{ .Values.kubernetesURL }}" + credentialsId: "{{ .Values.credentialsId }}" + {{- if .Values.agent.enabled }} + podLabels: + - key: "jenkins/{{ .Release.Name }}-{{ .Values.agent.componentName }}" + value: "true" + {{- range $key, $val := .Values.agent.podLabels }} + - key: {{ $key | quote }} + value: {{ $val | quote }} + {{- end }} + templates: + {{- if not .Values.agent.disableDefaultAgent }} + {{- include "jenkins.casc.podTemplate" . | nindent 8 }} + {{- end }} + {{- if .Values.additionalAgents }} + {{- /* save .Values.agent */}} + {{- $agent := .Values.agent }} + {{- range $name, $additionalAgent := .Values.additionalAgents }} + {{- $additionalContainersEmpty := and (hasKey $additionalAgent "additionalContainers") (empty $additionalAgent.additionalContainers) }} + {{- /* merge original .Values.agent into additional agent to ensure it at least has the default values */}} + {{- $additionalAgent := merge $additionalAgent $agent }} + {{- /* clear list of additional containers in case it is configured empty for this agent (merge might have overwritten that) */}} + {{- if $additionalContainersEmpty }} + {{- $_ := set $additionalAgent "additionalContainers" list }} + {{- end }} + {{- /* set .Values.agent to $additionalAgent */}} + {{- $_ := set $.Values "agent" $additionalAgent }} + {{- include "jenkins.casc.podTemplate" $ | nindent 8 }} + {{- end }} + {{- /* restore .Values.agent */}} + {{- $_ := set .Values "agent" $agent }} + {{- end }} + {{- with .Values.agent.podTemplates }} + {{- range $key, $val := . }} + {{- tpl $val $ | nindent 8 }} + {{- end }} + {{- end }} + {{- end }} + {{- end }} + {{- end }} + {{- /* restore root */}} + {{- $_ := set $ "Values" $oldRoot.Values }} + {{- end }} + {{- if .Values.controller.csrf.defaultCrumbIssuer.enabled }} + crumbIssuer: + standard: + excludeClientIPFromCrumb: {{ if .Values.controller.csrf.defaultCrumbIssuer.proxyCompatability }}true{{ else }}false{{- end }} + {{- end }} +{{- include "jenkins.casc.security" . }} +{{- with .Values.controller.scriptApproval }} + scriptApproval: + approvedSignatures: + {{- range $key, $val := . }} + - "{{ $val }}" + {{- end }} +{{- end }} +unclassified: + location: + {{- with .Values.controller.jenkinsAdminEmail }} + adminAddress: {{ . }} + {{- end }} + url: {{ template "jenkins.url" . }} +{{- end -}} + +{{/* +Returns a name template to be used for jcasc configmaps, using +suffix passed in at call as index 0 +*/}} +{{- define "jenkins.casc.configName" -}} +{{- $name := index . 0 -}} +{{- $root := index . 1 -}} +"{{- include "jenkins.fullname" $root -}}-jenkins-{{ $name }}" +{{- end -}} + +{{/* +Returns kubernetes pod template configuration as code +*/}} +{{- define "jenkins.casc.podTemplate" -}} +- name: "{{ .Values.agent.podName }}" + namespace: "{{ template "jenkins.agent.namespace" . }}" +{{- if .Values.agent.annotations }} + annotations: + {{- range $key, $value := .Values.agent.annotations }} + - key: {{ $key }} + value: {{ $value | quote }} + {{- end }} +{{- end }} + id: {{ sha256sum (toYaml .Values.agent) }} + containers: + - name: "{{ .Values.agent.sideContainerName }}" + alwaysPullImage: {{ .Values.agent.alwaysPullImage }} + args: "{{ .Values.agent.args | replace "$" "^$" }}" + {{- with .Values.agent.command }} + command: {{ . }} + {{- end }} + envVars: + - envVar: + {{- if .Values.agent.directConnection }} + key: "JENKINS_DIRECT_CONNECTION" + {{- if .Values.agent.jenkinsTunnel }} + value: "{{ tpl .Values.agent.jenkinsTunnel . }}" + {{- else }} + value: "{{ template "jenkins.fullname" . }}-agent.{{ template "jenkins.namespace" . }}.svc.{{.Values.clusterZone}}:{{ .Values.controller.agentListenerPort }}" + {{- end }} + {{- else }} + key: "JENKINS_URL" + {{- if .Values.agent.jenkinsUrl }} + value: {{ tpl .Values.agent.jenkinsUrl . }} + {{- else }} + value: "http://{{ template "jenkins.fullname" . }}.{{ template "jenkins.namespace" . }}.svc.{{.Values.clusterZone}}:{{.Values.controller.servicePort}}{{ default "/" .Values.controller.jenkinsUriPrefix }}" + {{- end }} + {{- end }} + image: "{{ .Values.agent.image.repository }}:{{ .Values.agent.image.tag }}" + {{- if .Values.agent.livenessProbe }} + livenessProbe: + execArgs: {{.Values.agent.livenessProbe.execArgs | quote}} + failureThreshold: {{.Values.agent.livenessProbe.failureThreshold}} + initialDelaySeconds: {{.Values.agent.livenessProbe.initialDelaySeconds}} + periodSeconds: {{.Values.agent.livenessProbe.periodSeconds}} + successThreshold: {{.Values.agent.livenessProbe.successThreshold}} + timeoutSeconds: {{.Values.agent.livenessProbe.timeoutSeconds}} + {{- end }} + privileged: "{{- if .Values.agent.privileged }}true{{- else }}false{{- end }}" + resourceLimitCpu: {{.Values.agent.resources.limits.cpu}} + resourceLimitMemory: {{.Values.agent.resources.limits.memory}} + {{- with .Values.agent.resources.limits.ephemeralStorage }} + resourceLimitEphemeralStorage: {{.}} + {{- end }} + resourceRequestCpu: {{.Values.agent.resources.requests.cpu}} + resourceRequestMemory: {{.Values.agent.resources.requests.memory}} + {{- with .Values.agent.resources.requests.ephemeralStorage }} + resourceRequestEphemeralStorage: {{.}} + {{- end }} + {{- with .Values.agent.runAsUser }} + runAsUser: {{ . }} + {{- end }} + {{- with .Values.agent.runAsGroup }} + runAsGroup: {{ . }} + {{- end }} + ttyEnabled: {{ .Values.agent.TTYEnabled }} + workingDir: {{ .Values.agent.workingDir }} +{{- range $additionalContainers := .Values.agent.additionalContainers }} + - name: "{{ $additionalContainers.sideContainerName }}" + alwaysPullImage: {{ $additionalContainers.alwaysPullImage | default $.Values.agent.alwaysPullImage }} + args: "{{ $additionalContainers.args | replace "$" "^$" }}" + {{- with $additionalContainers.command }} + command: {{ . }} + {{- end }} + envVars: + - envVar: + key: "JENKINS_URL" + {{- if $additionalContainers.jenkinsUrl }} + value: {{ tpl ($additionalContainers.jenkinsUrl) . }} + {{- else }} + value: "http://{{ template "jenkins.fullname" $ }}.{{ template "jenkins.namespace" $ }}.svc.{{ $.Values.clusterZone }}:{{ $.Values.controller.servicePort }}{{ default "/" $.Values.controller.jenkinsUriPrefix }}" + {{- end }} + image: "{{ $additionalContainers.image.repository }}:{{ $additionalContainers.image.tag }}" + {{- if $additionalContainers.livenessProbe }} + livenessProbe: + execArgs: {{$additionalContainers.livenessProbe.execArgs | quote}} + failureThreshold: {{$additionalContainers.livenessProbe.failureThreshold}} + initialDelaySeconds: {{$additionalContainers.livenessProbe.initialDelaySeconds}} + periodSeconds: {{$additionalContainers.livenessProbe.periodSeconds}} + successThreshold: {{$additionalContainers.livenessProbe.successThreshold}} + timeoutSeconds: {{$additionalContainers.livenessProbe.timeoutSeconds}} + {{- end }} + privileged: "{{- if $additionalContainers.privileged }}true{{- else }}false{{- end }}" + resourceLimitCpu: {{ if $additionalContainers.resources }}{{ $additionalContainers.resources.limits.cpu }}{{ else }}{{ $.Values.agent.resources.limits.cpu }}{{ end }} + resourceLimitMemory: {{ if $additionalContainers.resources }}{{ $additionalContainers.resources.limits.memory }}{{ else }}{{ $.Values.agent.resources.limits.memory }}{{ end }} + resourceRequestCpu: {{ if $additionalContainers.resources }}{{ $additionalContainers.resources.requests.cpu }}{{ else }}{{ $.Values.agent.resources.requests.cpu }}{{ end }} + resourceRequestMemory: {{ if $additionalContainers.resources }}{{ $additionalContainers.resources.requests.memory }}{{ else }}{{ $.Values.agent.resources.requests.memory }}{{ end }} + {{- if or $additionalContainers.runAsUser $.Values.agent.runAsUser }} + runAsUser: {{ $additionalContainers.runAsUser | default $.Values.agent.runAsUser }} + {{- end }} + {{- if or $additionalContainers.runAsGroup $.Values.agent.runAsGroup }} + runAsGroup: {{ $additionalContainers.runAsGroup | default $.Values.agent.runAsGroup }} + {{- end }} + ttyEnabled: {{ $additionalContainers.TTYEnabled | default $.Values.agent.TTYEnabled }} + workingDir: {{ $additionalContainers.workingDir | default $.Values.agent.workingDir }} +{{- end }} +{{- if or .Values.agent.envVars .Values.agent.secretEnvVars }} + envVars: + {{- range $index, $var := .Values.agent.envVars }} + - envVar: + key: {{ $var.name }} + value: {{ tpl $var.value $ }} + {{- end }} + {{- range $index, $var := .Values.agent.secretEnvVars }} + - secretEnvVar: + key: {{ $var.key }} + secretName: {{ $var.secretName }} + secretKey: {{ $var.secretKey }} + optional: {{ $var.optional | default false }} + {{- end }} +{{- end }} + idleMinutes: {{ .Values.agent.idleMinutes }} + instanceCap: 2147483647 + {{- if .Values.agent.hostNetworking }} + hostNetwork: {{ .Values.agent.hostNetworking }} + {{- end }} + {{- if .Values.agent.imagePullSecretName }} + imagePullSecrets: + - name: {{ .Values.agent.imagePullSecretName }} + {{- end }} + label: "{{ .Release.Name }}-{{ .Values.agent.componentName }} {{ .Values.agent.customJenkinsLabels | join " " }}" +{{- if .Values.agent.nodeSelector }} + nodeSelector: + {{- $local := dict "first" true }} + {{- range $key, $value := .Values.agent.nodeSelector }} + {{- if $local.first }} {{ else }},{{ end }} + {{- $key }}={{ tpl $value $ }} + {{- $_ := set $local "first" false }} + {{- end }} +{{- end }} + nodeUsageMode: {{ quote .Values.agent.nodeUsageMode }} + podRetention: {{ .Values.agent.podRetention }} + showRawYaml: {{ .Values.agent.showRawYaml }} +{{- $asaname := default (include "jenkins.serviceAccountAgentName" .) .Values.agent.serviceAccount -}} +{{- if or (.Values.agent.useDefaultServiceAccount) (.Values.agent.serviceAccount) }} + serviceAccount: "{{ $asaname }}" +{{- end }} + slaveConnectTimeoutStr: "{{ .Values.agent.connectTimeout }}" +{{- if .Values.agent.volumes }} + volumes: + {{- range $index, $volume := .Values.agent.volumes }} + -{{- if (eq $volume.type "ConfigMap") }} configMapVolume: + {{- else if (eq $volume.type "EmptyDir") }} emptyDirVolume: + {{- else if (eq $volume.type "EphemeralVolume") }} genericEphemeralVolume: + {{- else if (eq $volume.type "HostPath") }} hostPathVolume: + {{- else if (eq $volume.type "Nfs") }} nfsVolume: + {{- else if (eq $volume.type "PVC") }} persistentVolumeClaim: + {{- else if (eq $volume.type "Secret") }} secretVolume: + {{- else }} {{ $volume.type }}: + {{- end }} + {{- range $key, $value := $volume }} + {{- if not (eq $key "type") }} + {{ $key }}: {{ if kindIs "string" $value }}{{ tpl $value $ | quote }}{{ else }}{{ $value }}{{ end }} + {{- end }} + {{- end }} + {{- end }} +{{- end }} +{{- if .Values.agent.workspaceVolume }} + workspaceVolume: + {{- if (eq .Values.agent.workspaceVolume.type "DynamicPVC") }} + dynamicPVC: + {{- else if (eq .Values.agent.workspaceVolume.type "EmptyDir") }} + emptyDirWorkspaceVolume: + {{- else if (eq .Values.agent.workspaceVolume.type "EphemeralVolume") }} + genericEphemeralVolume: + {{- else if (eq .Values.agent.workspaceVolume.type "HostPath") }} + hostPathWorkspaceVolume: + {{- else if (eq .Values.agent.workspaceVolume.type "Nfs") }} + nfsWorkspaceVolume: + {{- else if (eq .Values.agent.workspaceVolume.type "PVC") }} + persistentVolumeClaimWorkspaceVolume: + {{- else }} + {{ .Values.agent.workspaceVolume.type }}: + {{- end }} + {{- range $key, $value := .Values.agent.workspaceVolume }} + {{- if not (eq $key "type") }} + {{ $key }}: {{ if kindIs "string" $value }}{{ tpl $value $ | quote }}{{ else }}{{ $value }}{{ end }} + {{- end }} + {{- end }} +{{- end }} +{{- if .Values.agent.yamlTemplate }} + yaml: |- + {{- tpl (trim .Values.agent.yamlTemplate) . | nindent 4 }} +{{- end }} + yamlMergeStrategy: {{ .Values.agent.yamlMergeStrategy }} + inheritYamlMergeStrategy: {{ .Values.agent.inheritYamlMergeStrategy }} +{{- end -}} + +{{- define "jenkins.kubernetes-version" -}} + {{- if .Values.controller.installPlugins -}} + {{- range .Values.controller.installPlugins -}} + {{- if hasPrefix "kubernetes:" . }} + {{- $split := splitList ":" . }} + {{- printf "%s" (index $split 1 ) -}} + {{- end -}} + {{- end -}} + {{- end -}} +{{- end -}} + +{{- define "jenkins.casc.security" }} +security: +{{- with .Values.controller.JCasC }} +{{- if .security }} + {{- .security | toYaml | nindent 2 }} +{{- end }} +{{- end }} +{{- end -}} + +{{/* +Create the name of the service account to use +*/}} +{{- define "jenkins.serviceAccountName" -}} +{{- if .Values.serviceAccount.create -}} + {{ default (include "jenkins.fullname" .) .Values.serviceAccount.name }} +{{- else -}} + {{ default "default" .Values.serviceAccount.name }} +{{- end -}} +{{- end -}} + +{{/* +Create the name of the service account for Jenkins agents to use +*/}} +{{- define "jenkins.serviceAccountAgentName" -}} +{{- if .Values.serviceAccountAgent.create -}} + {{ default (printf "%s-%s" (include "jenkins.fullname" .) "agent") .Values.serviceAccountAgent.name }} +{{- else -}} + {{ default "default" .Values.serviceAccountAgent.name }} +{{- end -}} +{{- end -}} + +{{/* +Create a full tag name for controller image +*/}} +{{- define "controller.image.tag" -}} +{{- if .Values.controller.image.tagLabel -}} + {{- default (printf "%s-%s" .Chart.AppVersion .Values.controller.image.tagLabel) .Values.controller.image.tag -}} +{{- else -}} + {{- default .Chart.AppVersion .Values.controller.image.tag -}} +{{- end -}} +{{- end -}} + +{{/* +Create the HTTP port for interacting with the controller +*/}} +{{- define "controller.httpPort" -}} +{{- if .Values.controller.httpsKeyStore.enable -}} + {{- .Values.controller.httpsKeyStore.httpPort -}} +{{- else -}} + {{- .Values.controller.targetPort -}} +{{- end -}} +{{- end -}} + +{{- define "jenkins.configReloadContainer" -}} +{{- $root := index . 0 -}} +{{- $containerName := index . 1 -}} +{{- $containerType := index . 2 -}} +- name: {{ $containerName }} + image: "{{ $root.Values.controller.sidecars.configAutoReload.image.registry }}/{{ $root.Values.controller.sidecars.configAutoReload.image.repository }}:{{ $root.Values.controller.sidecars.configAutoReload.image.tag }}" + imagePullPolicy: {{ $root.Values.controller.sidecars.configAutoReload.imagePullPolicy }} + {{- if $root.Values.controller.sidecars.configAutoReload.containerSecurityContext }} + securityContext: {{- toYaml $root.Values.controller.sidecars.configAutoReload.containerSecurityContext | nindent 4 }} + {{- end }} + {{- if $root.Values.controller.sidecars.configAutoReload.envFrom }} + envFrom: +{{ (tpl (toYaml $root.Values.controller.sidecars.configAutoReload.envFrom) $root) | indent 4 }} + {{- end }} + env: + - name: POD_NAME + valueFrom: + fieldRef: + fieldPath: metadata.name + - name: LABEL + value: "{{ template "jenkins.fullname" $root }}-jenkins-config" + - name: FOLDER + value: "{{ $root.Values.controller.sidecars.configAutoReload.folder }}" + - name: NAMESPACE + value: '{{ $root.Values.controller.sidecars.configAutoReload.searchNamespace | default (include "jenkins.namespace" $root) }}' + {{- if eq $containerType "init" }} + - name: METHOD + value: "LIST" + {{- else if $root.Values.controller.sidecars.configAutoReload.sleepTime }} + - name: METHOD + value: "SLEEP" + - name: SLEEP_TIME + value: "{{ $root.Values.controller.sidecars.configAutoReload.sleepTime }}" + {{- end }} + {{- if eq $containerType "sidecar" }} + - name: REQ_URL + value: "{{- default "http" $root.Values.controller.sidecars.configAutoReload.scheme }}://localhost:{{- include "controller.httpPort" $root -}}{{- $root.Values.controller.jenkinsUriPrefix -}}/reload-configuration-as-code/?casc-reload-token=$(POD_NAME)" + - name: REQ_METHOD + value: "POST" + - name: REQ_RETRY_CONNECT + value: "{{ $root.Values.controller.sidecars.configAutoReload.reqRetryConnect }}" + {{- if $root.Values.controller.sidecars.configAutoReload.skipTlsVerify }} + - name: REQ_SKIP_TLS_VERIFY + value: "true" + {{- end }} + {{- end }} + + {{- if $root.Values.controller.sidecars.configAutoReload.env }} + {{- range $envVarItem := $root.Values.controller.sidecars.configAutoReload.env -}} + {{- if or (ne $containerType "init") (ne .name "METHOD") }} +{{- (tpl (toYaml (list $envVarItem)) $root) | nindent 4 }} + {{- end -}} + {{- end -}} + {{- end }} + {{- if $root.Values.controller.sidecars.configAutoReload.logging.configuration.override }} + - name: LOG_CONFIG + value: "{{ $root.Values.controller.jenkinsHome }}/auto-reload/auto-reload-config.yaml" + {{- end }} + + resources: +{{ toYaml $root.Values.controller.sidecars.configAutoReload.resources | indent 4 }} + volumeMounts: + - name: sc-config-volume + mountPath: {{ $root.Values.controller.sidecars.configAutoReload.folder | quote }} + - name: jenkins-home + mountPath: {{ $root.Values.controller.jenkinsHome }} + {{- if $root.Values.persistence.subPath }} + subPath: {{ $root.Values.persistence.subPath }} + {{- end }} + {{- if $root.Values.controller.sidecars.configAutoReload.logging.configuration.override }} + - name: auto-reload-config + mountPath: {{ $root.Values.controller.jenkinsHome }}/auto-reload + - name: auto-reload-config-logs + mountPath: {{ $root.Values.controller.jenkinsHome }}/auto-reload-logs + {{- end }} + {{- if $root.Values.controller.sidecars.configAutoReload.additionalVolumeMounts }} +{{ (tpl (toYaml $root.Values.controller.sidecars.configAutoReload.additionalVolumeMounts) $root) | indent 4 }} + {{- end }} + +{{- end -}} diff --git a/charts/jenkins/jenkins/5.6.2/templates/auto-reload-config.yaml b/charts/jenkins/jenkins/5.6.2/templates/auto-reload-config.yaml new file mode 100644 index 000000000..8c177d7f3 --- /dev/null +++ b/charts/jenkins/jenkins/5.6.2/templates/auto-reload-config.yaml @@ -0,0 +1,60 @@ +{{- if .Values.controller.sidecars.configAutoReload.logging.configuration.override }} +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ template "jenkins.fullname" . }}-auto-reload-config + namespace: {{ template "jenkins.namespace" . }} + labels: + "app.kubernetes.io/name": {{ template "jenkins.name" . }} + {{- if .Values.renderHelmLabels }} + "helm.sh/chart": "{{ .Chart.Name }}-{{ .Chart.Version }}" + {{- end }} + "app.kubernetes.io/managed-by": "{{ $.Release.Service }}" + "app.kubernetes.io/instance": "{{ $.Release.Name }}" + "app.kubernetes.io/component": "{{ $.Values.controller.componentName }}" +data: + auto-reload-config.yaml: |- + version: 1 + disable_existing_loggers: false + root: + level: {{ .Values.controller.sidecars.configAutoReload.logging.configuration.logLevel }} + handlers: + {{- if .Values.controller.sidecars.configAutoReload.logging.configuration.logToConsole}} + - console + {{- end }} + {{- if .Values.controller.sidecars.configAutoReload.logging.configuration.logToFile }} + - file + {{- end }} + handlers: + {{- if .Values.controller.sidecars.configAutoReload.logging.configuration.logToConsole}} + console: + class: logging.StreamHandler + level: {{ .Values.controller.sidecars.configAutoReload.logging.configuration.logLevel }} + formatter: {{ .Values.controller.sidecars.configAutoReload.logging.configuration.formatter }} + {{- end }} + {{- if .Values.controller.sidecars.configAutoReload.logging.configuration.logToFile }} + file: + class : logging.handlers.RotatingFileHandler + formatter: {{ .Values.controller.sidecars.configAutoReload.logging.configuration.formatter }} + filename: {{ .Values.controller.jenkinsHome }}/auto-reload-logs/file.log + maxBytes: {{ .Values.controller.sidecars.configAutoReload.logging.configuration.maxBytes }} + backupCount: {{ .Values.controller.sidecars.configAutoReload.logging.configuration.backupCount }} + {{- end }} + formatters: + JSON: + "()": logger.JsonFormatter + format: "%(levelname)s %(message)s" + rename_fields: + message: msg + levelname: level + LOGFMT: + "()": logger.LogfmtFormatter + keys: + - time + - level + - msg + mapping: + time: asctime + level: levelname + msg: message + {{- end }} \ No newline at end of file diff --git a/charts/jenkins/jenkins/5.6.2/templates/config-init-scripts.yaml b/charts/jenkins/jenkins/5.6.2/templates/config-init-scripts.yaml new file mode 100644 index 000000000..7dd253cc3 --- /dev/null +++ b/charts/jenkins/jenkins/5.6.2/templates/config-init-scripts.yaml @@ -0,0 +1,18 @@ +{{- if .Values.controller.initScripts -}} + +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ template "jenkins.fullname" . }}-init-scripts + namespace: {{ template "jenkins.namespace" . }} + labels: + "app.kubernetes.io/name": '{{ template "jenkins.name" .}}' + "app.kubernetes.io/managed-by": "{{ .Release.Service }}" + "app.kubernetes.io/instance": "{{ .Release.Name }}" + "app.kubernetes.io/component": "{{ .Values.controller.componentName }}" +data: +{{- range $key, $val := .Values.controller.initScripts }} + init{{ $key }}.groovy: |- +{{ tpl $val $ | indent 4 }} +{{- end }} +{{- end }} diff --git a/charts/jenkins/jenkins/5.6.2/templates/config.yaml b/charts/jenkins/jenkins/5.6.2/templates/config.yaml new file mode 100644 index 000000000..5de0b9f72 --- /dev/null +++ b/charts/jenkins/jenkins/5.6.2/templates/config.yaml @@ -0,0 +1,92 @@ +{{- $jenkinsHome := .Values.controller.jenkinsHome -}} + +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ template "jenkins.fullname" . }} + namespace: {{ template "jenkins.namespace" . }} + labels: + "app.kubernetes.io/name": '{{ template "jenkins.name" .}}' + "app.kubernetes.io/managed-by": "{{ .Release.Service }}" + "app.kubernetes.io/instance": "{{ .Release.Name }}" + "app.kubernetes.io/component": "{{ .Values.controller.componentName }}" +data: + apply_config.sh: |- + set -e +{{- if .Values.controller.initializeOnce }} + if [ -f {{ .Values.controller.jenkinsHome }}/initialization-completed ]; then + echo "controller was previously initialized, refusing to re-initialize" + exit 0 + fi +{{- end }} + echo "disable Setup Wizard" + # Prevent Setup Wizard when JCasC is enabled + echo $JENKINS_VERSION > {{ .Values.controller.jenkinsHome }}/jenkins.install.UpgradeWizard.state + echo $JENKINS_VERSION > {{ .Values.controller.jenkinsHome }}/jenkins.install.InstallUtil.lastExecVersion +{{- if .Values.controller.overwritePlugins }} + echo "remove all plugins from shared volume" + # remove all plugins from shared volume + rm -rf {{ .Values.controller.jenkinsHome }}/plugins/* +{{- end }} +{{- if .Values.controller.JCasC.overwriteConfiguration }} + echo "deleting all XML config files" + rm -f {{ .Values.controller.jenkinsHome }}/config.xml + rm -f {{ .Values.controller.jenkinsHome }}/*plugins*.xml + find {{ .Values.controller.jenkinsHome }} -maxdepth 1 -type f -iname '*configuration*.xml' -exec rm -f {} \; +{{- end }} +{{- if .Values.controller.installPlugins }} + echo "download plugins" + # Install missing plugins + cp /var/jenkins_config/plugins.txt {{ .Values.controller.jenkinsHome }}; + rm -rf {{ .Values.controller.jenkinsRef }}/plugins/*.lock + version () { echo "$@" | awk -F. '{ printf("%d%03d%03d%03d\n", $1,$2,$3,$4); }'; } + if [ -f "{{ .Values.controller.jenkinsWar }}" ] && [ -n "$(command -v jenkins-plugin-cli)" 2>/dev/null ] && [ $(version $(jenkins-plugin-cli --version)) -ge $(version "2.1.1") ]; then + jenkins-plugin-cli --verbose --war "{{ .Values.controller.jenkinsWar }}" --plugin-file "{{ .Values.controller.jenkinsHome }}/plugins.txt" --latest {{ .Values.controller.installLatestPlugins }}{{- if .Values.controller.installLatestSpecifiedPlugins }} --latest-specified{{- end }}; + else + /usr/local/bin/install-plugins.sh `echo $(cat {{ .Values.controller.jenkinsHome }}/plugins.txt)`; + fi + echo "copy plugins to shared volume" + # Copy plugins to shared volume + yes n | cp -i {{ .Values.controller.jenkinsRef }}/plugins/* /var/jenkins_plugins/; +{{- end }} + {{- if not .Values.controller.sidecars.configAutoReload.enabled }} + echo "copy configuration as code files" + mkdir -p {{ .Values.controller.jenkinsHome }}/casc_configs; + rm -rf {{ .Values.controller.jenkinsHome }}/casc_configs/* + {{- if or .Values.controller.JCasC.defaultConfig .Values.controller.JCasC.configScripts }} + cp -v /var/jenkins_config/*.yaml {{ .Values.controller.jenkinsHome }}/casc_configs + {{- end }} + {{- end }} + echo "finished initialization" +{{- if .Values.controller.initializeOnce }} + touch {{ .Values.controller.jenkinsHome }}/initialization-completed +{{- end }} + {{- if not .Values.controller.sidecars.configAutoReload.enabled }} +# Only add config to this script if we aren't auto-reloading otherwise the pod will restart upon each config change: +{{- if .Values.controller.JCasC.defaultConfig }} + jcasc-default-config.yaml: |- + {{- include "jenkins.casc.defaults" . |nindent 4}} +{{- end }} +{{- range $key, $val := .Values.controller.JCasC.configScripts }} + {{ $key }}.yaml: |- +{{ tpl $val $| indent 4 }} +{{- end }} +{{- end }} + plugins.txt: |- +{{- if .Values.controller.installPlugins }} + {{- range $installPlugin := .Values.controller.installPlugins }} + {{- $installPlugin | nindent 4 }} + {{- end }} + {{- range $addlPlugin := .Values.controller.additionalPlugins }} + {{- /* duplicate plugin check */}} + {{- range $installPlugin := $.Values.controller.installPlugins }} + {{- if eq (splitList ":" $addlPlugin | first) (splitList ":" $installPlugin | first) }} + {{- $message := print "[PLUGIN CONFLICT] controller.additionalPlugins contains '" $addlPlugin "'" }} + {{- $message := print $message " but controller.installPlugins already contains '" $installPlugin "'." }} + {{- $message := print $message " Override controller.installPlugins to use '" $addlPlugin "' plugin." }} + {{- fail $message }} + {{- end }} + {{- end }} + {{- $addlPlugin | nindent 4 }} + {{- end }} +{{- end }} diff --git a/charts/jenkins/jenkins/5.6.2/templates/deprecation.yaml b/charts/jenkins/jenkins/5.6.2/templates/deprecation.yaml new file mode 100644 index 000000000..f54017ce4 --- /dev/null +++ b/charts/jenkins/jenkins/5.6.2/templates/deprecation.yaml @@ -0,0 +1,151 @@ +{{- if .Values.checkDeprecation }} + {{- if .Values.master }} + {{ fail "`master` does no longer exist. It has been renamed to `controller`" }} + {{- end }} + + {{- if .Values.controller.imageTag }} + {{ fail "`controller.imageTag` does no longer exist. Please use `controller.image.tag` instead" }} + {{- end }} + + {{- if .Values.controller.slaveListenerPort }} + {{ fail "`controller.slaveListenerPort` does no longer exist. It has been renamed to `controller.agentListenerPort`" }} + {{- end }} + + {{- if .Values.controller.slaveHostPort }} + {{ fail "`controller.slaveHostPort` does no longer exist. It has been renamed to `controller.agentListenerHostPort`" }} + {{- end }} + + {{- if .Values.controller.slaveKubernetesNamespace }} + {{ fail "`controller.slaveKubernetesNamespace` does no longer exist. It has been renamed to `agent.namespace`" }} + {{- end }} + + {{- if .Values.controller.slaveDefaultsProviderTemplate }} + {{ fail "`controller.slaveDefaultsProviderTemplate` does no longer exist. It has been renamed to `agent.defaultsProviderTemplate`" }} + {{- end }} + + {{- if .Values.controller.useSecurity }} + {{ fail "`controller.useSecurity` does no longer exist. It has been renamed to `controller.adminSecret`" }} + {{- end }} + + {{- if .Values.controller.slaveJenkinsUrl }} + {{ fail "`controller.slaveJenkinsUrl` does no longer exist. It has been renamed to `agent.jenkinsUrl`" }} + {{- end }} + + {{- if .Values.controller.slaveJenkinsTunnel }} + {{ fail "`controller.slaveJenkinsTunnel` does no longer exist. It has been renamed to `agent.jenkinsTunnel`" }} + {{- end }} + + {{- if .Values.controller.slaveConnectTimeout }} + {{ fail "`controller.slaveConnectTimeout` does no longer exist. It has been renamed to `agent.kubernetesConnectTimeout`" }} + {{- end }} + + {{- if .Values.controller.slaveReadTimeout }} + {{ fail "`controller.slaveReadTimeout` does no longer exist. It has been renamed to `agent.kubernetesReadTimeout`" }} + {{- end }} + + {{- if .Values.controller.slaveListenerServiceType }} + {{ fail "`controller.slaveListenerServiceType` does no longer exist. It has been renamed to `controller.agentListenerServiceType`" }} + {{- end }} + + {{- if .Values.controller.slaveListenerLoadBalancerIP }} + {{ fail "`controller.slaveListenerLoadBalancerIP` does no longer exist. It has been renamed to `controller.agentListenerLoadBalancerIP`" }} + {{- end }} + + {{- if .Values.controller.slaveListenerServiceAnnotations }} + {{ fail "`controller.slaveListenerServiceAnnotations` does no longer exist. It has been renamed to `controller.agentListenerServiceAnnotations`" }} + {{- end }} + + {{- if .Values.agent.slaveConnectTimeout }} + {{ fail "`agent.slaveConnectTimeout` does no longer exist. It has been renamed to `agent.connectTimeout`" }} + {{- end }} + + {{- if .Values.NetworkPolicy }} + + {{- if .Values.NetworkPolicy.Enabled }} + {{ fail "`NetworkPolicy.Enabled` does no longer exist. It has been renamed to `networkPolicy.enabled`" }} + {{- end }} + + {{- if .Values.NetworkPolicy.ApiVersion }} + {{ fail "`NetworkPolicy.ApiVersion` does no longer exist. It has been renamed to `networkPolicy.apiVersion`" }} + {{- end }} + + {{ fail "NetworkPolicy.* values have been renamed, please check the documentation" }} + {{- end }} + + + {{- if .Values.rbac.install }} + {{ fail "`rbac.install` does no longer exist. It has been renamed to `rbac.create` and is enabled by default!" }} + {{- end }} + + {{- if .Values.rbac.serviceAccountName }} + {{ fail "`rbac.serviceAccountName` does no longer exist. It has been renamed to `serviceAccount.name`" }} + {{- end }} + + {{- if .Values.rbac.serviceAccountAnnotations }} + {{ fail "`rbac.serviceAccountAnnotations` does no longer exist. It has been renamed to `serviceAccount.annotations`" }} + {{- end }} + + {{- if .Values.rbac.roleRef }} + {{ fail "`rbac.roleRef` does no longer exist. RBAC roles are now generated, please check the documentation" }} + {{- end }} + + {{- if .Values.rbac.roleKind }} + {{ fail "`rbac.roleKind` does no longer exist. RBAC roles are now generated, please check the documentation" }} + {{- end }} + + {{- if .Values.rbac.roleBindingKind }} + {{ fail "`rbac.roleBindingKind` does no longer exist. RBAC roles are now generated, please check the documentation" }} + {{- end }} + + {{- if .Values.controller.JCasC.pluginVersion }} + {{ fail "controller.JCasC.pluginVersion has been deprecated, please use controller.installPlugins instead" }} + {{- end }} + + {{- if .Values.controller.deploymentLabels }} + {{ fail "`controller.deploymentLabels` does no longer exist. It has been renamed to `controller.statefulSetLabels`" }} + {{- end }} + + {{- if .Values.controller.deploymentAnnotations }} + {{ fail "`controller.deploymentAnnotations` does no longer exist. It has been renamed to `controller.statefulSetAnnotations`" }} + {{- end }} + + {{- if .Values.controller.rollingUpdate }} + {{ fail "`controller.rollingUpdate` does no longer exist. It is no longer relevant, since a StatefulSet is used for the Jenkins controller" }} + {{- end }} + + {{- if .Values.controller.tag }} + {{ fail "`controller.tag` no longer exists. It has been renamed to `controller.image.tag'" }} + {{- end }} + + {{- if .Values.controller.tagLabel }} + {{ fail "`controller.tagLabel` no longer exists. It has been renamed to `controller.image.tagLabel`" }} + {{- end }} + + {{- if .Values.controller.adminSecret }} + {{ fail "`controller.adminSecret` no longer exists. It has been renamed to `controller.admin.createSecret`" }} + {{- end }} + + {{- if .Values.controller.adminUser }} + {{ fail "`controller.adminUser` no longer exists. It has been renamed to `controller.admin.username`" }} + {{- end }} + + {{- if .Values.controller.adminPassword }} + {{ fail "`controller.adminPassword` no longer exists. It has been renamed to `controller.admin.password`" }} + {{- end }} + + {{- if .Values.controller.sidecars.other }} + {{ fail "`controller.sidecars.other` no longer exists. It has been renamed to `controller.sidecars.additionalSidecarContainers`" }} + {{- end }} + + {{- if .Values.agent.tag }} + {{ fail "`controller.agent.tag` no longer exists. It has been renamed to `controller.agent.image.tag`" }} + {{- end }} + + {{- if .Values.backup }} + {{ fail "`controller.backup` no longer exists." }} + {{- end }} + + {{- if .Values.helmtest.bats.tag }} + {{ fail "`helmtest.bats.tag` no longer exists. It has been renamed to `helmtest.bats.image.tag`" }} + {{- end }} +{{- end }} diff --git a/charts/jenkins/jenkins/5.6.2/templates/home-pvc.yaml b/charts/jenkins/jenkins/5.6.2/templates/home-pvc.yaml new file mode 100644 index 000000000..f417d23ad --- /dev/null +++ b/charts/jenkins/jenkins/5.6.2/templates/home-pvc.yaml @@ -0,0 +1,41 @@ +{{- if not (contains "jenkins-home" (quote .Values.persistence.volumes)) }} +{{- if and .Values.persistence.enabled (not .Values.persistence.existingClaim) -}} +kind: PersistentVolumeClaim +apiVersion: v1 +metadata: +{{- if .Values.persistence.annotations }} + annotations: +{{ toYaml .Values.persistence.annotations | indent 4 }} +{{- end }} + name: {{ template "jenkins.fullname" . }} + namespace: {{ template "jenkins.namespace" . }} + labels: + "app.kubernetes.io/name": '{{ template "jenkins.name" .}}' + {{- if .Values.renderHelmLabels }} + "helm.sh/chart": "{{ template "jenkins.label" .}}" + {{- end }} + "app.kubernetes.io/managed-by": "{{ .Release.Service }}" + "app.kubernetes.io/instance": "{{ .Release.Name }}" + "app.kubernetes.io/component": "{{ .Values.controller.componentName }}" +{{- if .Values.persistence.labels }} +{{ toYaml .Values.persistence.labels | indent 4 }} +{{- end }} +spec: +{{- if .Values.persistence.dataSource }} + dataSource: +{{ toYaml .Values.persistence.dataSource | indent 4 }} +{{- end }} + accessModes: + - {{ .Values.persistence.accessMode | quote }} + resources: + requests: + storage: {{ .Values.persistence.size | quote }} +{{- if .Values.persistence.storageClass }} +{{- if (eq "-" .Values.persistence.storageClass) }} + storageClassName: "" +{{- else }} + storageClassName: "{{ .Values.persistence.storageClass }}" +{{- end }} +{{- end }} +{{- end }} +{{- end }} diff --git a/charts/jenkins/jenkins/5.6.2/templates/jcasc-config.yaml b/charts/jenkins/jenkins/5.6.2/templates/jcasc-config.yaml new file mode 100644 index 000000000..f51444525 --- /dev/null +++ b/charts/jenkins/jenkins/5.6.2/templates/jcasc-config.yaml @@ -0,0 +1,53 @@ +{{- $root := . }} +{{- if .Values.controller.sidecars.configAutoReload.enabled }} +{{- range $key, $val := .Values.controller.JCasC.configScripts }} +{{- if $val }} +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ template "jenkins.casc.configName" (list (printf "config-%s" $key) $ )}} + namespace: {{ template "jenkins.namespace" $root }} + labels: + "app.kubernetes.io/name": {{ template "jenkins.name" $root}} + {{- if $root.Values.renderHelmLabels }} + "helm.sh/chart": "{{ $root.Chart.Name }}-{{ $root.Chart.Version }}" + {{- end }} + "app.kubernetes.io/managed-by": "{{ $.Release.Service }}" + "app.kubernetes.io/instance": "{{ $.Release.Name }}" + "app.kubernetes.io/component": "{{ $.Values.controller.componentName }}" + {{ template "jenkins.fullname" $root }}-jenkins-config: "true" +{{- if $root.Values.controller.JCasC.configMapAnnotations }} + annotations: +{{ toYaml $root.Values.controller.JCasC.configMapAnnotations | indent 4 }} +{{- end }} +data: + {{ $key }}.yaml: |- +{{ tpl $val $| indent 4 }} +{{- end }} +{{- end }} +{{- if .Values.controller.JCasC.defaultConfig }} +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ template "jenkins.casc.configName" (list "jcasc-config" $ )}} + namespace: {{ template "jenkins.namespace" $root }} + labels: + "app.kubernetes.io/name": {{ template "jenkins.name" $root}} + {{- if .Values.renderHelmLabels }} + "helm.sh/chart": "{{ $root.Chart.Name }}-{{ $root.Chart.Version }}" + {{- end }} + "app.kubernetes.io/managed-by": "{{ $.Release.Service }}" + "app.kubernetes.io/instance": "{{ $.Release.Name }}" + "app.kubernetes.io/component": "{{ $.Values.controller.componentName }}" + {{ template "jenkins.fullname" $root }}-jenkins-config: "true" +{{- if $root.Values.controller.JCasC.configMapAnnotations }} + annotations: +{{ toYaml $root.Values.controller.JCasC.configMapAnnotations | indent 4 }} +{{- end }} +data: + jcasc-default-config.yaml: |- + {{- include "jenkins.casc.defaults" . | nindent 4 }} +{{- end}} +{{- end }} diff --git a/charts/jenkins/jenkins/5.6.2/templates/jenkins-agent-svc.yaml b/charts/jenkins/jenkins/5.6.2/templates/jenkins-agent-svc.yaml new file mode 100644 index 000000000..4440b91f8 --- /dev/null +++ b/charts/jenkins/jenkins/5.6.2/templates/jenkins-agent-svc.yaml @@ -0,0 +1,43 @@ +{{- if .Values.controller.agentListenerEnabled }} +apiVersion: v1 +kind: Service +metadata: + name: {{ template "jenkins.fullname" . }}-agent + namespace: {{ template "jenkins.namespace" . }} + labels: + "app.kubernetes.io/name": '{{ template "jenkins.name" .}}' + {{- if .Values.renderHelmLabels }} + "helm.sh/chart": "{{ template "jenkins.label" .}}" + {{- end }} + "app.kubernetes.io/managed-by": "{{ .Release.Service }}" + "app.kubernetes.io/instance": "{{ .Release.Name }}" + "app.kubernetes.io/component": "{{ .Values.controller.componentName }}" + {{- if .Values.controller.agentListenerServiceAnnotations }} + annotations: + {{- toYaml .Values.controller.agentListenerServiceAnnotations | nindent 4 }} + {{- end }} +spec: + {{- if .Values.controller.agentListenerExternalTrafficPolicy }} + externalTrafficPolicy: {{.Values.controller.agentListenerExternalTrafficPolicy}} + {{- end }} + ports: + - port: {{ .Values.controller.agentListenerPort }} + targetPort: {{ .Values.controller.agentListenerPort }} + {{- if (and (eq .Values.controller.agentListenerServiceType "NodePort") (not (empty .Values.controller.agentListenerNodePort))) }} + nodePort: {{ .Values.controller.agentListenerNodePort }} + {{- end }} + name: agent-listener + selector: + "app.kubernetes.io/component": "{{ .Values.controller.componentName }}" + "app.kubernetes.io/instance": "{{ .Release.Name }}" + type: {{ .Values.controller.agentListenerServiceType }} + {{if eq .Values.controller.agentListenerServiceType "LoadBalancer"}} +{{- if .Values.controller.agentListenerLoadBalancerSourceRanges }} + loadBalancerSourceRanges: +{{ toYaml .Values.controller.agentListenerLoadBalancerSourceRanges | indent 4 }} +{{- end }} + {{- end }} + {{- if and (eq .Values.controller.agentListenerServiceType "LoadBalancer") (.Values.controller.agentListenerLoadBalancerIP) }} + loadBalancerIP: {{ .Values.controller.agentListenerLoadBalancerIP }} + {{- end }} + {{- end }} diff --git a/charts/jenkins/jenkins/5.6.2/templates/jenkins-aws-security-group-policies.yaml b/charts/jenkins/jenkins/5.6.2/templates/jenkins-aws-security-group-policies.yaml new file mode 100644 index 000000000..2f6e7a13d --- /dev/null +++ b/charts/jenkins/jenkins/5.6.2/templates/jenkins-aws-security-group-policies.yaml @@ -0,0 +1,16 @@ +{{- if .Values.awsSecurityGroupPolicies.enabled -}} +{{- range .Values.awsSecurityGroupPolicies.policies -}} +apiVersion: vpcresources.k8s.aws/v1beta1 +kind: SecurityGroupPolicy +metadata: + name: {{ .name }} + namespace: {{ template "jenkins.namespace" $ }} +spec: + podSelector: + {{- toYaml .podSelector | nindent 6}} + securityGroups: + groupIds: + {{- toYaml .securityGroupIds | nindent 6}} +--- +{{- end -}} +{{- end -}} diff --git a/charts/jenkins/jenkins/5.6.2/templates/jenkins-controller-alerting-rules.yaml b/charts/jenkins/jenkins/5.6.2/templates/jenkins-controller-alerting-rules.yaml new file mode 100644 index 000000000..3fd806172 --- /dev/null +++ b/charts/jenkins/jenkins/5.6.2/templates/jenkins-controller-alerting-rules.yaml @@ -0,0 +1,26 @@ +{{- if and .Values.controller.prometheus.enabled .Values.controller.prometheus.alertingrules }} +--- +apiVersion: monitoring.coreos.com/v1 +kind: PrometheusRule +metadata: + name: {{ template "jenkins.fullname" . }} +{{- if .Values.controller.prometheus.prometheusRuleNamespace }} + namespace: {{ .Values.controller.prometheus.prometheusRuleNamespace }} +{{- else }} + namespace: {{ template "jenkins.namespace" . }} +{{- end }} + labels: + "app.kubernetes.io/name": '{{ template "jenkins.name" .}}' + {{- if .Values.renderHelmLabels }} + "helm.sh/chart": "{{ template "jenkins.label" .}}" + {{- end }} + "app.kubernetes.io/managed-by": "{{ .Release.Service }}" + "app.kubernetes.io/instance": "{{ .Release.Name }}" + "app.kubernetes.io/component": "{{ .Values.controller.componentName }}" + {{- range $key, $val := .Values.controller.prometheus.alertingRulesAdditionalLabels }} + {{ $key }}: {{ $val | quote }} + {{- end}} +spec: + groups: +{{ toYaml .Values.controller.prometheus.alertingrules | indent 2 }} +{{- end }} diff --git a/charts/jenkins/jenkins/5.6.2/templates/jenkins-controller-backendconfig.yaml b/charts/jenkins/jenkins/5.6.2/templates/jenkins-controller-backendconfig.yaml new file mode 100644 index 000000000..0e8a566fc --- /dev/null +++ b/charts/jenkins/jenkins/5.6.2/templates/jenkins-controller-backendconfig.yaml @@ -0,0 +1,24 @@ +{{- if .Values.controller.backendconfig.enabled }} +apiVersion: {{ .Values.controller.backendconfig.apiVersion }} +kind: BackendConfig +metadata: + name: {{ .Values.controller.backendconfig.name }} + namespace: {{ template "jenkins.namespace" . }} + labels: + "app.kubernetes.io/name": '{{ template "jenkins.name" .}}' + {{- if .Values.renderHelmLabels }} + "helm.sh/chart": "{{ template "jenkins.label" .}}" + {{- end }} + "app.kubernetes.io/managed-by": "{{ .Release.Service }}" + "app.kubernetes.io/instance": "{{ .Release.Name }}" + "app.kubernetes.io/component": "{{ .Values.controller.componentName }}" +{{- if .Values.controller.backendconfig.labels }} +{{ toYaml .Values.controller.backendconfig.labels | indent 4 }} +{{- end }} +{{- if .Values.controller.backendconfig.annotations }} + annotations: +{{ toYaml .Values.controller.backendconfig.annotations | indent 4 }} +{{- end }} +spec: +{{ toYaml .Values.controller.backendconfig.spec | indent 2 }} +{{- end }} diff --git a/charts/jenkins/jenkins/5.6.2/templates/jenkins-controller-ingress.yaml b/charts/jenkins/jenkins/5.6.2/templates/jenkins-controller-ingress.yaml new file mode 100644 index 000000000..b3b344ff8 --- /dev/null +++ b/charts/jenkins/jenkins/5.6.2/templates/jenkins-controller-ingress.yaml @@ -0,0 +1,77 @@ +{{- $kubeTargetVersion := default .Capabilities.KubeVersion.GitVersion .Values.kubeTargetVersionOverride }} +{{- if .Values.controller.ingress.enabled }} +{{- if semverCompare ">=1.19-0" $kubeTargetVersion -}} +apiVersion: networking.k8s.io/v1 +{{- else if semverCompare ">=1.14-0" $kubeTargetVersion -}} +apiVersion: networking.k8s.io/v1beta1 +{{- else -}} +apiVersion: {{ .Values.controller.ingress.apiVersion }} +{{- end }} +kind: Ingress +metadata: + namespace: {{ template "jenkins.namespace" . }} + labels: + "app.kubernetes.io/name": '{{ template "jenkins.name" .}}' + {{- if .Values.renderHelmLabels }} + "helm.sh/chart": "{{ template "jenkins.label" .}}" + {{- end }} + "app.kubernetes.io/managed-by": "{{ .Release.Service }}" + "app.kubernetes.io/instance": "{{ .Release.Name }}" + "app.kubernetes.io/component": "{{ .Values.controller.componentName }}" +{{- if .Values.controller.ingress.labels }} +{{ toYaml .Values.controller.ingress.labels | indent 4 }} +{{- end }} +{{- if .Values.controller.ingress.annotations }} + annotations: +{{ toYaml .Values.controller.ingress.annotations | indent 4 }} +{{- end }} + name: {{ template "jenkins.fullname" . }} +spec: +{{- if .Values.controller.ingress.ingressClassName }} + ingressClassName: {{ .Values.controller.ingress.ingressClassName | quote }} +{{- end }} + rules: + - http: + paths: +{{- if empty (.Values.controller.ingress.paths) }} + - backend: +{{- if semverCompare ">=1.19-0" $kubeTargetVersion }} + service: + name: {{ template "jenkins.fullname" . }} + port: + number: {{ .Values.controller.servicePort }} + pathType: ImplementationSpecific +{{- else }} + serviceName: {{ template "jenkins.fullname" . }} + servicePort: {{ .Values.controller.servicePort }} +{{- end }} +{{- if .Values.controller.ingress.path }} + path: {{ .Values.controller.ingress.path }} +{{- end -}} +{{- else }} +{{ tpl (toYaml .Values.controller.ingress.paths | indent 6) . }} +{{- end -}} +{{- if .Values.controller.ingress.hostName }} + host: {{ tpl .Values.controller.ingress.hostName . | quote }} +{{- end }} +{{- if .Values.controller.ingress.resourceRootUrl }} + - http: + paths: + - backend: +{{- if semverCompare ">=1.19-0" $kubeTargetVersion }} + service: + name: {{ template "jenkins.fullname" . }} + port: + number: {{ .Values.controller.servicePort }} + pathType: ImplementationSpecific +{{- else }} + serviceName: {{ template "jenkins.fullname" . }} + servicePort: {{ .Values.controller.servicePort }} +{{- end }} + host: {{ tpl .Values.controller.ingress.resourceRootUrl . | quote }} +{{- end }} +{{- if .Values.controller.ingress.tls }} + tls: +{{ tpl (toYaml .Values.controller.ingress.tls ) . | indent 4 }} +{{- end -}} +{{- end }} diff --git a/charts/jenkins/jenkins/5.6.2/templates/jenkins-controller-networkpolicy.yaml b/charts/jenkins/jenkins/5.6.2/templates/jenkins-controller-networkpolicy.yaml new file mode 100644 index 000000000..82835f2bd --- /dev/null +++ b/charts/jenkins/jenkins/5.6.2/templates/jenkins-controller-networkpolicy.yaml @@ -0,0 +1,76 @@ +{{- if .Values.networkPolicy.enabled }} +kind: NetworkPolicy +apiVersion: {{ .Values.networkPolicy.apiVersion }} +metadata: + name: "{{ .Release.Name }}-{{ .Values.controller.componentName }}" + namespace: {{ template "jenkins.namespace" . }} + labels: + "app.kubernetes.io/name": '{{ template "jenkins.name" .}}' + {{- if .Values.renderHelmLabels }} + "helm.sh/chart": "{{ template "jenkins.label" .}}" + {{- end }} + "app.kubernetes.io/managed-by": "{{ .Release.Service }}" + "app.kubernetes.io/instance": "{{ .Release.Name }}" + "app.kubernetes.io/component": "{{ .Values.controller.componentName }}" +spec: + podSelector: + matchLabels: + "app.kubernetes.io/component": "{{ .Values.controller.componentName }}" + "app.kubernetes.io/instance": "{{ .Release.Name }}" + ingress: + # Allow web access to the UI + - ports: + - port: {{ .Values.controller.targetPort }} + {{- if .Values.controller.agentListenerEnabled }} + # Allow inbound connections from agents + - from: + {{- if .Values.networkPolicy.internalAgents.allowed }} + - podSelector: + matchLabels: + "jenkins/{{ .Release.Name }}-{{ .Values.agent.componentName }}": "true" + {{- range $k,$v:= .Values.networkPolicy.internalAgents.podLabels }} + {{ $k }}: {{ $v }} + {{- end }} + {{- if .Values.networkPolicy.internalAgents.namespaceLabels }} + namespaceSelector: + matchLabels: + {{- range $k,$v:= .Values.networkPolicy.internalAgents.namespaceLabels }} + {{ $k }}: {{ $v }} + {{- end }} + {{- end }} + {{- end }} + {{- if or .Values.networkPolicy.externalAgents.ipCIDR .Values.networkPolicy.externalAgents.except }} + - ipBlock: + cidr: {{ required "ipCIDR is required if you wish to allow external agents to connect to Jenkins Controller." .Values.networkPolicy.externalAgents.ipCIDR }} + {{- if .Values.networkPolicy.externalAgents.except }} + except: + {{- range .Values.networkPolicy.externalAgents.except }} + - {{ . }} + {{- end }} + {{- end }} + {{- end }} + ports: + - port: {{ .Values.controller.agentListenerPort }} + {{- end }} +{{- if .Values.agent.enabled }} +--- +kind: NetworkPolicy +apiVersion: {{ .Values.networkPolicy.apiVersion }} +metadata: + name: "{{ .Release.Name }}-{{ .Values.agent.componentName }}" + namespace: {{ template "jenkins.namespace" . }} + labels: + "app.kubernetes.io/name": '{{ template "jenkins.name" .}}' + {{- if .Values.renderHelmLabels }} + "helm.sh/chart": "{{ template "jenkins.label" .}}" + {{- end }} + "app.kubernetes.io/managed-by": "{{ .Release.Service }}" + "app.kubernetes.io/instance": "{{ .Release.Name }}" + "app.kubernetes.io/component": "{{ .Values.controller.componentName }}" +spec: + podSelector: + matchLabels: + # DefaultDeny + "jenkins/{{ .Release.Name }}-{{ .Values.agent.componentName }}": "true" +{{- end }} +{{- end }} diff --git a/charts/jenkins/jenkins/5.6.2/templates/jenkins-controller-pdb.yaml b/charts/jenkins/jenkins/5.6.2/templates/jenkins-controller-pdb.yaml new file mode 100644 index 000000000..9dc1fafe2 --- /dev/null +++ b/charts/jenkins/jenkins/5.6.2/templates/jenkins-controller-pdb.yaml @@ -0,0 +1,34 @@ +{{- if .Values.controller.podDisruptionBudget.enabled }} +{{- $kubeTargetVersion := default .Capabilities.KubeVersion.GitVersion .Values.kubeTargetVersionOverride }} +{{- if semverCompare ">=1.21-0" $kubeTargetVersion -}} +apiVersion: policy/v1 +{{- else if semverCompare ">=1.5-0" $kubeTargetVersion -}} +apiVersion: policy/v1beta1 +{{- else -}} +apiVersion: {{ .Values.controller.podDisruptionBudget.apiVersion }} +{{- end }} +kind: PodDisruptionBudget +metadata: + name: {{ template "jenkins.fullname" . }}-pdb + namespace: {{ template "jenkins.namespace" . }} + labels: + "app.kubernetes.io/name": '{{ template "jenkins.name" .}}' + {{- if .Values.renderHelmLabels }} + "helm.sh/chart": "{{ template "jenkins.label" .}}" + {{- end }} + "app.kubernetes.io/managed-by": "{{ .Release.Service }}" + "app.kubernetes.io/instance": "{{ .Release.Name }}" + "app.kubernetes.io/component": "{{ .Values.controller.componentName }}" + {{- if .Values.controller.podDisruptionBudget.labels -}} + {{ toYaml .Values.controller.podDisruptionBudget.labels | nindent 4 }} + {{- end }} + {{- if .Values.controller.podDisruptionBudget.annotations }} + annotations: {{ toYaml .Values.controller.podDisruptionBudget.annotations | nindent 4 }} + {{- end }} +spec: + maxUnavailable: {{ .Values.controller.podDisruptionBudget.maxUnavailable }} + selector: + matchLabels: + "app.kubernetes.io/instance": "{{ .Release.Name }}" + "app.kubernetes.io/name": '{{ template "jenkins.name" .}}' +{{- end }} diff --git a/charts/jenkins/jenkins/5.6.2/templates/jenkins-controller-podmonitor.yaml b/charts/jenkins/jenkins/5.6.2/templates/jenkins-controller-podmonitor.yaml new file mode 100644 index 000000000..9a04019c3 --- /dev/null +++ b/charts/jenkins/jenkins/5.6.2/templates/jenkins-controller-podmonitor.yaml @@ -0,0 +1,30 @@ +{{- if .Values.controller.googlePodMonitor.enabled }} +apiVersion: monitoring.googleapis.com/v1 +kind: PodMonitoring + +metadata: + name: {{ template "jenkins.fullname" . }} +{{- if .Values.controller.googlePodMonitor.serviceMonitorNamespace }} + namespace: {{ .Values.controller.googlePodMonitor.serviceMonitorNamespace }} +{{- else }} + namespace: {{ template "jenkins.namespace" . }} +{{- end }} + labels: + "app.kubernetes.io/name": '{{ template "jenkins.name" .}}' + {{- if .Values.renderHelmLabels }} + "helm.sh/chart": "{{ template "jenkins.label" .}}" + {{- end }} + "app.kubernetes.io/managed-by": "{{ .Release.Service }}" + "app.kubernetes.io/instance": "{{ .Release.Name }}" + "app.kubernetes.io/component": "{{ .Values.controller.componentName }}" + +spec: + endpoints: + - interval: {{ .Values.controller.googlePodMonitor.scrapeInterval }} + port: http + path: {{ .Values.controller.jenkinsUriPrefix }}{{ .Values.controller.googlePodMonitor.scrapeEndpoint }} + selector: + matchLabels: + "app.kubernetes.io/instance": "{{ .Release.Name }}" + "app.kubernetes.io/component": "{{ .Values.controller.componentName }}" +{{- end }} diff --git a/charts/jenkins/jenkins/5.6.2/templates/jenkins-controller-route.yaml b/charts/jenkins/jenkins/5.6.2/templates/jenkins-controller-route.yaml new file mode 100644 index 000000000..3550380ee --- /dev/null +++ b/charts/jenkins/jenkins/5.6.2/templates/jenkins-controller-route.yaml @@ -0,0 +1,34 @@ +{{- if .Values.controller.route.enabled }} +apiVersion: route.openshift.io/v1 +kind: Route +metadata: + namespace: {{ template "jenkins.namespace" . }} + labels: + app: {{ template "jenkins.fullname" . }} + {{- if .Values.renderHelmLabels }} + "helm.sh/chart": "{{ template "jenkins.label" .}}" + {{- end }} + release: "{{ .Release.Name }}" + heritage: "{{ .Release.Service }}" + component: "{{ .Release.Name }}-{{ .Values.controller.componentName }}" +{{- if .Values.controller.route.labels }} +{{ toYaml .Values.controller.route.labels | indent 4 }} +{{- end }} +{{- if .Values.controller.route.annotations }} + annotations: +{{ toYaml .Values.controller.route.annotations | indent 4 }} +{{- end }} + name: {{ template "jenkins.fullname" . }} +spec: + host: {{ .Values.controller.route.path }} + port: + targetPort: http + tls: + insecureEdgeTerminationPolicy: Redirect + termination: edge + to: + kind: Service + name: {{ template "jenkins.fullname" . }} + weight: 100 + wildcardPolicy: None +{{- end }} diff --git a/charts/jenkins/jenkins/5.6.2/templates/jenkins-controller-secondary-ingress.yaml b/charts/jenkins/jenkins/5.6.2/templates/jenkins-controller-secondary-ingress.yaml new file mode 100644 index 000000000..c63e48229 --- /dev/null +++ b/charts/jenkins/jenkins/5.6.2/templates/jenkins-controller-secondary-ingress.yaml @@ -0,0 +1,56 @@ +{{- if .Values.controller.secondaryingress.enabled }} +{{- $kubeTargetVersion := default .Capabilities.KubeVersion.GitVersion .Values.kubeTargetVersionOverride }} +{{- $serviceName := include "jenkins.fullname" . -}} +{{- $servicePort := .Values.controller.servicePort -}} +{{- if semverCompare ">=1.19-0" $kubeTargetVersion -}} +apiVersion: networking.k8s.io/v1 +{{- else if semverCompare ">=1.14-0" $kubeTargetVersion -}} +apiVersion: networking.k8s.io/v1beta1 +{{- else -}} +apiVersion: {{ .Values.controller.secondaryingress.apiVersion }} +{{- end }} +kind: Ingress +metadata: + namespace: {{ template "jenkins.namespace" . }} + labels: + "app.kubernetes.io/name": '{{ template "jenkins.name" .}}' + {{- if .Values.renderHelmLabels }} + "helm.sh/chart": "{{ template "jenkins.label" .}}" + {{- end }} + "app.kubernetes.io/managed-by": "{{ .Release.Service }}" + "app.kubernetes.io/instance": "{{ .Release.Name }}" + "app.kubernetes.io/component": "{{ .Values.controller.componentName }}" + {{- if .Values.controller.secondaryingress.labels -}} + {{ toYaml .Values.controller.secondaryingress.labels | nindent 4 }} + {{- end }} + {{- if .Values.controller.secondaryingress.annotations }} + annotations: {{ toYaml .Values.controller.secondaryingress.annotations | nindent 4 }} + {{- end }} + name: {{ template "jenkins.fullname" . }}-secondary +spec: +{{- if .Values.controller.secondaryingress.ingressClassName }} + ingressClassName: {{ .Values.controller.secondaryingress.ingressClassName | quote }} +{{- end }} + rules: + - host: {{ .Values.controller.secondaryingress.hostName }} + http: + paths: + {{- range .Values.controller.secondaryingress.paths }} + - path: {{ . | quote }} + backend: +{{ if semverCompare ">=1.19-0" $kubeTargetVersion }} + service: + name: {{ $serviceName }} + port: + number: {{ $servicePort }} + pathType: ImplementationSpecific +{{ else }} + serviceName: {{ $serviceName }} + servicePort: {{ $servicePort }} +{{ end }} + {{- end}} +{{- if .Values.controller.secondaryingress.tls }} + tls: +{{ toYaml .Values.controller.secondaryingress.tls | indent 4 }} +{{- end -}} +{{- end }} diff --git a/charts/jenkins/jenkins/5.6.2/templates/jenkins-controller-servicemonitor.yaml b/charts/jenkins/jenkins/5.6.2/templates/jenkins-controller-servicemonitor.yaml new file mode 100644 index 000000000..8710b2bc9 --- /dev/null +++ b/charts/jenkins/jenkins/5.6.2/templates/jenkins-controller-servicemonitor.yaml @@ -0,0 +1,45 @@ +{{- if and .Values.controller.prometheus.enabled }} +apiVersion: monitoring.coreos.com/v1 +kind: ServiceMonitor + +metadata: + name: {{ template "jenkins.fullname" . }} +{{- if .Values.controller.prometheus.serviceMonitorNamespace }} + namespace: {{ .Values.controller.prometheus.serviceMonitorNamespace }} +{{- else }} + namespace: {{ template "jenkins.namespace" . }} +{{- end }} + labels: + "app.kubernetes.io/name": '{{ template "jenkins.name" .}}' + {{- if .Values.renderHelmLabels }} + "helm.sh/chart": "{{ template "jenkins.label" .}}" + {{- end }} + "app.kubernetes.io/managed-by": "{{ .Release.Service }}" + "app.kubernetes.io/instance": "{{ .Release.Name }}" + "app.kubernetes.io/component": "{{ .Values.controller.componentName }}" + {{- range $key, $val := .Values.controller.prometheus.serviceMonitorAdditionalLabels }} + {{ $key }}: {{ $val | quote }} + {{- end}} + +spec: + endpoints: + - interval: {{ .Values.controller.prometheus.scrapeInterval }} + port: http + path: {{ .Values.controller.jenkinsUriPrefix }}{{ .Values.controller.prometheus.scrapeEndpoint }} + {{- with .Values.controller.prometheus.relabelings }} + relabelings: + {{- toYaml . | nindent 6 }} + {{- end }} + {{- with .Values.controller.prometheus.metricRelabelings }} + metricRelabelings: + {{- toYaml . | nindent 6 }} + {{- end }} + jobLabel: {{ template "jenkins.fullname" . }} + namespaceSelector: + matchNames: + - "{{ template "jenkins.namespace" $ }}" + selector: + matchLabels: + "app.kubernetes.io/instance": "{{ .Release.Name }}" + "app.kubernetes.io/component": "{{ .Values.controller.componentName }}" +{{- end }} diff --git a/charts/jenkins/jenkins/5.6.2/templates/jenkins-controller-statefulset.yaml b/charts/jenkins/jenkins/5.6.2/templates/jenkins-controller-statefulset.yaml new file mode 100644 index 000000000..421a6186d --- /dev/null +++ b/charts/jenkins/jenkins/5.6.2/templates/jenkins-controller-statefulset.yaml @@ -0,0 +1,427 @@ +{{- if .Capabilities.APIVersions.Has "apps/v1" }} +apiVersion: apps/v1 +{{- else }} +apiVersion: apps/v1beta1 +{{- end }} +kind: StatefulSet +metadata: + name: {{ template "jenkins.fullname" . }} + namespace: {{ template "jenkins.namespace" . }} + labels: + "app.kubernetes.io/name": '{{ template "jenkins.name" .}}' + {{- if .Values.renderHelmLabels }} + "helm.sh/chart": "{{ template "jenkins.label" .}}" + {{- end }} + "app.kubernetes.io/managed-by": "{{ .Release.Service }}" + "app.kubernetes.io/instance": "{{ .Release.Name }}" + "app.kubernetes.io/component": "{{ .Values.controller.componentName }}" + {{- range $key, $val := .Values.controller.statefulSetLabels }} + {{ $key }}: {{ $val | quote }} + {{- end}} + {{- if .Values.controller.statefulSetAnnotations }} + annotations: +{{ toYaml .Values.controller.statefulSetAnnotations | indent 4 }} + {{- end }} +spec: + serviceName: {{ template "jenkins.fullname" . }} + replicas: 1 + selector: + matchLabels: + "app.kubernetes.io/component": "{{ .Values.controller.componentName }}" + "app.kubernetes.io/instance": "{{ .Release.Name }}" + {{- if .Values.controller.updateStrategy }} + updateStrategy: +{{ toYaml .Values.controller.updateStrategy | indent 4 }} + {{- end }} + template: + metadata: + labels: + "app.kubernetes.io/name": '{{ template "jenkins.name" .}}' + "app.kubernetes.io/managed-by": "{{ .Release.Service }}" + "app.kubernetes.io/instance": "{{ .Release.Name }}" + "app.kubernetes.io/component": "{{ .Values.controller.componentName }}" + {{- range $key, $val := .Values.controller.podLabels }} + {{ $key }}: {{ $val | quote }} + {{- end}} + annotations: + checksum/config: {{ include (print $.Template.BasePath "/config.yaml") . | sha256sum }} + {{- if .Values.controller.initScripts }} + checksum/config-init-scripts: {{ include (print $.Template.BasePath "/config-init-scripts.yaml") . | sha256sum }} + {{- end }} + {{- if .Values.controller.podAnnotations }} +{{ tpl (toYaml .Values.controller.podAnnotations | indent 8) . }} + {{- end }} + spec: + {{- if .Values.controller.schedulerName }} + schedulerName: {{ .Values.controller.schedulerName }} + {{- end }} + {{- if .Values.controller.nodeSelector }} + nodeSelector: +{{ toYaml .Values.controller.nodeSelector | indent 8 }} + {{- end }} + {{- if .Values.controller.tolerations }} + tolerations: +{{ toYaml .Values.controller.tolerations | indent 8 }} + {{- end }} + {{- if .Values.controller.affinity }} + affinity: +{{ toYaml .Values.controller.affinity | indent 8 }} + {{- end }} + {{- if .Values.controller.topologySpreadConstraints }} + topologySpreadConstraints: +{{ toYaml .Values.controller.topologySpreadConstraints | indent 8 }} + {{- end }} + {{- if quote .Values.controller.terminationGracePeriodSeconds }} + terminationGracePeriodSeconds: {{ .Values.controller.terminationGracePeriodSeconds }} + {{- end }} + {{- if .Values.controller.priorityClassName }} + priorityClassName: {{ .Values.controller.priorityClassName }} + {{- end }} + {{- if .Values.controller.shareProcessNamespace }} + shareProcessNamespace: true + {{- end }} + {{- if not .Values.controller.enableServiceLinks }} + enableServiceLinks: false + {{- end }} +{{- if .Values.controller.usePodSecurityContext }} + securityContext: + {{- if kindIs "map" .Values.controller.podSecurityContextOverride }} + {{- tpl (toYaml .Values.controller.podSecurityContextOverride | nindent 8) . -}} + {{- else }} + {{/* The rest of this section should be replaced with the contents of this comment one the runAsUser, fsGroup, and securityContextCapabilities Helm chart values have been removed: + runAsUser: 1000 + fsGroup: 1000 + runAsNonRoot: true + */}} + runAsUser: {{ default 0 .Values.controller.runAsUser }} + {{- if and (.Values.controller.runAsUser) (.Values.controller.fsGroup) }} + {{- if not (eq (int .Values.controller.runAsUser) 0) }} + fsGroup: {{ .Values.controller.fsGroup }} + runAsNonRoot: true + {{- end }} + {{- if .Values.controller.securityContextCapabilities }} + capabilities: + {{- toYaml .Values.controller.securityContextCapabilities | nindent 10 }} + {{- end }} + {{- end }} + {{- end }} +{{- end }} + serviceAccountName: "{{ template "jenkins.serviceAccountName" . }}" +{{- if .Values.controller.hostNetworking }} + hostNetwork: true + dnsPolicy: ClusterFirstWithHostNet +{{- end }} + {{- if .Values.controller.hostAliases }} + hostAliases: + {{- toYaml .Values.controller.hostAliases | nindent 8 }} + {{- end }} + initContainers: +{{- if .Values.controller.customInitContainers }} +{{ tpl (toYaml .Values.controller.customInitContainers) . | indent 8 }} +{{- end }} + +{{- if .Values.controller.sidecars.configAutoReload.enabled }} +{{- include "jenkins.configReloadContainer" (list $ "config-reload-init" "init") | nindent 8 }} +{{- end}} + + - name: "init" + image: "{{ .Values.controller.image.registry }}/{{ .Values.controller.image.repository }}:{{- include "controller.image.tag" . -}}" + imagePullPolicy: "{{ .Values.controller.image.pullPolicy }}" + {{- if .Values.controller.containerSecurityContext }} + securityContext: {{- toYaml .Values.controller.containerSecurityContext | nindent 12 }} + {{- end }} + command: [ "sh", "/var/jenkins_config/apply_config.sh" ] + {{- if .Values.controller.initContainerEnvFrom }} + envFrom: +{{ (tpl (toYaml .Values.controller.initContainerEnvFrom) .) | indent 12 }} + {{- end }} + {{- if .Values.controller.initContainerEnv }} + env: +{{ (tpl (toYaml .Values.controller.initContainerEnv) .) | indent 12 }} + {{- end }} + resources: +{{- if .Values.controller.initContainerResources }} +{{ toYaml .Values.controller.initContainerResources | indent 12 }} +{{- else }} +{{ toYaml .Values.controller.resources | indent 12 }} +{{- end }} + volumeMounts: + {{- if .Values.persistence.mounts }} +{{ toYaml .Values.persistence.mounts | indent 12 }} + {{- end }} + - mountPath: {{ .Values.controller.jenkinsHome }} + name: jenkins-home + {{- if .Values.persistence.subPath }} + subPath: {{ .Values.persistence.subPath }} + {{- end }} + - mountPath: /var/jenkins_config + name: jenkins-config + {{- if .Values.controller.installPlugins }} + {{- if .Values.controller.overwritePluginsFromImage }} + - mountPath: {{ .Values.controller.jenkinsRef }}/plugins + name: plugins + {{- end }} + - mountPath: /var/jenkins_plugins + name: plugin-dir + - mountPath: /tmp + name: tmp-volume + {{- end }} + {{- if or .Values.controller.initScripts .Values.controller.initConfigMap }} + - mountPath: {{ .Values.controller.jenkinsHome }}/init.groovy.d + name: init-scripts + {{- end }} + {{- if and .Values.controller.httpsKeyStore.enable (not .Values.controller.httpsKeyStore.disableSecretMount) }} + {{- $httpsJKSDirPath := printf "%s" .Values.controller.httpsKeyStore.path }} + - mountPath: {{ $httpsJKSDirPath }} + name: jenkins-https-keystore + {{- end }} + containers: + - name: jenkins + image: "{{ .Values.controller.image.registry }}/{{ .Values.controller.image.repository }}:{{- include "controller.image.tag" . -}}" + imagePullPolicy: "{{ .Values.controller.image.pullPolicy }}" + {{- if .Values.controller.containerSecurityContext }} + securityContext: {{- toYaml .Values.controller.containerSecurityContext | nindent 12 }} + {{- end }} + {{- if .Values.controller.overrideArgs }} + args: [ + {{- range $overrideArg := .Values.controller.overrideArgs }} + "{{- tpl $overrideArg $ }}", + {{- end }} + ] + {{- else if .Values.controller.httpsKeyStore.enable }} + {{- $httpsJKSFilePath := printf "%s/%s" .Values.controller.httpsKeyStore.path .Values.controller.httpsKeyStore.fileName }} + args: [ "--httpPort={{.Values.controller.httpsKeyStore.httpPort}}", "--httpsPort={{.Values.controller.targetPort}}", '--httpsKeyStore={{ $httpsJKSFilePath }}', "--httpsKeyStorePassword=$(JENKINS_HTTPS_KEYSTORE_PASSWORD)" ] + {{- else }} + args: [ "--httpPort={{.Values.controller.targetPort}}"] + {{- end }} + {{- if .Values.controller.lifecycle }} + lifecycle: +{{ toYaml .Values.controller.lifecycle | indent 12 }} + {{- end }} +{{- if .Values.controller.terminationMessagePath }} + terminationMessagePath: {{ .Values.controller.terminationMessagePath }} +{{- end }} +{{- if .Values.controller.terminationMessagePolicy }} + terminationMessagePolicy: {{ .Values.controller.terminationMessagePolicy }} +{{- end }} + {{- if .Values.controller.containerEnvFrom }} + envFrom: +{{ (tpl ( toYaml .Values.controller.containerEnvFrom) .) | indent 12 }} + {{- end }} + env: + {{- if .Values.controller.containerEnv }} +{{ (tpl ( toYaml .Values.controller.containerEnv) .) | indent 12 }} + {{- end }} + {{- if or .Values.controller.additionalSecrets .Values.controller.existingSecret .Values.controller.additionalExistingSecrets .Values.controller.admin.createSecret }} + - name: SECRETS + value: /run/secrets/additional + {{- end }} + - name: POD_NAME + valueFrom: + fieldRef: + fieldPath: metadata.name + - name: JAVA_OPTS + value: >- + {{ if .Values.controller.sidecars.configAutoReload.enabled }} -Dcasc.reload.token=$(POD_NAME) {{ end }}{{ default "" .Values.controller.javaOpts }} + - name: JENKINS_OPTS + value: >- + {{ if .Values.controller.jenkinsUriPrefix }}--prefix={{ .Values.controller.jenkinsUriPrefix }} {{ end }} --webroot=/var/jenkins_cache/war {{ default "" .Values.controller.jenkinsOpts}} + - name: JENKINS_SLAVE_AGENT_PORT + value: "{{ .Values.controller.agentListenerPort }}" + {{- if .Values.controller.httpsKeyStore.enable }} + - name: JENKINS_HTTPS_KEYSTORE_PASSWORD + {{- if not .Values.controller.httpsKeyStore.disableSecretMount }} + valueFrom: + secretKeyRef: + name: {{ if .Values.controller.httpsKeyStore.jenkinsHttpsJksPasswordSecretName }} {{ .Values.controller.httpsKeyStore.jenkinsHttpsJksPasswordSecretName }} {{ else if .Values.controller.httpsKeyStore.jenkinsHttpsJksSecretName }} {{ .Values.controller.httpsKeyStore.jenkinsHttpsJksSecretName }} {{ else }} {{ template "jenkins.fullname" . }}-https-jks {{ end }} + key: "{{ .Values.controller.httpsKeyStore.jenkinsHttpsJksPasswordSecretKey }}" + {{- else }} + value: {{ .Values.controller.httpsKeyStore.password }} + {{- end }} + {{- end }} + + - name: CASC_JENKINS_CONFIG + value: {{ .Values.controller.sidecars.configAutoReload.folder | default (printf "%s/casc_configs" (.Values.controller.jenkinsRef)) }}{{- if .Values.controller.JCasC.configUrls }},{{ join "," .Values.controller.JCasC.configUrls }}{{- end }} + ports: + {{- if .Values.controller.httpsKeyStore.enable }} + - containerPort: {{.Values.controller.httpsKeyStore.httpPort}} + {{- else }} + - containerPort: {{.Values.controller.targetPort}} + {{- end }} + name: http + - containerPort: {{ .Values.controller.agentListenerPort }} + name: agent-listener + {{- if .Values.controller.agentListenerHostPort }} + hostPort: {{ .Values.controller.agentListenerHostPort }} + {{- end }} + {{- if .Values.controller.jmxPort }} + - containerPort: {{ .Values.controller.jmxPort }} + name: jmx + {{- end }} +{{- range $index, $port := .Values.controller.extraPorts }} + - containerPort: {{ $port.port }} + name: {{ $port.name }} +{{- end }} +{{- if and .Values.controller.healthProbes .Values.controller.probes}} + {{- if semverCompare ">=1.16-0" .Capabilities.KubeVersion.GitVersion }} + startupProbe: +{{ tpl (toYaml .Values.controller.probes.startupProbe | indent 12) .}} + {{- end }} + livenessProbe: +{{ tpl (toYaml .Values.controller.probes.livenessProbe | indent 12) .}} + readinessProbe: +{{ tpl (toYaml .Values.controller.probes.readinessProbe | indent 12) .}} +{{- end }} + resources: +{{ toYaml .Values.controller.resources | indent 12 }} + volumeMounts: +{{- if .Values.persistence.mounts }} +{{ toYaml .Values.persistence.mounts | indent 12 }} +{{- end }} + {{- if and .Values.controller.httpsKeyStore.enable (not .Values.controller.httpsKeyStore.disableSecretMount) }} + {{- $httpsJKSDirPath := printf "%s" .Values.controller.httpsKeyStore.path }} + - mountPath: {{ $httpsJKSDirPath }} + name: jenkins-https-keystore + {{- end }} + - mountPath: {{ .Values.controller.jenkinsHome }} + name: jenkins-home + readOnly: false + {{- if .Values.persistence.subPath }} + subPath: {{ .Values.persistence.subPath }} + {{- end }} + - mountPath: /var/jenkins_config + name: jenkins-config + readOnly: true + {{- if .Values.controller.installPlugins }} + - mountPath: {{ .Values.controller.jenkinsRef }}/plugins/ + name: plugin-dir + readOnly: false + {{- end }} + {{- if or .Values.controller.initScripts .Values.controller.initConfigMap }} + - mountPath: {{ .Values.controller.jenkinsHome }}/init.groovy.d + name: init-scripts + {{- end }} + {{- if .Values.controller.sidecars.configAutoReload.enabled }} + - name: sc-config-volume + mountPath: {{ .Values.controller.sidecars.configAutoReload.folder | default (printf "%s/casc_configs" (.Values.controller.jenkinsRef)) }} + {{- end }} + {{- if or .Values.controller.additionalSecrets .Values.controller.existingSecret .Values.controller.additionalExistingSecrets .Values.controller.admin.createSecret }} + - name: jenkins-secrets + mountPath: /run/secrets/additional + readOnly: true + {{- end }} + - name: jenkins-cache + mountPath: /var/jenkins_cache + - mountPath: /tmp + name: tmp-volume + +{{- if .Values.controller.sidecars.configAutoReload.enabled }} +{{- include "jenkins.configReloadContainer" (list $ "config-reload" "sidecar") | nindent 8 }} +{{- end}} + + +{{- if .Values.controller.sidecars.additionalSidecarContainers}} +{{ tpl (toYaml .Values.controller.sidecars.additionalSidecarContainers | indent 8) .}} +{{- end }} + + volumes: +{{- if .Values.persistence.volumes }} +{{ tpl (toYaml .Values.persistence.volumes | indent 6) . }} +{{- end }} + {{- if .Values.controller.sidecars.configAutoReload.logging.configuration.override }} + - name: auto-reload-config + configMap: + name: {{ template "jenkins.fullname" . }}-auto-reload-config + - name: auto-reload-config-logs + emptyDir: {} + {{- end }} + {{- if .Values.controller.installPlugins }} + {{- if .Values.controller.overwritePluginsFromImage }} + - name: plugins + emptyDir: {} + {{- end }} + {{- end }} + {{- if and .Values.controller.initScripts .Values.controller.initConfigMap }} + - name: init-scripts + projected: + sources: + - configMap: + name: {{ template "jenkins.fullname" . }}-init-scripts + - configMap: + name: {{ .Values.controller.initConfigMap }} + {{- else if .Values.controller.initConfigMap }} + - name: init-scripts + configMap: + name: {{ .Values.controller.initConfigMap }} + {{- else if .Values.controller.initScripts }} + - name: init-scripts + configMap: + name: {{ template "jenkins.fullname" . }}-init-scripts + {{- end }} + - name: jenkins-config + configMap: + name: {{ template "jenkins.fullname" . }} + {{- if .Values.controller.installPlugins }} + - name: plugin-dir + emptyDir: {} + {{- end }} + {{- if or .Values.controller.additionalSecrets .Values.controller.existingSecret .Values.controller.additionalExistingSecrets .Values.controller.admin.createSecret }} + - name: jenkins-secrets + projected: + sources: + {{- if .Values.controller.additionalSecrets }} + - secret: + name: {{ template "jenkins.fullname" . }}-additional-secrets + {{- end }} + {{- if .Values.controller.additionalExistingSecrets }} + {{- range $key, $value := .Values.controller.additionalExistingSecrets }} + - secret: + name: {{ tpl $value.name $ }} + items: + - key: {{ tpl $value.keyName $ }} + path: {{ tpl $value.name $ }}-{{ tpl $value.keyName $ }} + {{- end }} + {{- end }} + {{- if .Values.controller.admin.createSecret }} + - secret: + name: {{ .Values.controller.admin.existingSecret | default (include "jenkins.fullname" .) }} + items: + - key: {{ .Values.controller.admin.userKey | default "jenkins-admin-user" }} + path: chart-admin-username + - key: {{ .Values.controller.admin.passwordKey | default "jenkins-admin-password" }} + path: chart-admin-password + {{- end }} + {{- if .Values.controller.existingSecret }} + - secret: + name: {{ .Values.controller.existingSecret }} + {{- end }} + {{- end }} + - name: jenkins-cache + emptyDir: {} + {{- if not (contains "jenkins-home" (quote .Values.persistence.volumes)) }} + - name: jenkins-home + {{- if .Values.persistence.enabled }} + persistentVolumeClaim: + claimName: {{ .Values.persistence.existingClaim | default (include "jenkins.fullname" .) }} + {{- else }} + emptyDir: {} + {{- end -}} + {{- end }} + - name: sc-config-volume + emptyDir: {} + - name: tmp-volume + emptyDir: {} + + {{- if and .Values.controller.httpsKeyStore.enable (not .Values.controller.httpsKeyStore.disableSecretMount) }} + - name: jenkins-https-keystore + secret: + secretName: {{ if .Values.controller.httpsKeyStore.jenkinsHttpsJksSecretName }} {{ .Values.controller.httpsKeyStore.jenkinsHttpsJksSecretName }} {{ else }} {{ template "jenkins.fullname" . }}-https-jks {{ end }} + items: + - key: {{ .Values.controller.httpsKeyStore.jenkinsHttpsJksSecretKey }} + path: {{ .Values.controller.httpsKeyStore.fileName }} + {{- end }} + +{{- if .Values.controller.imagePullSecretName }} + imagePullSecrets: + - name: {{ .Values.controller.imagePullSecretName }} +{{- end -}} diff --git a/charts/jenkins/jenkins/5.6.2/templates/jenkins-controller-svc.yaml b/charts/jenkins/jenkins/5.6.2/templates/jenkins-controller-svc.yaml new file mode 100644 index 000000000..a83466ce3 --- /dev/null +++ b/charts/jenkins/jenkins/5.6.2/templates/jenkins-controller-svc.yaml @@ -0,0 +1,56 @@ +apiVersion: v1 +kind: Service +metadata: + name: {{template "jenkins.fullname" . }} + namespace: {{ template "jenkins.namespace" . }} + labels: + "app.kubernetes.io/name": '{{ template "jenkins.name" .}}' + {{- if .Values.renderHelmLabels }} + "helm.sh/chart": "{{ template "jenkins.label" .}}" + {{- end }} + "app.kubernetes.io/managed-by": "{{ .Release.Service }}" + "app.kubernetes.io/instance": "{{ .Release.Name }}" + "app.kubernetes.io/component": "{{ .Values.controller.componentName }}" + {{- if .Values.controller.serviceLabels }} +{{ toYaml .Values.controller.serviceLabels | indent 4 }} + {{- end }} +{{- if .Values.controller.serviceAnnotations }} + annotations: +{{ toYaml .Values.controller.serviceAnnotations | indent 4 }} +{{- end }} +spec: + {{- if .Values.controller.serviceExternalTrafficPolicy }} + externalTrafficPolicy: {{.Values.controller.serviceExternalTrafficPolicy}} + {{- end }} + {{- if (and (eq .Values.controller.serviceType "ClusterIP") (not (empty .Values.controller.clusterIP))) }} + clusterIP: {{.Values.controller.clusterIP}} + {{- end }} + ports: + - port: {{.Values.controller.servicePort}} + name: http + targetPort: {{ .Values.controller.targetPort }} + {{- if (and (eq .Values.controller.serviceType "NodePort") (not (empty .Values.controller.nodePort))) }} + nodePort: {{.Values.controller.nodePort}} + {{- end }} +{{- range $index, $port := .Values.controller.extraPorts }} + - port: {{ $port.port }} + name: {{ $port.name }} + {{- if $port.targetPort }} + targetPort: {{ $port.targetPort }} + {{- else }} + targetPort: {{ $port.port }} + {{- end -}} +{{- end }} + selector: + "app.kubernetes.io/component": "{{ .Values.controller.componentName }}" + "app.kubernetes.io/instance": "{{ .Release.Name }}" + type: {{.Values.controller.serviceType}} + {{if eq .Values.controller.serviceType "LoadBalancer"}} +{{- if .Values.controller.loadBalancerSourceRanges }} + loadBalancerSourceRanges: +{{ toYaml .Values.controller.loadBalancerSourceRanges | indent 4 }} +{{- end }} + {{if .Values.controller.loadBalancerIP}} + loadBalancerIP: {{.Values.controller.loadBalancerIP}} + {{end}} + {{end}} diff --git a/charts/jenkins/jenkins/5.6.2/templates/rbac.yaml b/charts/jenkins/jenkins/5.6.2/templates/rbac.yaml new file mode 100644 index 000000000..581cb8d48 --- /dev/null +++ b/charts/jenkins/jenkins/5.6.2/templates/rbac.yaml @@ -0,0 +1,149 @@ +{{ if .Values.rbac.create }} +{{- $serviceName := include "jenkins.fullname" . -}} + +# This role is used to allow Jenkins scheduling of agents via Kubernetes plugin. +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: {{ $serviceName }}-schedule-agents + namespace: {{ template "jenkins.agent.namespace" . }} + labels: + "app.kubernetes.io/name": '{{ template "jenkins.name" .}}' + {{- if .Values.renderHelmLabels }} + "helm.sh/chart": "{{ template "jenkins.label" .}}" + {{- end }} + "app.kubernetes.io/managed-by": "{{ .Release.Service }}" + "app.kubernetes.io/instance": "{{ .Release.Name }}" + "app.kubernetes.io/component": "{{ .Values.controller.componentName }}" +rules: +- apiGroups: [""] + resources: ["pods", "pods/exec", "pods/log", "persistentvolumeclaims", "events"] + verbs: ["get", "list", "watch"] +- apiGroups: [""] + resources: ["pods", "pods/exec", "persistentvolumeclaims"] + verbs: ["create", "delete", "deletecollection", "patch", "update"] + +--- + +# We bind the role to the Jenkins service account. The role binding is created in the namespace +# where the agents are supposed to run. +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: {{ $serviceName }}-schedule-agents + namespace: {{ template "jenkins.agent.namespace" . }} + labels: + "app.kubernetes.io/name": '{{ template "jenkins.name" .}}' + {{- if .Values.renderHelmLabels }} + "helm.sh/chart": "{{ template "jenkins.label" .}}" + {{- end }} + "app.kubernetes.io/managed-by": "{{ .Release.Service }}" + "app.kubernetes.io/instance": "{{ .Release.Name }}" + "app.kubernetes.io/component": "{{ .Values.controller.componentName }}" +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: {{ $serviceName }}-schedule-agents +subjects: +- kind: ServiceAccount + name: {{ template "jenkins.serviceAccountName" .}} + namespace: {{ template "jenkins.namespace" . }} + +--- + +{{- if .Values.rbac.readSecrets }} +# This is needed if you want to use https://jenkinsci.github.io/kubernetes-credentials-provider-plugin/ +# as it needs permissions to get/watch/list Secrets +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: {{ template "jenkins.fullname" . }}-read-secrets + namespace: {{ template "jenkins.namespace" . }} + labels: + "app.kubernetes.io/name": '{{ template "jenkins.name" .}}' + {{- if .Values.renderHelmLabels }} + "helm.sh/chart": "{{ template "jenkins.label" .}}" + {{- end }} + "app.kubernetes.io/managed-by": "{{ .Release.Service }}" + "app.kubernetes.io/instance": "{{ .Release.Name }}" + "app.kubernetes.io/component": "{{ .Values.controller.componentName }}" +rules: + - apiGroups: [""] + resources: ["secrets"] + verbs: ["get", "watch", "list"] + +--- + +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: {{ $serviceName }}-read-secrets + namespace: {{ template "jenkins.namespace" . }} + labels: + "app.kubernetes.io/name": '{{ template "jenkins.name" .}}' + {{- if .Values.renderHelmLabels }} + "helm.sh/chart": "{{ template "jenkins.label" .}}" + {{- end }} + "app.kubernetes.io/managed-by": "{{ .Release.Service }}" + "app.kubernetes.io/instance": "{{ .Release.Name }}" + "app.kubernetes.io/component": "{{ .Values.controller.componentName }}" +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: {{ template "jenkins.fullname" . }}-read-secrets +subjects: + - kind: ServiceAccount + name: {{ template "jenkins.serviceAccountName" . }} + namespace: {{ template "jenkins.namespace" . }} + +--- +{{- end}} + +{{- if .Values.controller.sidecars.configAutoReload.enabled }} +# The sidecar container which is responsible for reloading configuration changes +# needs permissions to watch ConfigMaps +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: {{ template "jenkins.fullname" . }}-casc-reload + namespace: {{ template "jenkins.namespace" . }} + labels: + "app.kubernetes.io/name": '{{ template "jenkins.name" .}}' + {{- if .Values.renderHelmLabels }} + "helm.sh/chart": "{{ template "jenkins.label" .}}" + {{- end }} + "app.kubernetes.io/managed-by": "{{ .Release.Service }}" + "app.kubernetes.io/instance": "{{ .Release.Name }}" + "app.kubernetes.io/component": "{{ .Values.controller.componentName }}" +rules: +- apiGroups: [""] + resources: ["configmaps"] + verbs: ["get", "watch", "list"] + +--- + +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: {{ $serviceName }}-watch-configmaps + namespace: {{ template "jenkins.namespace" . }} + labels: + "app.kubernetes.io/name": '{{ template "jenkins.name" .}}' + {{- if .Values.renderHelmLabels }} + "helm.sh/chart": "{{ template "jenkins.label" .}}" + {{- end }} + "app.kubernetes.io/managed-by": "{{ .Release.Service }}" + "app.kubernetes.io/instance": "{{ .Release.Name }}" + "app.kubernetes.io/component": "{{ .Values.controller.componentName }}" +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: {{ template "jenkins.fullname" . }}-casc-reload +subjects: +- kind: ServiceAccount + name: {{ template "jenkins.serviceAccountName" . }} + namespace: {{ template "jenkins.namespace" . }} + +{{- end}} + +{{ end }} diff --git a/charts/jenkins/jenkins/5.6.2/templates/secret-additional.yaml b/charts/jenkins/jenkins/5.6.2/templates/secret-additional.yaml new file mode 100644 index 000000000..d1908aa9b --- /dev/null +++ b/charts/jenkins/jenkins/5.6.2/templates/secret-additional.yaml @@ -0,0 +1,21 @@ +{{- if .Values.controller.additionalSecrets -}} +--- +apiVersion: v1 +kind: Secret +metadata: + name: {{ template "jenkins.fullname" . }}-additional-secrets + namespace: {{ template "jenkins.namespace" . }} + labels: + "app.kubernetes.io/name": '{{ template "jenkins.name" .}}' + {{- if .Values.renderHelmLabels }} + "helm.sh/chart": "{{ template "jenkins.label" .}}" + {{- end }} + "app.kubernetes.io/managed-by": "{{ .Release.Service }}" + "app.kubernetes.io/instance": "{{ .Release.Name }}" + "app.kubernetes.io/component": "{{ .Values.controller.componentName }}" +type: Opaque +data: +{{- range .Values.controller.additionalSecrets }} + {{ .name }}: {{ .value | b64enc }} +{{- end }} +{{- end }} diff --git a/charts/jenkins/jenkins/5.6.2/templates/secret-claims.yaml b/charts/jenkins/jenkins/5.6.2/templates/secret-claims.yaml new file mode 100644 index 000000000..e8b6d6c8e --- /dev/null +++ b/charts/jenkins/jenkins/5.6.2/templates/secret-claims.yaml @@ -0,0 +1,29 @@ +{{- if .Values.controller.secretClaims -}} +{{- $r := .Release -}} +{{- $v := .Values -}} +{{- $chart := printf "%s-%s" .Chart.Name .Chart.Version -}} +{{- $namespace := include "jenkins.namespace" . -}} +{{- $serviceName := include "jenkins.fullname" . -}} +{{ range .Values.controller.secretClaims }} +--- +kind: SecretClaim +apiVersion: vaultproject.io/v1 +metadata: + name: {{ $serviceName }}-{{ .name | default .path | lower }} + namespace: {{ $namespace }} + labels: + "app.kubernetes.io/name": '{{ $serviceName }}' + {{- if $v.renderHelmLabels }} + "helm.sh/chart": "{{ $chart }}" + {{- end }} + "app.kubernetes.io/managed-by": "{{ $r.Service }}" + "app.kubernetes.io/instance": "{{ $r.Name }}" + "app.kubernetes.io/component": "{{ $v.controller.componentName }}" +spec: + type: {{ .type | default "Opaque" }} + path: {{ .path }} +{{- if .renew }} + renew: {{ .renew }} +{{- end }} +{{- end }} +{{- end }} \ No newline at end of file diff --git a/charts/jenkins/jenkins/5.6.2/templates/secret-https-jks.yaml b/charts/jenkins/jenkins/5.6.2/templates/secret-https-jks.yaml new file mode 100644 index 000000000..5348de41e --- /dev/null +++ b/charts/jenkins/jenkins/5.6.2/templates/secret-https-jks.yaml @@ -0,0 +1,20 @@ +{{- if and .Values.controller.httpsKeyStore.enable ( not .Values.controller.httpsKeyStore.jenkinsHttpsJksSecretName ) (not .Values.controller.httpsKeyStore.disableSecretMount) -}} +apiVersion: v1 +kind: Secret +metadata: + name: {{ template "jenkins.fullname" . }}-https-jks + namespace: {{ template "jenkins.namespace" . }} + labels: + "app.kubernetes.io/name": '{{ template "jenkins.name" .}}' + {{- if .Values.renderHelmLabels }} + "helm.sh/chart": "{{ template "jenkins.label" .}}" + {{- end }} + "app.kubernetes.io/managed-by": "{{ .Release.Service }}" + "app.kubernetes.io/instance": "{{ .Release.Name }}" + "app.kubernetes.io/component": "{{ .Values.controller.componentName }}" +type: Opaque +data: + jenkins-jks-file: | +{{ .Values.controller.httpsKeyStore.jenkinsKeyStoreBase64Encoded | indent 4 }} + https-jks-password: {{ .Values.controller.httpsKeyStore.password | b64enc }} +{{- end }} diff --git a/charts/jenkins/jenkins/5.6.2/templates/secret.yaml b/charts/jenkins/jenkins/5.6.2/templates/secret.yaml new file mode 100644 index 000000000..cc6ace179 --- /dev/null +++ b/charts/jenkins/jenkins/5.6.2/templates/secret.yaml @@ -0,0 +1,20 @@ +{{- if and (not .Values.controller.admin.existingSecret) (.Values.controller.admin.createSecret) -}} + +apiVersion: v1 +kind: Secret +metadata: + name: {{ template "jenkins.fullname" . }} + namespace: {{ template "jenkins.namespace" . }} + labels: + "app.kubernetes.io/name": '{{ template "jenkins.name" .}}' + {{- if .Values.renderHelmLabels }} + "helm.sh/chart": "{{ template "jenkins.label" .}}" + {{- end }} + "app.kubernetes.io/managed-by": "{{ .Release.Service }}" + "app.kubernetes.io/instance": "{{ .Release.Name }}" + "app.kubernetes.io/component": "{{ .Values.controller.componentName }}" +type: Opaque +data: + jenkins-admin-password: {{ template "jenkins.password" . }} + jenkins-admin-user: {{ .Values.controller.admin.username | b64enc | quote }} +{{- end }} diff --git a/charts/jenkins/jenkins/5.6.2/templates/service-account-agent.yaml b/charts/jenkins/jenkins/5.6.2/templates/service-account-agent.yaml new file mode 100644 index 000000000..48f08ba6c --- /dev/null +++ b/charts/jenkins/jenkins/5.6.2/templates/service-account-agent.yaml @@ -0,0 +1,26 @@ +{{ if .Values.serviceAccountAgent.create }} +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ include "jenkins.serviceAccountAgentName" . }} + namespace: {{ template "jenkins.agent.namespace" . }} +{{- if .Values.serviceAccountAgent.annotations }} + annotations: +{{ tpl (toYaml .Values.serviceAccountAgent.annotations) . | indent 4 }} +{{- end }} + labels: + "app.kubernetes.io/name": '{{ template "jenkins.name" .}}' + {{- if .Values.renderHelmLabels }} + "helm.sh/chart": "{{ template "jenkins.label" .}}" + {{- end }} + "app.kubernetes.io/managed-by": "{{ .Release.Service }}" + "app.kubernetes.io/instance": "{{ .Release.Name }}" + "app.kubernetes.io/component": "{{ .Values.controller.componentName }}" +{{- if .Values.serviceAccountAgent.extraLabels }} +{{ tpl (toYaml .Values.serviceAccountAgent.extraLabels) . | indent 4 }} +{{- end }} +{{- if .Values.serviceAccountAgent.imagePullSecretName }} +imagePullSecrets: + - name: {{ .Values.serviceAccountAgent.imagePullSecretName }} +{{- end -}} +{{ end }} diff --git a/charts/jenkins/jenkins/5.6.2/templates/service-account.yaml b/charts/jenkins/jenkins/5.6.2/templates/service-account.yaml new file mode 100644 index 000000000..b44eb488c --- /dev/null +++ b/charts/jenkins/jenkins/5.6.2/templates/service-account.yaml @@ -0,0 +1,26 @@ +{{ if .Values.serviceAccount.create }} +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ include "jenkins.serviceAccountName" . }} + namespace: {{ template "jenkins.namespace" . }} +{{- if .Values.serviceAccount.annotations }} + annotations: +{{ tpl (toYaml .Values.serviceAccount.annotations) . | indent 4 }} +{{- end }} + labels: + "app.kubernetes.io/name": '{{ template "jenkins.name" .}}' + {{- if .Values.renderHelmLabels }} + "helm.sh/chart": "{{ template "jenkins.label" .}}" + {{- end }} + "app.kubernetes.io/managed-by": "{{ .Release.Service }}" + "app.kubernetes.io/instance": "{{ .Release.Name }}" + "app.kubernetes.io/component": "{{ .Values.controller.componentName }}" +{{- if .Values.serviceAccount.extraLabels }} +{{ tpl (toYaml .Values.serviceAccount.extraLabels) . | indent 4 }} +{{- end }} +{{- if .Values.serviceAccount.imagePullSecretName }} +imagePullSecrets: + - name: {{ .Values.serviceAccount.imagePullSecretName }} +{{- end -}} +{{ end }} diff --git a/charts/jenkins/jenkins/5.6.2/templates/tests/jenkins-test.yaml b/charts/jenkins/jenkins/5.6.2/templates/tests/jenkins-test.yaml new file mode 100644 index 000000000..12a935ecc --- /dev/null +++ b/charts/jenkins/jenkins/5.6.2/templates/tests/jenkins-test.yaml @@ -0,0 +1,49 @@ +{{- if .Values.controller.testEnabled }} +apiVersion: v1 +kind: Pod +metadata: + name: "{{ .Release.Name }}-ui-test-{{ randAlphaNum 5 | lower }}" + namespace: {{ template "jenkins.namespace" . }} + annotations: + "helm.sh/hook": test-success +spec: + {{- if .Values.controller.nodeSelector }} + nodeSelector: +{{ toYaml .Values.controller.nodeSelector | indent 4 }} + {{- end }} + {{- if .Values.controller.tolerations }} + tolerations: +{{ toYaml .Values.controller.tolerations | indent 4 }} + {{- end }} + initContainers: + - name: "test-framework" + image: "{{ .Values.helmtest.bats.image.registry }}/{{ .Values.helmtest.bats.image.repository }}:{{ .Values.helmtest.bats.image.tag }}" + command: + - "bash" + - "-c" + args: + - | + # copy bats to tools dir + set -ex + cp -R /opt/bats /tools/bats/ + volumeMounts: + - mountPath: /tools + name: tools + containers: + - name: {{ .Release.Name }}-ui-test + image: "{{ .Values.controller.image.registry }}/{{ .Values.controller.image.repository }}:{{- include "controller.image.tag" . -}}" + command: ["/tools/bats/bin/bats", "-t", "/tests/run.sh"] + volumeMounts: + - mountPath: /tests + name: tests + readOnly: true + - mountPath: /tools + name: tools + volumes: + - name: tests + configMap: + name: {{ template "jenkins.fullname" . }}-tests + - name: tools + emptyDir: {} + restartPolicy: Never +{{- end }} diff --git a/charts/jenkins/jenkins/5.6.2/templates/tests/test-config.yaml b/charts/jenkins/jenkins/5.6.2/templates/tests/test-config.yaml new file mode 100644 index 000000000..12c5b3a0d --- /dev/null +++ b/charts/jenkins/jenkins/5.6.2/templates/tests/test-config.yaml @@ -0,0 +1,14 @@ +{{- if .Values.controller.testEnabled }} +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ template "jenkins.fullname" . }}-tests + namespace: {{ template "jenkins.namespace" . }} + annotations: + "helm.sh/hook": test +data: + run.sh: |- + @test "Testing Jenkins UI is accessible" { + curl --retry 48 --retry-delay 10 {{ template "jenkins.fullname" . }}:{{ .Values.controller.servicePort }}{{ default "" .Values.controller.jenkinsUriPrefix }}/login + } +{{- end }} diff --git a/charts/jenkins/jenkins/5.6.2/values.yaml b/charts/jenkins/jenkins/5.6.2/values.yaml new file mode 100644 index 000000000..221debd40 --- /dev/null +++ b/charts/jenkins/jenkins/5.6.2/values.yaml @@ -0,0 +1,1363 @@ +# Default values for jenkins. +# This is a YAML-formatted file. +# Declare name/value pairs to be passed into your templates. +# name: value + +## Overrides for generated resource names +# See templates/_helpers.tpl +# -- Override the resource name prefix +# @default -- `Chart.Name` +nameOverride: +# -- Override the full resource names +# @default -- `jenkins-(release-name)` or `jenkins` if the release-name is `jenkins` +fullnameOverride: +# -- Override the deployment namespace +# @default -- `Release.Namespace` +namespaceOverride: + +# For FQDN resolving of the controller service. Change this value to match your existing configuration. +# ref: https://github.com/kubernetes/dns/blob/master/docs/specification.md +# -- Override the cluster name for FQDN resolving +clusterZone: "cluster.local" + +# -- The URL of the Kubernetes API server +kubernetesURL: "https://kubernetes.default" + +# -- The Jenkins credentials to access the Kubernetes API server. For the default cluster it is not needed. +credentialsId: + +# -- Enables rendering of the helm.sh/chart label to the annotations +renderHelmLabels: true + +controller: + # -- Used for label app.kubernetes.io/component + componentName: "jenkins-controller" + image: + # -- Controller image registry + registry: "docker.io" + # -- Controller image repository + repository: "jenkins/jenkins" + + # -- Controller image tag override; i.e., tag: "2.440.1-jdk17" + tag: + + # -- Controller image tag label + tagLabel: jdk17 + # -- Controller image pull policy + pullPolicy: "Always" + # -- Controller image pull secret + imagePullSecretName: + # -- Lifecycle specification for controller-container + lifecycle: {} + # postStart: + # exec: + # command: + # - "uname" + # - "-a" + + # -- Disable use of remember me + disableRememberMe: false + + # -- Set Number of executors + numExecutors: 0 + + # -- Sets the executor mode of the Jenkins node. Possible values are "NORMAL" or "EXCLUSIVE" + executorMode: "NORMAL" + + # -- Append Jenkins labels to the controller + customJenkinsLabels: [] + + hostNetworking: false + + # When enabling LDAP or another non-Jenkins identity source, the built-in admin account will no longer exist. + # If you disable the non-Jenkins identity store and instead use the Jenkins internal one, + # you should revert controller.admin.username to your preferred admin user: + admin: + + # -- Admin username created as a secret if `controller.admin.createSecret` is true + username: "admin" + # -- Admin password created as a secret if `controller.admin.createSecret` is true + # @default -- + password: + + # -- The key in the existing admin secret containing the username + userKey: jenkins-admin-user + # -- The key in the existing admin secret containing the password + passwordKey: jenkins-admin-password + + # The default configuration uses this secret to configure an admin user + # If you don't need that user or use a different security realm, then you can disable it + # -- Create secret for admin user + createSecret: true + + # -- The name of an existing secret containing the admin credentials + existingSecret: "" + # -- Email address for the administrator of the Jenkins instance + jenkinsAdminEmail: + + # This value should not be changed unless you use your custom image of jenkins or any derived from. + # If you want to use Cloudbees Jenkins Distribution docker, you should set jenkinsHome: "/var/cloudbees-jenkins-distribution" + # -- Custom Jenkins home path + jenkinsHome: "/var/jenkins_home" + + # This value should not be changed unless you use your custom image of jenkins or any derived from. + # If you want to use Cloudbees Jenkins Distribution docker, you should set jenkinsRef: "/usr/share/cloudbees-jenkins-distribution/ref" + # -- Custom Jenkins reference path + jenkinsRef: "/usr/share/jenkins/ref" + + # Path to the jenkins war file which is used by jenkins-plugin-cli. + jenkinsWar: "/usr/share/jenkins/jenkins.war" + # Override the default arguments passed to the war + # overrideArgs: + # - --httpPort=8080 + + # -- Resource allocation (Requests and Limits) + resources: + requests: + cpu: "50m" + memory: "256Mi" + limits: + cpu: "2000m" + memory: "4096Mi" + + # Share process namespace to allow sidecar containers to interact with processes in other containers in the same pod + shareProcessNamespace: false + + # Service links might cause issue if running in a namespace with a large amount of services + # that might cause a slow startup when plugins are copied from ref to volume + # Set to true to keep previous behavior + # See https://github.com/kubernetes/kubernetes/issues/121787 + enableServiceLinks: false + + # Overrides the init container default values + # -- Resources allocation (Requests and Limits) for Init Container + initContainerResources: {} + # initContainerResources: + # requests: + # cpu: "50m" + # memory: "256Mi" + # limits: + # cpu: "2000m" + # memory: "4096Mi" + # -- Environment variable sources for Init Container + initContainerEnvFrom: [] + + # useful for i.e., http_proxy + # -- Environment variables for Init Container + initContainerEnv: [] + # initContainerEnv: + # - name: http_proxy + # value: "http://192.168.64.1:3128" + + # -- Environment variable sources for Jenkins Container + containerEnvFrom: [] + + # -- Environment variables for Jenkins Container + containerEnv: [] + # - name: http_proxy + # value: "http://192.168.64.1:3128" + + # Set min/max heap here if needed with "-Xms512m -Xmx512m" + # -- Append to `JAVA_OPTS` env var + javaOpts: + # -- Append to `JENKINS_OPTS` env var + jenkinsOpts: + + # If you are using the ingress definitions provided by this chart via the `controller.ingress` block, + # the configured hostname will be the ingress hostname starting with `https://` + # or `http://` depending on the `tls` configuration. + # The Protocol can be overwritten by specifying `controller.jenkinsUrlProtocol`. + # -- Set protocol for Jenkins URL; `https` if `controller.ingress.tls`, `http` otherwise + jenkinsUrlProtocol: + + # -- Set Jenkins URL if you are not using the ingress definitions provided by the chart + jenkinsUrl: + + # If you set this prefix and use ingress controller, then you might want to set the ingress path below + # I.e., "/jenkins" + # -- Root URI Jenkins will be served on + jenkinsUriPrefix: + + # -- Enable pod security context (must be `true` if podSecurityContextOverride, runAsUser or fsGroup are set) + usePodSecurityContext: true + + # Note that `runAsUser`, `fsGroup`, and `securityContextCapabilities` are + # being deprecated and replaced by `podSecurityContextOverride`. + # Set runAsUser to 1000 to let Jenkins run as non-root user 'jenkins', which exists in 'jenkins/jenkins' docker image. + # When configuring runAsUser to a different value than 0 also set fsGroup to the same value: + # -- Deprecated in favor of `controller.podSecurityContextOverride`. uid that jenkins runs with. + runAsUser: 1000 + + # -- Deprecated in favor of `controller.podSecurityContextOverride`. uid that will be used for persistent volume. + fsGroup: 1000 + + # If you have PodSecurityPolicies that require dropping of capabilities as suggested by CIS K8s benchmark, put them here + # securityContextCapabilities: + # drop: + # - NET_RAW + securityContextCapabilities: {} + + # In the case of mounting an ext4 filesystem, it might be desirable to use `supplementalGroups` instead of `fsGroup` in + # the `securityContext` block: https://github.com/kubernetes/kubernetes/issues/67014#issuecomment-589915496 + # podSecurityContextOverride: + # runAsUser: 1000 + # runAsNonRoot: true + # supplementalGroups: [1000] + # capabilities: {} + # -- Completely overwrites the contents of the pod security context, ignoring the values provided for `runAsUser`, `fsGroup`, and `securityContextCapabilities` + podSecurityContextOverride: ~ + + # -- Allow controlling the securityContext for the jenkins container + containerSecurityContext: + runAsUser: 1000 + runAsGroup: 1000 + readOnlyRootFilesystem: true + allowPrivilegeEscalation: false + + # For minikube, set this to NodePort, elsewhere uses LoadBalancer + # Use ClusterIP if your setup includes ingress controller + # -- k8s service type + serviceType: ClusterIP + + # -- k8s service clusterIP. Only used if serviceType is ClusterIP + clusterIp: + # -- k8s service port + servicePort: 8080 + # -- k8s target port + targetPort: 8080 + # -- k8s node port. Only used if serviceType is NodePort + nodePort: + + # Use Local to preserve the client source IP and avoids a second hop for LoadBalancer and NodePort type services, + # but risks potentially imbalanced traffic spreading. + serviceExternalTrafficPolicy: + + # -- Jenkins controller service annotations + serviceAnnotations: {} + # -- Jenkins controller custom labels for the StatefulSet + statefulSetLabels: {} + # foo: bar + # bar: foo + # -- Labels for the Jenkins controller-service + serviceLabels: {} + # service.beta.kubernetes.io/aws-load-balancer-backend-protocol: https + + # Put labels on Jenkins controller pod + # -- Custom Pod labels (an object with `label-key: label-value` pairs) + podLabels: {} + + # Enable Kubernetes Startup, Liveness and Readiness Probes + # if Startup Probe is supported, enable it too + # ~ 2 minutes to allow Jenkins to restart when upgrading plugins. Set ReadinessTimeout to be shorter than LivenessTimeout. + # ref: https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes + # -- Enable Kubernetes Probes configuration configured in `controller.probes` + healthProbes: true + + probes: + startupProbe: + # -- Set the failure threshold for the startup probe + failureThreshold: 12 + httpGet: + # -- Set the Pod's HTTP path for the startup probe + path: '{{ default "" .Values.controller.jenkinsUriPrefix }}/login' + # -- Set the Pod's HTTP port to use for the startup probe + port: http + # -- Set the time interval between two startup probes executions in seconds + periodSeconds: 10 + # -- Set the timeout for the startup probe in seconds + timeoutSeconds: 5 + + livenessProbe: + # -- Set the failure threshold for the liveness probe + failureThreshold: 5 + httpGet: + # -- Set the Pod's HTTP path for the liveness probe + path: '{{ default "" .Values.controller.jenkinsUriPrefix }}/login' + # -- Set the Pod's HTTP port to use for the liveness probe + port: http + # -- Set the time interval between two liveness probes executions in seconds + periodSeconds: 10 + # -- Set the timeout for the liveness probe in seconds + timeoutSeconds: 5 + + # If Startup Probe is not supported on your Kubernetes cluster, you might want to use "initialDelaySeconds" instead. + # It delays the initial liveness probe while Jenkins is starting + # -- Set the initial delay for the liveness probe in seconds + initialDelaySeconds: + + readinessProbe: + # -- Set the failure threshold for the readiness probe + failureThreshold: 3 + httpGet: + # -- Set the Pod's HTTP path for the liveness probe + path: '{{ default "" .Values.controller.jenkinsUriPrefix }}/login' + # -- Set the Pod's HTTP port to use for the readiness probe + port: http + # -- Set the time interval between two readiness probes executions in seconds + periodSeconds: 10 + # -- Set the timeout for the readiness probe in seconds + timeoutSeconds: 5 + + # If Startup Probe is not supported on your Kubernetes cluster, you might want to use "initialDelaySeconds" instead. + # It delays the initial readiness probe while Jenkins is starting + # -- Set the initial delay for the readiness probe in seconds + initialDelaySeconds: + + # PodDisruptionBudget config + podDisruptionBudget: + # ref: https://kubernetes.io/docs/tasks/run-application/configure-pdb/ + + # -- Enable Kubernetes Pod Disruption Budget configuration + enabled: false + + # For Kubernetes v1.5+, use 'policy/v1beta1' + # For Kubernetes v1.21+, use 'policy/v1' + # -- Policy API version + apiVersion: "policy/v1beta1" + + annotations: {} + labels: {} + # -- Number of pods that can be unavailable. Either an absolute number or a percentage + maxUnavailable: "0" + + # -- Create Agent listener service + agentListenerEnabled: true + # -- Listening port for agents + agentListenerPort: 50000 + # -- Host port to listen for agents + agentListenerHostPort: + # -- Node port to listen for agents + agentListenerNodePort: + + # ref: https://kubernetes.io/docs/concepts/services-networking/service/#traffic-policies + # -- Traffic Policy of for the agentListener service + agentListenerExternalTrafficPolicy: + # -- Allowed inbound IP for the agentListener service + agentListenerLoadBalancerSourceRanges: + - 0.0.0.0/0 + # -- Disabled agent protocols + disabledAgentProtocols: + - JNLP-connect + - JNLP2-connect + csrf: + defaultCrumbIssuer: + # -- Enable the default CSRF Crumb issuer + enabled: true + # -- Enable proxy compatibility + proxyCompatability: true + + # Kubernetes service type for the JNLP agent service + # agentListenerServiceType is the Kubernetes Service type for the JNLP agent service, + # either 'LoadBalancer', 'NodePort', or 'ClusterIP' + # Note if you set this to 'LoadBalancer', you *must* define annotations to secure it. By default, + # this will be an external load balancer and allowing inbound 0.0.0.0/0, a HUGE + # security risk: https://github.com/kubernetes/charts/issues/1341 + # -- Defines how to expose the agentListener service + agentListenerServiceType: "ClusterIP" + + # -- Annotations for the agentListener service + agentListenerServiceAnnotations: {} + + # Optionally, assign an IP to the LoadBalancer agentListenerService LoadBalancer + # GKE users: only regional static IPs will work for Service Load balancer. + # -- Static IP for the agentListener LoadBalancer + agentListenerLoadBalancerIP: + + # -- Whether legacy remoting security should be enabled + legacyRemotingSecurityEnabled: false + + # Example of a 'LoadBalancer'-type agent listener with annotations securing it + # agentListenerServiceType: LoadBalancer + # agentListenerServiceAnnotations: + # service.beta.kubernetes.io/aws-load-balancer-internal: "True" + # service.beta.kubernetes.io/load-balancer-source-ranges: "172.0.0.0/8, 10.0.0.0/8" + + # LoadBalancerSourcesRange is a list of allowed CIDR values, which are combined with ServicePort to + # set allowed inbound rules on the security group assigned to the controller load balancer + # -- Allowed inbound IP addresses + loadBalancerSourceRanges: + - 0.0.0.0/0 + + # -- Optionally assign a known public LB IP + loadBalancerIP: + + # Optionally configure a JMX port. This requires additional javaOpts, for example, + # javaOpts: > + # -Dcom.sun.management.jmxremote.port=4000 + # -Dcom.sun.management.jmxremote.authenticate=false + # -Dcom.sun.management.jmxremote.ssl=false + # jmxPort: 4000 + # -- Open a port, for JMX stats + jmxPort: + + # -- Optionally configure other ports to expose in the controller container + extraPorts: [] + # - name: BuildInfoProxy + # port: 9000 + # targetPort: 9010 (Optional: Use to explicitly set targetPort if different from port) + + # Plugins will be installed during Jenkins controller start + # -- List of Jenkins plugins to install. If you don't want to install plugins, set it to `false` + installPlugins: + - kubernetes:4288.v1719f9d0c854 + - workflow-aggregator:600.vb_57cdd26fdd7 + - git:5.4.1 + - configuration-as-code:1850.va_a_8c31d3158b_ + + # If set to false, Jenkins will download the minimum required version of all dependencies. + # -- Download the minimum required version or latest version of all dependencies + installLatestPlugins: true + + # -- Set to true to download the latest version of any plugin that is requested to have the latest version + installLatestSpecifiedPlugins: false + + # -- List of plugins to install in addition to those listed in controller.installPlugins + additionalPlugins: [] + + # Without this; whenever the controller gets restarted (Evicted, etc.) it will fetch plugin updates that have the potential to cause breakage. + # Note that for this to work, `persistence.enabled` needs to be set to `true` + # -- Initialize only on first installation. Ensures plugins do not get updated inadvertently. Requires `persistence.enabled` to be set to `true` + initializeOnce: false + + # Enable to always override the installed plugins with the values of 'controller.installPlugins' on upgrade or redeployment. + # -- Overwrite installed plugins on start + overwritePlugins: false + + # Configures if plugins bundled with `controller.image` should be overwritten with the values of 'controller.installPlugins' on upgrade or redeployment. + # -- Overwrite plugins that are already installed in the controller image + overwritePluginsFromImage: true + + # Configures the restrictions for naming projects. Set this key to null or empty to skip it in the default config. + projectNamingStrategy: standard + + # Useful with ghprb plugin. The OWASP plugin is not installed by default, please update controller.installPlugins. + # -- Enable HTML parsing using OWASP Markup Formatter Plugin (antisamy-markup-formatter) + enableRawHtmlMarkupFormatter: false + + # This is ignored if enableRawHtmlMarkupFormatter is true + # -- Yaml of the markup formatter to use + markupFormatter: plainText + + # Used to approve a list of groovy functions in pipelines used the script-security plugin. Can be viewed under /scriptApproval + # -- List of groovy functions to approve + scriptApproval: [] + # - "method groovy.json.JsonSlurperClassic parseText java.lang.String" + # - "new groovy.json.JsonSlurperClassic" + + # -- Map of groovy init scripts to be executed during Jenkins controller start + initScripts: {} + # test: |- + # print 'adding global pipeline libraries, register properties, bootstrap jobs...' + # -- Name of the existing ConfigMap that contains init scripts + initConfigMap: + + # 'name' is a name of an existing secret in the same namespace as jenkins, + # 'keyName' is the name of one of the keys inside the current secret. + # the 'name' and 'keyName' are concatenated with a '-' in between, so for example: + # an existing secret "secret-credentials" and a key inside it named "github-password" should be used in JCasC as ${secret-credentials-github-password} + # 'name' and 'keyName' must be lowercase RFC 1123 label must consist of lower case alphanumeric characters or '-', + # and must start and end with an alphanumeric character (e.g. 'my-name', or '123-abc') + # existingSecret existing secret "secret-credentials" and a key inside it named "github-username" should be used in JCasC as ${github-username} + # When using existingSecret no need to specify the keyName under additionalExistingSecrets. + existingSecret: + + # -- List of additional existing secrets to mount + additionalExistingSecrets: [] + # ref: https://github.com/jenkinsci/configuration-as-code-plugin/blob/master/docs/features/secrets.adoc#kubernetes-secrets + # additionalExistingSecrets: + # - name: secret-name-1 + # keyName: username + # - name: secret-name-1 + # keyName: password + + # -- List of additional secrets to create and mount + additionalSecrets: [] + # ref: https://github.com/jenkinsci/configuration-as-code-plugin/blob/master/docs/features/secrets.adoc#kubernetes-secrets + # additionalSecrets: + # - name: nameOfSecret + # value: secretText + + # Generate SecretClaim resources to create Kubernetes secrets from HashiCorp Vault using kube-vault-controller. + # 'name' is the name of the secret that will be created in Kubernetes. The Jenkins fullname is prepended to this value. + # 'path' is the fully qualified path to the secret in Vault + # 'type' is an optional Kubernetes secret type. The default is 'Opaque' + # 'renew' is an optional secret renewal time in seconds + # -- List of `SecretClaim` resources to create + secretClaims: [] + # - name: secretName # required + # path: testPath # required + # type: kubernetes.io/tls # optional + # renew: 60 # optional + + # -- Name of default cloud configuration. + cloudName: "kubernetes" + + # Below is the implementation of Jenkins Configuration as Code. Add a key under configScripts for each configuration area, + # where each corresponds to a plugin or section of the UI. Each key (prior to | character) is just a label, and can be any value. + # Keys are only used to give the section a meaningful name. The only restriction is they may only contain RFC 1123 \ DNS label + # characters: lowercase letters, numbers, and hyphens. The keys become the name of a configuration yaml file on the controller in + # /var/jenkins_home/casc_configs (by default) and will be processed by the Configuration as Code Plugin. The lines after each | + # become the content of the configuration yaml file. The first line after this is a JCasC root element, e.g., jenkins, credentials, + # etc. Best reference is https:///configuration-as-code/reference. The example below creates a welcome message: + JCasC: + # -- Enables default Jenkins configuration via configuration as code plugin + defaultConfig: true + + # If true, the init container deletes all the plugin config files and Jenkins Config as Code overwrites any existing configuration + # -- Whether Jenkins Config as Code should overwrite any existing configuration + overwriteConfiguration: false + # -- Remote URLs for configuration files. + configUrls: [] + # - https://acme.org/jenkins.yaml + # -- List of Jenkins Config as Code scripts + configScripts: {} + # welcome-message: | + # jenkins: + # systemMessage: Welcome to our CI\CD server. This Jenkins is configured and managed 'as code'. + + # Allows adding to the top-level security JCasC section. For legacy purposes, by default, the chart includes apiToken configurations + # -- Jenkins Config as Code security-section + security: + apiToken: + creationOfLegacyTokenEnabled: false + tokenGenerationOnCreationEnabled: false + usageStatisticsEnabled: true + + # Ignored if securityRealm is defined in controller.JCasC.configScripts + # -- Jenkins Config as Code Security Realm-section + securityRealm: |- + local: + allowsSignup: false + enableCaptcha: false + users: + - id: "${chart-admin-username}" + name: "Jenkins Admin" + password: "${chart-admin-password}" + + # Ignored if authorizationStrategy is defined in controller.JCasC.configScripts + # -- Jenkins Config as Code Authorization Strategy-section + authorizationStrategy: |- + loggedInUsersCanDoAnything: + allowAnonymousRead: false + + # -- Annotations for the JCasC ConfigMap + configMapAnnotations: {} + + # -- Custom init-container specification in raw-yaml format + customInitContainers: [] + # - name: custom-init + # image: "alpine:3" + # imagePullPolicy: Always + # command: [ "uname", "-a" ] + + sidecars: + configAutoReload: + # If enabled: true, Jenkins Configuration as Code will be reloaded on-the-fly without a reboot. + # If false or not-specified, JCasC changes will cause a reboot and will only be applied at the subsequent start-up. + # Auto-reload uses the http:///reload-configuration-as-code endpoint to reapply config when changes to + # the configScripts are detected. + # -- Enables Jenkins Config as Code auto-reload + enabled: true + image: + # -- Registry for the image that triggers the reload + registry: docker.io + # -- Repository of the image that triggers the reload + repository: kiwigrid/k8s-sidecar + # -- Tag for the image that triggers the reload + tag: 1.27.6 + imagePullPolicy: IfNotPresent + resources: {} + # limits: + # cpu: 100m + # memory: 100Mi + # requests: + # cpu: 50m + # memory: 50Mi + # -- Enables additional volume mounts for the config auto-reload container + additionalVolumeMounts: [] + # - name: auto-reload-config + # mountPath: /var/config/logger + # - name: auto-reload-logs + # mountPath: /var/log/auto_reload + # -- Config auto-reload logging settings + logging: + # See default settings https://github.com/kiwigrid/k8s-sidecar/blob/master/src/logger.py + configuration: + # -- Enables custom log config utilizing using the settings below. + override: false + logLevel: INFO + formatter: JSON + logToConsole: true + logToFile: false + maxBytes: 1024 + backupCount: 3 + + # -- The scheme to use when connecting to the Jenkins configuration as code endpoint + scheme: http + # -- Skip TLS verification when connecting to the Jenkins configuration as code endpoint + skipTlsVerify: false + + # -- How many connection-related errors to retry on + reqRetryConnect: 10 + # -- How many seconds to wait before updating config-maps/secrets (sets METHOD=SLEEP on the sidecar) + sleepTime: + + # -- Environment variable sources for the Jenkins Config as Code auto-reload container + envFrom: [] + # -- Environment variables for the Jenkins Config as Code auto-reload container + env: {} + # - name: REQ_TIMEOUT + # value: "30" + + # SSH port value can be set to any unused TCP port. The default, 1044, is a non-standard SSH port that has been chosen at random. + # This is only used to reload JCasC config from the sidecar container running in the Jenkins controller pod. + # This TCP port will not be open in the pod (unless you specifically configure this), so Jenkins will not be + # accessible via SSH from outside the pod. Note if you use non-root pod privileges (runAsUser & fsGroup), + # this must be > 1024: + sshTcpPort: 1044 + # folder in the pod that should hold the collected dashboards: + folder: "/var/jenkins_home/casc_configs" + + # If specified, the sidecar will search for JCasC config-maps inside this namespace. + # Otherwise, the namespace in which the sidecar is running will be used. + # It's also possible to specify ALL to search in all namespaces: + # searchNamespace: + # -- Enable container security context + containerSecurityContext: + readOnlyRootFilesystem: true + allowPrivilegeEscalation: false + + # -- Configures additional sidecar container(s) for the Jenkins controller + additionalSidecarContainers: [] + ## The example below runs the client for https://smee.io as sidecar container next to Jenkins, + ## that allows triggering build behind a secure firewall. + ## https://jenkins.io/blog/2019/01/07/webhook-firewalls/#triggering-builds-with-webhooks-behind-a-secure-firewall + ## + ## Note: To use it you should go to https://smee.io/new and update the url to the generated one. + # - name: smee + # image: docker.io/twalter/smee-client:1.0.2 + # args: ["--port", "{{ .Values.controller.servicePort }}", "--path", "/github-webhook/", "--url", "https://smee.io/new"] + # resources: + # limits: + # cpu: 50m + # memory: 128Mi + # requests: + # cpu: 10m + # memory: 32Mi + + # -- Name of the Kubernetes scheduler to use + schedulerName: "" + + # ref: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#nodeselector + # -- Node labels for pod assignment + nodeSelector: {} + + # ref: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#taints-and-tolerations-beta-feature + # -- Toleration labels for pod assignment + tolerations: [] + # -- Set TerminationGracePeriodSeconds + terminationGracePeriodSeconds: + # -- Set the termination message path + terminationMessagePath: + # -- Set the termination message policy + terminationMessagePolicy: + + # -- Affinity settings + affinity: {} + + # Leverage a priorityClass to ensure your pods survive resource shortages + # ref: https://kubernetes.io/docs/concepts/configuration/pod-priority-preemption/ + # -- The name of a `priorityClass` to apply to the controller pod + priorityClassName: + + # -- Annotations for controller pod + podAnnotations: {} + # -- Annotations for controller StatefulSet + statefulSetAnnotations: {} + + # ref: https://kubernetes.io/docs/concepts/workloads/controllers/statefulset/#update-strategies + # -- Update strategy for StatefulSet + updateStrategy: {} + + # -- Topology spread constraints + topologySpreadConstraints: {} + + ingress: + # -- Enables ingress + enabled: false + + # Override for the default paths that map requests to the backend + # -- Override for the default Ingress paths + paths: [] + # - backend: + # serviceName: ssl-redirect + # servicePort: use-annotation + # - backend: + # serviceName: >- + # {{ template "jenkins.fullname" . }} + # # Don't use string here, use only integer value! + # servicePort: 8080 + + # For Kubernetes v1.14+, use 'networking.k8s.io/v1beta1' + # For Kubernetes v1.19+, use 'networking.k8s.io/v1' + # -- Ingress API version + apiVersion: "extensions/v1beta1" + # -- Ingress labels + labels: {} + # -- Ingress annotations + annotations: {} + # kubernetes.io/ingress.class: nginx + # kubernetes.io/tls-acme: "true" + # 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 + + # Set this path to jenkinsUriPrefix above or use annotations to rewrite path + # -- Ingress path + path: + + # configures the hostname e.g. jenkins.example.com + # -- Ingress hostname + hostName: + # -- Hostname to serve assets from + resourceRootUrl: + # -- Ingress TLS configuration + tls: [] + # - secretName: jenkins.cluster.local + # hosts: + # - jenkins.cluster.local + + # often you want to have your controller all locked down and private, + # but you still want to get webhooks from your SCM + # A secondary ingress will let you expose different urls + # with a different configuration + secondaryingress: + enabled: false + # paths you want forwarded to the backend + # ex /github-webhook + paths: [] + # For Kubernetes v1.14+, use 'networking.k8s.io/v1beta1' + # For Kubernetes v1.19+, use 'networking.k8s.io/v1' + apiVersion: "extensions/v1beta1" + labels: {} + annotations: {} + # kubernetes.io/ingress.class: nginx + # kubernetes.io/tls-acme: "true" + # 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 + # configures the hostname e.g., jenkins-external.example.com + hostName: + tls: + # - secretName: jenkins-external.example.com + # hosts: + # - jenkins-external.example.com + + # If you're running on GKE and need to configure a backendconfig + # to finish ingress setup, use the following values. + # Docs: https://cloud.google.com/kubernetes-engine/docs/concepts/backendconfig + backendconfig: + # -- Enables backendconfig + enabled: false + # -- backendconfig API version + apiVersion: "extensions/v1beta1" + # -- backendconfig name + name: + # -- backendconfig labels + labels: {} + # -- backendconfig annotations + annotations: {} + # -- backendconfig spec + spec: {} + + # Openshift route + route: + # -- Enables openshift route + enabled: false + # -- Route labels + labels: {} + # -- Route annotations + annotations: {} + # -- Route path + path: + + # -- Allows for adding entries to Pod /etc/hosts + hostAliases: [] + # ref: https://kubernetes.io/docs/concepts/services-networking/add-entries-to-pod-etc-hosts-with-host-aliases/ + # hostAliases: + # - ip: 192.168.50.50 + # hostnames: + # - something.local + # - ip: 10.0.50.50 + # hostnames: + # - other.local + + # Expose Prometheus metrics + prometheus: + # If enabled, add the prometheus plugin to the list of plugins to install + # https://plugins.jenkins.io/prometheus + + # -- Enables prometheus service monitor + enabled: false + # -- Additional labels to add to the service monitor object + serviceMonitorAdditionalLabels: {} + # -- Set a custom namespace where to deploy ServiceMonitor resource + serviceMonitorNamespace: + # -- How often prometheus should scrape metrics + scrapeInterval: 60s + + # Defaults to the default endpoint used by the prometheus plugin + # -- The endpoint prometheus should get metrics from + scrapeEndpoint: /prometheus + + # See here: https://prometheus.io/docs/prometheus/latest/configuration/alerting_rules/ + # The `groups` root object is added by default, add the rule entries + # -- Array of prometheus alerting rules + alertingrules: [] + # -- Additional labels to add to the PrometheusRule object + alertingRulesAdditionalLabels: {} + # -- Set a custom namespace where to deploy PrometheusRule resource + prometheusRuleNamespace: "" + + # 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: [] + + googlePodMonitor: + # If enabled, It creates Google Managed Prometheus scraping config + enabled: false + # Set a custom namespace where to deploy PodMonitoring resource + # serviceMonitorNamespace: "" + scrapeInterval: 60s + # This is the default endpoint used by the prometheus plugin + scrapeEndpoint: /prometheus + + # -- Can be used to disable rendering controller test resources when using helm template + testEnabled: true + + httpsKeyStore: + # -- Enables HTTPS keystore on jenkins controller + enable: false + # -- Name of the secret that already has ssl keystore + jenkinsHttpsJksSecretName: "" + # -- Name of the key in the secret that already has ssl keystore + jenkinsHttpsJksSecretKey: "jenkins-jks-file" + # -- Name of the secret that contains the JKS password, if it is not in the same secret as the JKS file + jenkinsHttpsJksPasswordSecretName: "" + # -- Name of the key in the secret that contains the JKS password + jenkinsHttpsJksPasswordSecretKey: "https-jks-password" + disableSecretMount: false + + # When HTTPS keystore is enabled, servicePort and targetPort will be used as HTTPS port + # -- HTTP Port that Jenkins should listen to along with HTTPS, it also serves as the liveness and readiness probes port. + httpPort: 8081 + # -- Path of HTTPS keystore file + path: "/var/jenkins_keystore" + # -- Jenkins keystore filename which will appear under controller.httpsKeyStore.path + fileName: "keystore.jks" + # -- Jenkins keystore password + password: "password" + + # -- Base64 encoded Keystore content. Keystore must be converted to base64 then being pasted here + jenkinsKeyStoreBase64Encoded: + # Convert keystore.jks files content to base64 > $ cat keystore.jks | base64 +# /u3+7QAAAAIAAAABAAAAAQANamVua2luc2NpLmNvbQAAAW2r/b1ZAAAFATCCBP0wDgYKKwYBBAEq +# AhEBAQUABIIE6QbCqasvoHS0pSwYqSvdydMCB9t+VNfwhFIiiuAelJfO5sSe2SebJbtwHgLcRz1Z +# gMtWgOSFdl3bWSzA7vrW2LED52h+jXLYSWvZzuDuh8hYO85m10ikF6QR+dTi4jra0whIFDvq3pxe +# TnESxEsN+DvbZM3jA3qsjQJSeISNpDjO099dqQvHpnCn18lyk7J4TWJ8sOQQb1EM2zDAfAOSqA/x +# QuPEFl74DlY+5DIk6EBvpmWhaMSvXzWZACGA0sYqa157dq7O0AqmuLG/EI5EkHETO4CrtBW+yLcy +# 2dUCXOMA+j+NjM1BjrQkYE5vtSfNO6lFZcISyKo5pTFlcA7ut0Fx2nZ8GhHTn32CpeWwNcZBn1gR +# pZVt6DxVVkhTAkMLhR4rL2wGIi/1WRs23ZOLGKtyDNvDHnQyDiQEoJGy9nAthA8aNHa3cfdF10vB +# Drb19vtpFHmpvKEEhpk2EBRF4fTi644Fuhu2Ied6118AlaPvEea+n6G4vBz+8RWuVCmZjLU+7h8l +# Hy3/WdUPoIL5eW7Kz+hS+sRTFzfu9C48dMkQH3a6f3wSY+mufizNF9U298r98TnYy+PfDJK0bstG +# Ph6yPWx8DGXKQBwrhWJWXI6JwZDeC5Ny+l8p1SypTmAjpIaSW3ge+KgcL6Wtt1R5hUV1ajVwVSUi +# HF/FachKqPqyLJFZTGjNrxnmNYpt8P1d5JTvJfmfr55Su/P9n7kcyWp7zMcb2Q5nlXt4tWogOHLI +# OzEWKCacbFfVHE+PpdrcvCVZMDzFogIq5EqGTOZe2poPpBVE+1y9mf5+TXBegy5HToLWvmfmJNTO +# NCDuBjgLs2tdw2yMPm4YEr57PnMX5gGTC3f2ZihXCIJDCRCdQ9sVBOjIQbOCzxFXkVITo0BAZhCi +# Yz61wt3Ud8e//zhXWCkCsSV+IZCxxPzhEFd+RFVjW0Nm9hsb2FgAhkXCjsGROgoleYgaZJWvQaAg +# UyBzMmKDPKTllBHyE3Gy1ehBNGPgEBChf17/9M+j8pcm1OmlM434ctWQ4qW7RU56//yq1soFY0Te +# fu2ei03a6m68fYuW6s7XEEK58QisJWRAvEbpwu/eyqfs7PsQ+zSgJHyk2rO95IxdMtEESb2GRuoi +# Bs+AHNdYFTAi+GBWw9dvEgqQ0Mpv0//6bBE/Fb4d7b7f56uUNnnE7mFnjGmGQN+MvC62pfwfvJTT +# EkT1iZ9kjM9FprTFWXT4UmO3XTvesGeE50sV9YPm71X4DCQwc4KE8vyuwj0s6oMNAUACW2ClU9QQ +# y0tRpaF1tzs4N42Q5zl0TzWxbCCjAtC3u6xf+c8MCGrr7DzNhm42LOQiHTa4MwX4x96q7235oiAU +# iQqSI/hyF5yLpWw4etyUvsx2/0/0wkuTU1FozbLoCWJEWcPS7QadMrRRISxHf0YobIeQyz34regl +# t1qSQ3dCU9D6AHLgX6kqllx4X0fnFq7LtfN7fA2itW26v+kAT2QFZ3qZhINGfofCja/pITC1uNAZ +# gsJaTMcQ600krj/ynoxnjT+n1gmeqThac6/Mi3YlVeRtaxI2InL82ZuD+w/dfY9OpPssQjy3xiQa +# jPuaMWXRxz/sS9syOoGVH7XBwKrWpQcpchozWJt40QV5DslJkclcr8aC2AGlzuJMTdEgz1eqV0+H +# bAXG9HRHN/0eJTn1/QAAAAEABVguNTA5AAADjzCCA4swggJzAhRGqVxH4HTLYPGO4rzHcCPeGDKn +# xTANBgkqhkiG9w0BAQsFADCBgTELMAkGA1UEBhMCY2ExEDAOBgNVBAgMB29udGFyaW8xEDAOBgNV +# BAcMB3Rvcm9udG8xFDASBgNVBAoMC2plbmtpbnN0ZXN0MRkwFwYDVQQDDBBqZW5raW5zdGVzdC5p +# bmZvMR0wGwYJKoZIhvcNAQkBFg50ZXN0QHRlc3QuaW5mbzAeFw0xOTEwMDgxNTI5NTVaFw0xOTEx +# MDcxNTI5NTVaMIGBMQswCQYDVQQGEwJjYTEQMA4GA1UECAwHb250YXJpbzEQMA4GA1UEBwwHdG9y +# b250bzEUMBIGA1UECgwLamVua2luc3Rlc3QxGTAXBgNVBAMMEGplbmtpbnN0ZXN0LmluZm8xHTAb +# BgkqhkiG9w0BCQEWDnRlc3RAdGVzdC5pbmZvMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC +# AQEA02q352JTHGvROMBhSHvSv+vnoOTDKSTz2aLQn0tYrIRqRo+8bfmMjXuhkwZPSnCpvUGNAJ+w +# Jrt/dqMoYUjCBkjylD/qHmnXN5EwS1cMg1Djh65gi5JJLFJ7eNcoSsr/0AJ+TweIal1jJSP3t3PF +# 9Uv21gm6xdm7HnNK66WpUUXLDTKaIs/jtagVY1bLOo9oEVeLN4nT2CYWztpMvdCyEDUzgEdDbmrP +# F5nKUPK5hrFqo1Dc5rUI4ZshL3Lpv398aMxv6n2adQvuL++URMEbXXBhxOrT6rCtYzbcR5fkwS9i +# d3Br45CoWOQro02JAepoU0MQKY5+xQ4Bq9Q7tB9BAwIDAQABMA0GCSqGSIb3DQEBCwUAA4IBAQAe +# 4xc+mSvKkrKBHg9/zpkWgZUiOp4ENJCi8H4tea/PCM439v6y/kfjT/okOokFvX8N5aa1OSz2Vsrl +# m8kjIc6hiA7bKzT6lb0EyjUShFFZ5jmGVP4S7/hviDvgB5yEQxOPpumkdRP513YnEGj/o9Pazi5h +# /MwpRxxazoda9r45kqQpyG+XoM4pB+Fd3JzMc4FUGxfVPxJU4jLawnJJiZ3vqiSyaB0YyUL+Er1Q +# 6NnqtR4gEBF0ZVlQmkycFvD4EC2boP943dLqNUvop+4R3SM1QMM6P5u8iTXtHd/VN4MwMyy1wtog +# hYAzODo1Jt59pcqqKJEas0C/lFJEB3frw4ImNx5fNlJYOpx+ijfQs9m39CevDq0= + +agent: + # -- Enable Kubernetes plugin jnlp-agent podTemplate + enabled: true + # -- The name of the pod template to use for providing default values + defaultsProviderTemplate: "" + + # Useful for not including a serviceAccount in the template if `false` + # -- Use `serviceAccountAgent.name` as the default value for defaults template `serviceAccount` + useDefaultServiceAccount: true + + # -- Override the default service account + # @default -- `serviceAccountAgent.name` if `agent.useDefaultServiceAccount` is `true` + serviceAccount: + + # For connecting to the Jenkins controller + # -- Overrides the Kubernetes Jenkins URL + jenkinsUrl: + + # connects to the specified host and port, instead of connecting directly to the Jenkins controller + # -- Overrides the Kubernetes Jenkins tunnel + jenkinsTunnel: + # -- Disables the verification of the controller certificate on remote connection. This flag correspond to the "Disable https certificate check" flag in kubernetes plugin UI + skipTlsVerify: false + # -- Enable the possibility to restrict the usage of this agent to specific folder. This flag correspond to the "Restrict pipeline support to authorized folders" flag in kubernetes plugin UI + usageRestricted: false + # -- The connection timeout in seconds for connections to Kubernetes API. The minimum value is 5 + kubernetesConnectTimeout: 5 + # -- The read timeout in seconds for connections to Kubernetes API. The minimum value is 15 + kubernetesReadTimeout: 15 + # -- The maximum concurrent connections to Kubernetes API + maxRequestsPerHostStr: "32" + # -- Time in minutes after which the Kubernetes cloud plugin will clean up an idle worker that has not already terminated + retentionTimeout: 5 + # -- Seconds to wait for pod to be running + waitForPodSec: 600 + # -- Namespace in which the Kubernetes agents should be launched + namespace: + # -- Custom Pod labels (an object with `label-key: label-value` pairs) + podLabels: {} + # -- Custom registry used to pull the agent jnlp image from + jnlpregistry: + image: + # -- Repository to pull the agent jnlp image from + repository: "jenkins/inbound-agent" + # -- Tag of the image to pull + tag: "3261.v9c670a_4748a_9-1" + # -- Configure working directory for default agent + workingDir: "/home/jenkins/agent" + nodeUsageMode: "NORMAL" + # -- Append Jenkins labels to the agent + customJenkinsLabels: [] + # -- Name of the secret to be used to pull the image + imagePullSecretName: + componentName: "jenkins-agent" + # -- Enables agent communication via websockets + websocket: false + directConnection: false + # -- Agent privileged container + privileged: false + # -- Configure container user + runAsUser: + # -- Configure container group + runAsGroup: + # -- Enables the agent to use the host network + hostNetworking: false + # -- Resources allocation (Requests and Limits) + resources: + requests: + cpu: "512m" + memory: "512Mi" + # ephemeralStorage: + limits: + cpu: "512m" + memory: "512Mi" + # ephemeralStorage: + livenessProbe: {} +# execArgs: "cat /tmp/healthy" +# failureThreshold: 3 +# initialDelaySeconds: 0 +# periodSeconds: 10 +# successThreshold: 1 +# timeoutSeconds: 1 + + # You may want to change this to true while testing a new image + # -- Always pull agent container image before build + alwaysPullImage: false + # When using Pod Security Admission in the Agents namespace with the restricted Pod Security Standard, + # the jnlp container cannot be scheduled without overriding its container definition with a securityContext. + # This option allows to automatically inject in the jnlp container a securityContext + # that is suitable for the use of the restricted Pod Security Standard. + # -- Set a restricted securityContext on jnlp containers + restrictedPssSecurityContext: false + # Controls how agent pods are retained after the Jenkins build completes + # Possible values: Always, Never, OnFailure + podRetention: "Never" + # Disable if you do not want the Yaml the agent pod template to show up + # in the job Console Output. This can be helpful for either security reasons + # or simply to clean up the output to make it easier to read. + showRawYaml: true + + # You can define the volumes that you want to mount for this container + # Allowed types are: ConfigMap, EmptyDir, EphemeralVolume, HostPath, Nfs, PVC, Secret + # Configure the attributes as they appear in the corresponding Java class for that type + # https://github.com/jenkinsci/kubernetes-plugin/tree/master/src/main/java/org/csanchez/jenkins/plugins/kubernetes/volumes + # -- Additional volumes + volumes: [] + # - type: ConfigMap + # configMapName: myconfigmap + # mountPath: /var/myapp/myconfigmap + # - type: EmptyDir + # mountPath: /var/myapp/myemptydir + # memory: false + # - type: EphemeralVolume + # mountPath: /var/myapp/myephemeralvolume + # accessModes: ReadWriteOnce + # requestsSize: 10Gi + # storageClassName: mystorageclass + # - type: HostPath + # hostPath: /var/lib/containers + # mountPath: /var/myapp/myhostpath + # - type: Nfs + # mountPath: /var/myapp/mynfs + # readOnly: false + # serverAddress: "192.0.2.0" + # serverPath: /var/lib/containers + # - type: PVC + # claimName: mypvc + # mountPath: /var/myapp/mypvc + # readOnly: false + # - type: Secret + # defaultMode: "600" + # mountPath: /var/myapp/mysecret + # secretName: mysecret + # Pod-wide environment, these vars are visible to any container in the agent pod + + # You can define the workspaceVolume that you want to mount for this container + # Allowed types are: DynamicPVC, EmptyDir, EphemeralVolume, HostPath, Nfs, PVC + # Configure the attributes as they appear in the corresponding Java class for that type + # https://github.com/jenkinsci/kubernetes-plugin/tree/master/src/main/java/org/csanchez/jenkins/plugins/kubernetes/volumes/workspace + # -- Workspace volume (defaults to EmptyDir) + workspaceVolume: {} + ## DynamicPVC example + # - type: DynamicPVC + # configMapName: myconfigmap + ## EmptyDir example + # - type: EmptyDir + # memory: false + ## EphemeralVolume example + # - type: EphemeralVolume + # accessModes: ReadWriteOnce + # requestsSize: 10Gi + # storageClassName: mystorageclass + ## HostPath example + # - type: HostPath + # hostPath: /var/lib/containers + ## NFS example + # - type: Nfs + # readOnly: false + # serverAddress: "192.0.2.0" + # serverPath: /var/lib/containers + ## PVC example + # - type: PVC + # claimName: mypvc + # readOnly: false + + # Pod-wide environment, these vars are visible to any container in the agent pod + # -- Environment variables for the agent Pod + envVars: [] + # - name: PATH + # value: /usr/local/bin + # -- Mount a secret as environment variable + secretEnvVars: [] + # - key: PATH + # optional: false # default: false + # secretKey: MY-K8S-PATH + # secretName: my-k8s-secret + + # -- Node labels for pod assignment + nodeSelector: {} + # Key Value selectors. Ex: + # nodeSelector + # jenkins-agent: v1 + + # -- Command to execute when side container starts + command: + # -- Arguments passed to command to execute + args: "${computer.jnlpmac} ${computer.name}" + # -- Side container name + sideContainerName: "jnlp" + + # Doesn't allocate pseudo TTY by default + # -- Allocate pseudo tty to the side container + TTYEnabled: false + # -- Max number of agents to launch + containerCap: 10 + # -- Agent Pod base name + podName: "default" + + # Enables garbage collection of orphan pods for this Kubernetes cloud. (beta) + garbageCollection: + # -- When enabled, Jenkins will periodically check for orphan pods that have not been touched for the given timeout period and delete them. + enabled: false + # -- Namespaces to look at for garbage collection, in addition to the default namespace defined for the cloud. One namespace per line. + namespaces: "" + # namespaces: |- + # namespaceOne + # namespaceTwo + # -- Timeout value for orphaned pods + timeout: 300 + + # -- Allows the Pod to remain active for reuse until the configured number of minutes has passed since the last step was executed on it + idleMinutes: 0 + + + # The raw yaml of a Pod API Object, for example, this allows usage of toleration for agent pods. + # https://github.com/jenkinsci/kubernetes-plugin#using-yaml-to-define-pod-templates + # https://kubernetes.io/docs/concepts/configuration/taint-and-toleration/ + # -- The raw yaml of a Pod API Object to merge into the agent spec + yamlTemplate: "" + # yamlTemplate: |- + # apiVersion: v1 + # kind: Pod + # spec: + # tolerations: + # - key: "key" + # operator: "Equal" + # value: "value" + + # -- Defines how the raw yaml field gets merged with yaml definitions from inherited pod templates. Possible values: "merge" or "override" + yamlMergeStrategy: "override" + # -- Controls whether the defined yaml merge strategy will be inherited if another defined pod template is configured to inherit from the current one + inheritYamlMergeStrategy: false + # -- Timeout in seconds for an agent to be online + connectTimeout: 100 + # -- Annotations to apply to the pod + annotations: {} + + # Containers specified here are added to all agents. Set key empty to remove container from additional agents. + # -- Add additional containers to the agents + additionalContainers: [] + # - sideContainerName: dind + # image: + # repository: docker + # tag: dind + # command: dockerd-entrypoint.sh + # args: "" + # privileged: true + # resources: + # requests: + # cpu: 500m + # memory: 1Gi + # limits: + # cpu: 1 + # memory: 2Gi + + # Useful when configuring agents only with the podTemplates value, since the default podTemplate populated by values mentioned above will be excluded in the rendered template. + # -- Disable the default Jenkins Agent configuration + disableDefaultAgent: false + + # Below is the implementation of custom pod templates for the default configured kubernetes cloud. + # Add a key under podTemplates for each pod template. Each key (prior to | character) is just a label, and can be any value. + # Keys are only used to give the pod template a meaningful name. The only restriction is they may only contain RFC 1123 \ DNS label + # characters: lowercase letters, numbers, and hyphens. Each pod template can contain multiple containers. + # For this pod templates configuration to be loaded, the following values must be set: + # controller.JCasC.defaultConfig: true + # Best reference is https:///configuration-as-code/reference#Cloud-kubernetes. The example below creates a python pod template. + # -- Configures extra pod templates for the default kubernetes cloud + podTemplates: {} + # python: | + # - name: python + # label: jenkins-python + # serviceAccount: jenkins + # containers: + # - name: python + # image: python:3 + # command: "/bin/sh -c" + # args: "cat" + # ttyEnabled: true + # privileged: true + # resourceRequestCpu: "400m" + # resourceRequestMemory: "512Mi" + # resourceLimitCpu: "1" + # resourceLimitMemory: "1024Mi" + +# Inherits all values from `agent` so you only need to specify values which differ +# -- Configure additional +additionalAgents: {} +# maven: +# podName: maven +# customJenkinsLabels: maven +# # An example of overriding the jnlp container +# # sideContainerName: jnlp +# image: +# repository: jenkins/jnlp-agent-maven +# tag: latest +# python: +# podName: python +# customJenkinsLabels: python +# sideContainerName: python +# image: +# repository: python +# tag: "3" +# command: "/bin/sh -c" +# args: "cat" +# TTYEnabled: true + +# Here you can add additional clouds +# They inherit all values from the default cloud (including the main agent), so +# you only need to specify values which differ. If you want to override +# default additionalAgents with the additionalClouds.additionalAgents set +# additionalAgentsOverride to `true`. +additionalClouds: {} +# remote-cloud-1: +# kubernetesURL: https://api.remote-cloud.com +# additionalAgentsOverride: true +# additionalAgents: +# maven-2: +# podName: maven-2 +# customJenkinsLabels: maven +# # An example of overriding the jnlp container +# # sideContainerName: jnlp +# image: +# repository: jenkins/jnlp-agent-maven +# tag: latest +# namespace: my-other-maven-namespace +# remote-cloud-2: +# kubernetesURL: https://api.remote-cloud.com + +persistence: + # -- Enable the use of a Jenkins PVC + enabled: true + + # A manually managed Persistent Volume and Claim + # Requires persistence.enabled: true + # If defined, PVC must be created manually before volume will be bound + # -- Provide the name of a PVC + existingClaim: + + # jenkins 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) + # -- Storage class for the PVC + storageClass: + # -- Annotations for the PVC + annotations: {} + # -- Labels for the PVC + labels: {} + # -- The PVC access mode + accessMode: "ReadWriteOnce" + # -- The size of the PVC + size: "8Gi" + + # ref: https://kubernetes.io/docs/concepts/storage/volume-pvc-datasource/ + # -- Existing data source to clone PVC from + dataSource: {} + # name: PVC-NAME + # kind: PersistentVolumeClaim + + # -- SubPath for jenkins-home mount + subPath: + # -- Additional volumes + volumes: [] + # - name: nothing + # emptyDir: {} + + # -- Additional mounts + mounts: [] + # - mountPath: /var/nothing + # name: nothing + # readOnly: true + +networkPolicy: + # -- Enable the creation of NetworkPolicy resources + enabled: false + + # For Kubernetes v1.4, v1.5 and v1.6, use 'extensions/v1beta1' + # For Kubernetes v1.7, use 'networking.k8s.io/v1' + # -- NetworkPolicy ApiVersion + apiVersion: networking.k8s.io/v1 + # You can allow agents to connect from both within the cluster (from within specific/all namespaces) AND/OR from a given external IP range + internalAgents: + # -- Allow internal agents (from the same cluster) to connect to controller. Agent pods will be filtered based on PodLabels + allowed: true + # -- A map of labels (keys/values) that agent pods must have to be able to connect to controller + podLabels: {} + # -- A map of labels (keys/values) that agents namespaces must have to be able to connect to controller + namespaceLabels: {} + # project: myproject + externalAgents: + # -- The IP range from which external agents are allowed to connect to controller, i.e., 172.17.0.0/16 + ipCIDR: + # -- A list of IP sub-ranges to be excluded from the allowlisted IP range + except: [] + # - 172.17.1.0/24 + +## Install Default RBAC roles and bindings +rbac: + # -- Whether RBAC resources are created + create: true + # -- Whether the Jenkins service account should be able to read Kubernetes secrets + readSecrets: false + +serviceAccount: + # -- Configures if a ServiceAccount with this name should be created + create: true + + # The name of the ServiceAccount is autogenerated by default + # -- The name of the ServiceAccount to be used by access-controlled resources + name: + # -- Configures annotations for the ServiceAccount + annotations: {} + # -- Configures extra labels for the ServiceAccount + extraLabels: {} + # -- Controller ServiceAccount image pull secret + imagePullSecretName: + + +serviceAccountAgent: + # -- Configures if an agent ServiceAccount should be created + create: false + + # If not set and create is true, a name is generated using the fullname template + # -- The name of the agent ServiceAccount to be used by access-controlled resources + name: + # -- Configures annotations for the agent ServiceAccount + annotations: {} + # -- Configures extra labels for the agent ServiceAccount + extraLabels: {} + # -- Agent ServiceAccount image pull secret + imagePullSecretName: + +# -- Checks if any deprecated values are used +checkDeprecation: true + +awsSecurityGroupPolicies: + enabled: false + policies: + - name: "" + securityGroupIds: [] + podSelector: {} + +# Here you can configure unit tests values when executing the helm unittest in the CONTRIBUTING.md +helmtest: + # A testing framework for bash + bats: + # Bash Automated Testing System (BATS) + image: + # -- Registry of the image used to test the framework + registry: "docker.io" + # -- Repository of the image used to test the framework + repository: "bats/bats" + # -- Tag of the image to test the framework + tag: "1.11.0" diff --git a/charts/traefik/traefik/31.1.0/.helmignore b/charts/traefik/traefik/31.1.0/.helmignore new file mode 100644 index 000000000..9c42ddd90 --- /dev/null +++ b/charts/traefik/traefik/31.1.0/.helmignore @@ -0,0 +1,2 @@ +tests/ +crds/kustomization.yaml diff --git a/charts/traefik/traefik/31.1.0/Changelog.md b/charts/traefik/traefik/31.1.0/Changelog.md new file mode 100644 index 000000000..1d9b5d0b1 --- /dev/null +++ b/charts/traefik/traefik/31.1.0/Changelog.md @@ -0,0 +1,10084 @@ +# Change Log + +## 31.1.0 ![AppVersion: v3.1.3](https://img.shields.io/static/v1?label=AppVersion&message=v3.1.3&color=success&logo=) ![Kubernetes: >=1.22.0-0](https://img.shields.io/static/v1?label=Kubernetes&message=%3E%3D1.22.0-0&color=informational&logo=kubernetes) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2024-09-18 + +* fix: 🐛 update CRD to v3.1 +* feat: ✨ input validation using schema +* feat: ✨ add AllowACMEByPass and improve schema/doc on ports values +* feat: add new webhooks and removes unnecessary ones +* feat(deps): update traefik docker tag to v3.1.3 +* chore(release): 🚀 publish v31.1.0 + +### Default value changes + +```diff +diff --git a/traefik/values.yaml b/traefik/values.yaml +index 2232d9e..1b9d0fd 100644 +--- a/traefik/values.yaml ++++ b/traefik/values.yaml +@@ -2,13 +2,13 @@ + # This is a YAML-formatted file. + # Declare variables to be passed into templates + +-image: ++image: # @schema additionalProperties: false + # -- Traefik image host registry + registry: docker.io + # -- Traefik image repository + repository: traefik + # -- defaults to appVersion +- tag: ++ tag: # @schema type:[string, null] + # -- Traefik image pull policy + pullPolicy: IfNotPresent + +@@ -23,27 +23,27 @@ deployment: + # -- Number of pods of the deployment (only applies when kind == Deployment) + replicas: 1 + # -- Number of old history to retain to allow rollback (If not set, default Kubernetes value is set to 10) +- # revisionHistoryLimit: 1 ++ revisionHistoryLimit: # @schema type:[integer, null];minimum:0 + # -- Amount of time (in seconds) before Kubernetes will send the SIGKILL signal if Traefik does not shut down + terminationGracePeriodSeconds: 60 + # -- The minimum number of seconds Traefik needs to be up and running before the DaemonSet/Deployment controller considers it available + minReadySeconds: 0 +- ## Override the liveness/readiness port. This is useful to integrate traefik ++ ## -- Override the liveness/readiness port. This is useful to integrate traefik + ## with an external Load Balancer that performs healthchecks. + ## Default: ports.traefik.port +- # healthchecksPort: 9000 +- ## Override the liveness/readiness host. Useful for getting ping to respond on non-default entryPoint. ++ healthchecksPort: # @schema type:[integer, null];minimum:0 ++ ## -- Override the liveness/readiness host. Useful for getting ping to respond on non-default entryPoint. + ## Default: ports.traefik.hostIP if set, otherwise Pod IP +- # healthchecksHost: localhost +- ## Override the liveness/readiness scheme. Useful for getting ping to ++ healthchecksHost: "" ++ ## -- Override the liveness/readiness scheme. Useful for getting ping to + ## respond on websecure entryPoint. +- # healthchecksScheme: HTTPS +- ## Override the readiness path. ++ healthchecksScheme: # @schema enum:[HTTP, HTTPS, null]; type:[string, null]; default: HTTP ++ ## -- Override the readiness path. + ## Default: /ping +- # readinessPath: /ping +- # Override the liveness path. ++ readinessPath: "" ++ # -- Override the liveness path. + # Default: /ping +- # livenessPath: /ping ++ livenessPath: "" + # -- Additional deployment annotations (e.g. for jaeger-operator sidecar injection) + annotations: {} + # -- Additional deployment labels (e.g. for filtering deployment by custom labels) +@@ -80,7 +80,7 @@ deployment: + # -- Use process namespace sharing + shareProcessNamespace: false + # -- Custom pod DNS policy. Apply if `hostNetwork: true` +- # dnsPolicy: ClusterFirstWithHostNet ++ dnsPolicy: "" + # -- Custom pod [DNS config](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.30/#poddnsconfig-v1-core) + dnsConfig: {} + # -- Custom [host aliases](https://kubernetes.io/docs/tasks/network/customize-hosts-file-for-pods/) +@@ -99,24 +99,24 @@ deployment: + # host: localhost + # scheme: HTTP + # -- Set a runtimeClassName on pod +- runtimeClassName: ++ runtimeClassName: "" + + # -- [Pod Disruption Budget](https://kubernetes.io/docs/reference/kubernetes-api/policy-resources/pod-disruption-budget-v1/) +-podDisruptionBudget: +- enabled: +- maxUnavailable: +- minAvailable: ++podDisruptionBudget: # @schema additionalProperties: false ++ enabled: false ++ maxUnavailable: # @schema type:[string, integer, null];minimum:0 ++ minAvailable: # @schema type:[string, integer, null];minimum:0 + + # -- Create a default IngressClass for Traefik +-ingressClass: ++ingressClass: # @schema additionalProperties: false + enabled: true + isDefaultClass: true +- # name: my-custom-class ++ name: "" + +-core: ++core: # @schema additionalProperties: false + # -- Can be used to use globally v2 router syntax + # See https://doc.traefik.io/traefik/v3.0/migration/v2-to-v3/#new-v3-syntax-notable-changes +- defaultRuleSyntax: ++ defaultRuleSyntax: "" + + # Traefik experimental features + experimental: +@@ -133,11 +133,11 @@ gateway: + # -- When providers.kubernetesGateway.enabled, deploy a default gateway + enabled: true + # -- Set a custom name to gateway +- name: ++ name: "" + # -- By default, Gateway is created in the same `Namespace` than Traefik. +- namespace: ++ namespace: "" + # -- Additional gateway annotations (e.g. for cert-manager.io/issuer) +- annotations: ++ annotations: {} + # -- Define listeners + listeners: + web: +@@ -145,11 +145,11 @@ gateway: + # The port must match a port declared in ports section. + port: 8000 + # -- Optional hostname. See [Hostname](https://gateway-api.sigs.k8s.io/reference/spec/#gateway.networking.k8s.io/v1.Hostname) +- hostname: ++ hostname: "" + # Specify expected protocol on this listener. See [ProtocolType](https://gateway-api.sigs.k8s.io/reference/spec/#gateway.networking.k8s.io/v1.ProtocolType) + protocol: HTTP + # -- Routes are restricted to namespace of the gateway [by default](https://gateway-api.sigs.k8s.io/reference/spec/#gateway.networking.k8s.io/v1.FromNamespaces +- namespacePolicy: ++ namespacePolicy: # @schema type:[string, null] + # websecure listener is disabled by default because certificateRefs needs to be added, + # or you may specify TLS protocol with Passthrough mode and add "--providers.kubernetesGateway.experimentalChannel=true" in additionalArguments section. + # websecure: +@@ -167,13 +167,13 @@ gateway: + # # -- TLS behavior for the TLS session initiated by the client. See [TLSModeType](https://gateway-api.sigs.k8s.io/reference/spec/#gateway.networking.k8s.io/v1.TLSModeType). + # mode: + +-gatewayClass: ++gatewayClass: # @schema additionalProperties: false + # -- When providers.kubernetesGateway.enabled and gateway.enabled, deploy a default gatewayClass + enabled: true + # -- Set a custom name to GatewayClass +- name: ++ name: "" + # -- Additional gatewayClass labels (e.g. for filtering gateway objects by custom labels) +- labels: ++ labels: {} + + ingressRoute: + dashboard: +@@ -218,14 +218,14 @@ ingressRoute: + # -- TLS options (e.g. secret containing certificate) + tls: {} + +-updateStrategy: ++updateStrategy: # @schema additionalProperties: false + # -- Customize updateStrategy: RollingUpdate or OnDelete + type: RollingUpdate + rollingUpdate: +- maxUnavailable: 0 +- maxSurge: 1 ++ maxUnavailable: 0 # @schema type:[integer, string, null] ++ maxSurge: 1 # @schema type:[integer, string, null] + +-readinessProbe: ++readinessProbe: # @schema additionalProperties: false + # -- The number of consecutive failures allowed before considering the probe as failed. + failureThreshold: 1 + # -- The number of seconds to wait before starting the first probe. +@@ -236,7 +236,7 @@ readinessProbe: + successThreshold: 1 + # -- The number of seconds to wait for a probe response before considering it as failed. + timeoutSeconds: 2 +-livenessProbe: ++livenessProbe: # @schema additionalProperties: false + # -- The number of consecutive failures allowed before considering the probe as failed. + failureThreshold: 3 + # -- The number of seconds to wait before starting the first probe. +@@ -249,9 +249,9 @@ livenessProbe: + timeoutSeconds: 2 + + # -- Define [Startup Probe](https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/#define-startup-probes) +-startupProbe: ++startupProbe: {} + +-providers: ++providers: # @schema additionalProperties: false + kubernetesCRD: + # -- Load Kubernetes IngressRoute provider + enabled: true +@@ -262,12 +262,12 @@ providers: + # -- Allows to return 503 when there is no endpoints available + allowEmptyServices: true + # -- When the parameter is set, only resources containing an annotation with the same value are processed. Otherwise, resources missing the annotation, having an empty value, or the value traefik are processed. It will also set required annotation on Dashboard and Healthcheck IngressRoute when enabled. +- ingressClass: ++ ingressClass: "" + # labelSelector: environment=production,method=traefik + # -- Array of namespaces to watch. If left empty, Traefik watches all namespaces. + namespaces: [] + # -- Defines whether to use Native Kubernetes load-balancing mode by default. +- nativeLBByDefault: ++ nativeLBByDefault: false + + kubernetesIngress: + # -- Load Kubernetes Ingress provider +@@ -277,7 +277,7 @@ providers: + # -- Allows to return 503 when there is no endpoints available + allowEmptyServices: true + # -- When ingressClass is set, only Ingresses containing an annotation with the same value are processed. Otherwise, Ingresses missing the annotation, having an empty value, or the value traefik are processed. +- ingressClass: ++ ingressClass: # @schema type:[string, null] + # labelSelector: environment=production,method=traefik + # -- Array of namespaces to watch. If left empty, Traefik watches all namespaces. + namespaces: [] +@@ -288,7 +288,7 @@ providers: + # By default this Traefik service + # pathOverride: "" + # -- Defines whether to use Native Kubernetes load-balancing mode by default. +- nativeLBByDefault: ++ nativeLBByDefault: false + + kubernetesGateway: + # -- Enable Traefik Gateway provider for Gateway API +@@ -299,7 +299,7 @@ providers: + # -- Array of namespaces to watch. If left empty, Traefik watches all namespaces. + namespaces: [] + # -- A label selector can be defined to filter on specific GatewayClass objects only. +- labelselector: ++ labelselector: "" + + file: + # -- Create a file provider +@@ -307,7 +307,7 @@ providers: + # -- Allows Traefik to automatically watch for file changes + watch: true + # -- File content (YAML format, go template supported) (see https://doc.traefik.io/traefik/providers/file/) +- content: ++ content: "" + + # -- Add volumes to the traefik pod. The volume name will be passed to tpl. + # This can be used to mount a cert pair or a configmap that holds a config.toml file. +@@ -333,90 +333,88 @@ additionalVolumeMounts: [] + logs: + general: + # -- Set [logs format](https://doc.traefik.io/traefik/observability/logs/#format) +- # @default common +- format: ++ format: # @schema enum:["common", "json", null]; type:[string, null]; default: "common" + # By default, the level is set to INFO. + # -- Alternative logging levels are DEBUG, PANIC, FATAL, ERROR, WARN, and INFO. +- level: INFO +- # +- # filePath: "/var/log/traefik/traefik.log +- # noColor: true ++ level: "INFO" # @schema enum:[INFO,WARN,ERROR,FATAL,PANIC,DEBUG]; default: "INFO" ++ # -- To write the logs into a log file, use the filePath option. ++ filePath: "" ++ # -- When set to true and format is common, it disables the colorized output. ++ noColor: false + access: + # -- To enable access logs + enabled: false + # -- Set [access log format](https://doc.traefik.io/traefik/observability/access-logs/#format) +- format: ++ format: # @schema enum:["CLF", "json", null]; type:[string, null]; default: "CLF" + # filePath: "/var/log/traefik/access.log + # -- Set [bufferingSize](https://doc.traefik.io/traefik/observability/access-logs/#bufferingsize) +- bufferingSize: ++ bufferingSize: # @schema type:[integer, null] + # -- Set [filtering](https://docs.traefik.io/observability/access-logs/#filtering) + filters: {} +- # statuscodes: "200,300-302" +- # retryattempts: true +- # minduration: 10ms ++ statuscodes: "" ++ retryattempts: false ++ minduration: "" + # -- Enables accessLogs for internal resources. Default: false. +- addInternals: ++ addInternals: false + fields: + general: +- # -- Available modes: keep, drop, redact. +- defaultmode: keep ++ # -- Set default mode for fields.names ++ defaultmode: keep # @schema enum:[keep, drop, redact]; default: keep + # -- Names of the fields to limit. + names: {} +- ## Examples: +- # ClientUsername: drop + # -- [Limit logged fields or headers](https://doc.traefik.io/traefik/observability/access-logs/#limiting-the-fieldsincluding-headers) + headers: +- # -- Available modes: keep, drop, redact. +- defaultmode: drop ++ # -- Set default mode for fields.headers ++ defaultmode: drop # @schema enum:[keep, drop, redact]; default: drop + names: {} + + metrics: + ## -- Enable metrics for internal resources. Default: false +- addInternals: ++ addInternals: false + + ## -- Prometheus is enabled by default. + ## -- It can be disabled by setting "prometheus: null" + prometheus: + # -- Entry point used to expose metrics. + entryPoint: metrics +- ## Enable metrics on entry points. Default=true +- # addEntryPointsLabels: false +- ## Enable metrics on routers. Default=false +- # addRoutersLabels: true +- ## Enable metrics on services. Default=true +- # addServicesLabels: false ++ ## Enable metrics on entry points. Default: true ++ addEntryPointsLabels: # @schema type:[boolean, null] ++ ## Enable metrics on routers. Default: false ++ addRoutersLabels: # @schema type:[boolean, null] ++ ## Enable metrics on services. Default: true ++ addServicesLabels: # @schema type:[boolean, null] + ## Buckets for latency metrics. Default="0.1,0.3,1.2,5.0" +- # buckets: "0.5,1.0,2.5" ++ buckets: "" + ## When manualRouting is true, it disables the default internal router in + ## order to allow creating a custom router for prometheus@internal service. +- # manualRouting: true ++ manualRouting: false + service: + # -- Create a dedicated metrics service to use with ServiceMonitor +- enabled: +- labels: +- annotations: ++ enabled: false ++ labels: {} ++ annotations: {} + # -- When set to true, it won't check if Prometheus Operator CRDs are deployed +- disableAPICheck: ++ disableAPICheck: # @schema type:[boolean, null] + serviceMonitor: + # -- Enable optional CR for Prometheus Operator. See EXAMPLES.md for more details. + enabled: false +- metricRelabelings: +- relabelings: +- jobLabel: +- interval: +- honorLabels: +- scrapeTimeout: +- honorTimestamps: +- enableHttp2: +- followRedirects: +- additionalLabels: +- namespace: +- namespaceSelector: ++ metricRelabelings: [] ++ relabelings: [] ++ jobLabel: "" ++ interval: "" ++ honorLabels: false ++ scrapeTimeout: "" ++ honorTimestamps: false ++ enableHttp2: false ++ followRedirects: false ++ additionalLabels: {} ++ namespace: "" ++ namespaceSelector: {} + prometheusRule: + # -- Enable optional CR for Prometheus Operator. See EXAMPLES.md for more details. + enabled: false +- additionalLabels: +- namespace: ++ additionalLabels: {} ++ namespace: "" + + # datadog: + # ## Address instructs exporter to send metrics to datadog-agent at this address. +@@ -469,55 +467,55 @@ metrics: + # -- Set to true in order to enable the OpenTelemetry metrics + enabled: false + # -- Enable metrics on entry points. Default: true +- addEntryPointsLabels: ++ addEntryPointsLabels: # @schema type:[boolean, null] + # -- Enable metrics on routers. Default: false +- addRoutersLabels: ++ addRoutersLabels: # @schema type:[boolean, null] + # -- Enable metrics on services. Default: true +- addServicesLabels: ++ addServicesLabels: # @schema type:[boolean, null] + # -- Explicit boundaries for Histogram data points. Default: [.005, .01, .025, .05, .1, .25, .5, 1, 2.5, 5, 10] +- explicitBoundaries: ++ explicitBoundaries: [] + # -- Interval at which metrics are sent to the OpenTelemetry Collector. Default: 10s +- pushInterval: ++ pushInterval: "" + http: + # -- Set to true in order to send metrics to the OpenTelemetry Collector using HTTP. + enabled: false + # -- Format: ://:. Default: http://localhost:4318/v1/metrics +- endpoint: ++ endpoint: "" + # -- Additional headers sent with metrics by the reporter to the OpenTelemetry Collector. +- headers: ++ headers: {} + ## Defines the TLS configuration used by the reporter to send metrics to the OpenTelemetry Collector. + tls: + # -- The path to the certificate authority, it defaults to the system bundle. +- ca: ++ ca: "" + # -- The path to the public certificate. When using this option, setting the key option is required. +- cert: ++ cert: "" + # -- The path to the private key. When using this option, setting the cert option is required. +- key: ++ key: "" + # -- When set to true, the TLS connection accepts any certificate presented by the server regardless of the hostnames it covers. +- insecureSkipVerify: ++ insecureSkipVerify: # @schema type:[boolean, null] + grpc: + # -- Set to true in order to send metrics to the OpenTelemetry Collector using gRPC + enabled: false + # -- Format: ://:. Default: http://localhost:4318/v1/metrics +- endpoint: ++ endpoint: "" + # -- Allows reporter to send metrics to the OpenTelemetry Collector without using a secured protocol. +- insecure: ++ insecure: false + ## Defines the TLS configuration used by the reporter to send metrics to the OpenTelemetry Collector. + tls: + # -- The path to the certificate authority, it defaults to the system bundle. +- ca: ++ ca: "" + # -- The path to the public certificate. When using this option, setting the key option is required. +- cert: ++ cert: "" + # -- The path to the private key. When using this option, setting the cert option is required. +- key: ++ key: "" + # -- When set to true, the TLS connection accepts any certificate presented by the server regardless of the hostnames it covers. +- insecureSkipVerify: ++ insecureSkipVerify: false + + ## Tracing + # -- https://doc.traefik.io/traefik/observability/tracing/overview/ +-tracing: ++tracing: # @schema additionalProperties: false + # -- Enables tracing for internal resources. Default: false. +- addInternals: ++ addInternals: false + otlp: + # -- See https://doc.traefik.io/traefik/v3.0/observability/tracing/opentelemetry/ + enabled: false +@@ -525,36 +523,36 @@ tracing: + # -- Set to true in order to send metrics to the OpenTelemetry Collector using HTTP. + enabled: false + # -- Format: ://:. Default: http://localhost:4318/v1/metrics +- endpoint: ++ endpoint: "" + # -- Additional headers sent with metrics by the reporter to the OpenTelemetry Collector. +- headers: ++ headers: {} + ## Defines the TLS configuration used by the reporter to send metrics to the OpenTelemetry Collector. + tls: + # -- The path to the certificate authority, it defaults to the system bundle. +- ca: ++ ca: "" + # -- The path to the public certificate. When using this option, setting the key option is required. +- cert: ++ cert: "" + # -- The path to the private key. When using this option, setting the cert option is required. +- key: ++ key: "" + # -- When set to true, the TLS connection accepts any certificate presented by the server regardless of the hostnames it covers. +- insecureSkipVerify: ++ insecureSkipVerify: false + grpc: + # -- Set to true in order to send metrics to the OpenTelemetry Collector using gRPC + enabled: false + # -- Format: ://:. Default: http://localhost:4318/v1/metrics +- endpoint: ++ endpoint: "" + # -- Allows reporter to send metrics to the OpenTelemetry Collector without using a secured protocol. +- insecure: ++ insecure: false + ## Defines the TLS configuration used by the reporter to send metrics to the OpenTelemetry Collector. + tls: + # -- The path to the certificate authority, it defaults to the system bundle. +- ca: ++ ca: "" + # -- The path to the public certificate. When using this option, setting the key option is required. +- cert: ++ cert: "" + # -- The path to the private key. When using this option, setting the cert option is required. +- key: ++ key: "" + # -- When set to true, the TLS connection accepts any certificate presented by the server regardless of the hostnames it covers. +- insecureSkipVerify: ++ insecureSkipVerify: false + + # -- Global command arguments to be passed to all traefik's pods + globalArguments: +@@ -587,13 +585,12 @@ ports: + traefik: + port: 9000 + # -- Use hostPort if set. +- # hostPort: 9000 +- # ++ hostPort: # @schema type:[integer, null]; minimum:0 + # -- Use hostIP if set. If not set, Kubernetes will default to 0.0.0.0, which + # means it's listening on all your interfaces and all your IPs. You may want + # to set this value if you need traefik to listen on specific interface + # only. +- # hostIP: 192.168.100.10 ++ hostIP: # @schema type:[string, null] + + # Defines whether the port is exposed if service.type is LoadBalancer or + # NodePort. +@@ -617,112 +614,93 @@ ports: + default: true + exposedPort: 80 + ## -- Different target traefik port on the cluster, useful for IP type LB +- # targetPort: 80 ++ targetPort: # @schema type:[integer, null]; minimum:0 + # The port protocol (TCP/UDP) + protocol: TCP +- # -- Use nodeport if set. This is useful if you have configured Traefik in a +- # LoadBalancer. +- # nodePort: 32080 ++ # -- See [upstream documentation](https://kubernetes.io/docs/concepts/services-networking/service/#type-nodeport) ++ nodePort: # @schema type:[integer, null]; minimum:0 + # Port Redirections + # Added in 2.2, you can make permanent redirects via entrypoints. + # https://docs.traefik.io/routing/entrypoints/#redirection +- # redirectTo: +- # port: websecure +- # (Optional) +- # priority: 10 +- # permanent: true +- # +- # -- Trust forwarded headers information (X-Forwarded-*). +- # forwardedHeaders: +- # trustedIPs: [] +- # insecure: false +- # +- # -- Enable the Proxy Protocol header parsing for the entry point +- # proxyProtocol: +- # trustedIPs: [] +- # insecure: false +- # ++ redirectTo: {} ++ forwardedHeaders: ++ # -- Trust forwarded headers information (X-Forwarded-*). ++ trustedIPs: [] ++ insecure: false ++ proxyProtocol: ++ # -- Enable the Proxy Protocol header parsing for the entry point ++ trustedIPs: [] ++ insecure: false + # -- Set transport settings for the entrypoint; see also + # https://doc.traefik.io/traefik/routing/entrypoints/#transport + transport: + respondingTimeouts: +- readTimeout: +- writeTimeout: +- idleTimeout: ++ readTimeout: # @schema type:[string, integer, null] ++ writeTimeout: # @schema type:[string, integer, null] ++ idleTimeout: # @schema type:[string, integer, null] + lifeCycle: +- requestAcceptGraceTimeout: +- graceTimeOut: +- keepAliveMaxRequests: +- keepAliveMaxTime: ++ requestAcceptGraceTimeout: # @schema type:[string, integer, null] ++ graceTimeOut: # @schema type:[string, integer, null] ++ keepAliveMaxRequests: # @schema type:[integer, null]; minimum:0 ++ keepAliveMaxTime: # @schema type:[string, integer, null] + websecure: + ## -- Enable this entrypoint as a default entrypoint. When a service doesn't explicitly set an entrypoint it will only use this entrypoint. + # asDefault: true + port: 8443 +- # hostPort: 8443 +- # containerPort: 8443 ++ hostPort: # @schema type:[integer, null]; minimum:0 ++ containerPort: # @schema type:[integer, null]; minimum:0 + expose: + default: true + exposedPort: 443 + ## -- Different target traefik port on the cluster, useful for IP type LB +- # targetPort: 80 ++ targetPort: # @schema type:[integer, null]; minimum:0 + ## -- The port protocol (TCP/UDP) + protocol: TCP +- # nodePort: 32443 +- ## -- Specify an application protocol. This may be used as a hint for a Layer 7 load balancer. +- # appProtocol: https +- # +- ## -- Enable HTTP/3 on the entrypoint +- ## Enabling it will also enable http3 experimental feature +- ## https://doc.traefik.io/traefik/routing/entrypoints/#http3 +- ## There are known limitations when trying to listen on same ports for +- ## TCP & UDP (Http3). There is a workaround in this chart using dual Service. +- ## https://github.com/kubernetes/kubernetes/issues/47249#issuecomment-587960741 ++ # -- See [upstream documentation](https://kubernetes.io/docs/concepts/services-networking/service/#type-nodeport) ++ nodePort: # @schema type:[integer, null]; minimum:0 ++ # -- See [upstream documentation](https://kubernetes.io/docs/concepts/services-networking/service/#application-protocol) ++ appProtocol: # @schema type:[string, null] ++ # -- See [upstream documentation](https://doc.traefik.io/traefik/routing/entrypoints/#allowacmebypass) ++ allowACMEByPass: false + http3: ++ ## -- Enable HTTP/3 on the entrypoint ++ ## Enabling it will also enable http3 experimental feature ++ ## https://doc.traefik.io/traefik/routing/entrypoints/#http3 ++ ## There are known limitations when trying to listen on same ports for ++ ## TCP & UDP (Http3). There is a workaround in this chart using dual Service. ++ ## https://github.com/kubernetes/kubernetes/issues/47249#issuecomment-587960741 + enabled: false +- # advertisedPort: 4443 +- # +- # -- Trust forwarded headers information (X-Forwarded-*). +- # forwardedHeaders: +- # trustedIPs: [] +- # insecure: false +- # +- # -- Enable the Proxy Protocol header parsing for the entry point +- # proxyProtocol: +- # trustedIPs: [] +- # insecure: false +- # +- # -- Set transport settings for the entrypoint; see also +- # https://doc.traefik.io/traefik/routing/entrypoints/#transport ++ advertisedPort: # @schema type:[integer, null]; minimum:0 ++ forwardedHeaders: ++ # -- Trust forwarded headers information (X-Forwarded-*). ++ trustedIPs: [] ++ insecure: false ++ proxyProtocol: ++ # -- Enable the Proxy Protocol header parsing for the entry point ++ trustedIPs: [] ++ insecure: false ++ # -- See [upstream documentation](https://doc.traefik.io/traefik/routing/entrypoints/#transport) + transport: + respondingTimeouts: +- readTimeout: +- writeTimeout: +- idleTimeout: ++ readTimeout: # @schema type:[string, integer, null] ++ writeTimeout: # @schema type:[string, integer, null] ++ idleTimeout: # @schema type:[string, integer, null] + lifeCycle: +- requestAcceptGraceTimeout: +- graceTimeOut: +- keepAliveMaxRequests: +- keepAliveMaxTime: +- # +- ## Set TLS at the entrypoint +- ## https://doc.traefik.io/traefik/routing/entrypoints/#tls ++ requestAcceptGraceTimeout: # @schema type:[string, integer, null] ++ graceTimeOut: # @schema type:[string, integer, null] ++ keepAliveMaxRequests: # @schema type:[integer, null]; minimum:0 ++ keepAliveMaxTime: # @schema type:[string, integer, null] ++ # -- See [upstream documentation](https://doc.traefik.io/traefik/routing/entrypoints/#tls) + tls: + enabled: true +- # this is the name of a TLSOption definition + options: "" + certResolver: "" + domains: [] +- # - main: example.com +- # sans: +- # - foo.example.com +- # - bar.example.com +- # + # -- One can apply Middlewares on an entrypoint + # https://doc.traefik.io/traefik/middlewares/overview/ + # https://doc.traefik.io/traefik/routing/entrypoints/#middlewares + # -- /!\ It introduces here a link between your static configuration and your dynamic configuration /!\ + # It follows the provider naming convention: https://doc.traefik.io/traefik/providers/overview/#provider-namespace +- # middlewares: + # - namespace-name1@kubernetescrd + # - namespace-name2@kubernetescrd + middlewares: [] +@@ -730,10 +708,6 @@ ports: + # -- When using hostNetwork, use another port to avoid conflict with node exporter: + # https://github.com/prometheus/prometheus/wiki/Default-port-allocations + port: 9100 +- # hostPort: 9100 +- # Defines whether the port is exposed if service.type is LoadBalancer or +- # NodePort. +- # + # -- You may not want to expose the metrics port on production deployments. + # If you want to access it from outside your cluster, + # use `kubectl port-forward` or create a secure ingress +@@ -810,15 +784,15 @@ persistence: + # It can be used to store TLS certificates, see `storage` in certResolvers + enabled: false + name: data +- # existingClaim: "" ++ existingClaim: "" + accessMode: ReadWriteOnce + size: 128Mi +- # storageClass: "" +- # volumeName: "" ++ storageClass: "" ++ volumeName: "" + path: /data + annotations: {} + # -- Only mount a subpath of the Volume into the pod +- # subPath: "" ++ subPath: "" + + # -- Certificates resolvers configuration. + # Ref: https://doc.traefik.io/traefik/https/acme/#certificate-resolvers +@@ -832,7 +806,7 @@ certResolvers: {} + hostNetwork: false + + # -- Whether Role Based Access Control objects like roles and rolebindings should be created +-rbac: ++rbac: # @schema additionalProperties: false + enabled: true + # When set to true: + # 1. Use `Role` and `RoleBinding` instead of `ClusterRole` and `ClusterRoleBinding`. +@@ -843,7 +817,7 @@ rbac: + namespaced: false + # Enable user-facing roles + # https://kubernetes.io/docs/reference/access-authn-authz/rbac/#user-facing-roles +- # aggregateTo: [ "admin" ] ++ aggregateTo: [] + # List of Kubernetes secrets that are accessible for Traefik. If empty, then access is granted to every secret. + secretResourceNames: [] + +@@ -852,7 +826,7 @@ podSecurityPolicy: + enabled: false + + # -- The service account the pods will use to interact with the Kubernetes API +-serviceAccount: ++serviceAccount: # @schema additionalProperties: false + # If set, an existing service account is used + # If not set, a service account is created automatically using the fullname template + name: "" +@@ -918,54 +892,54 @@ extraObjects: [] + + # -- This field override the default Release Namespace for Helm. + # It will not affect optional CRDs such as `ServiceMonitor` and `PrometheusRules` +-namespaceOverride: ++namespaceOverride: "" + + ## -- This field override the default app.kubernetes.io/instance label for all Objects. +-instanceLabelOverride: ++instanceLabelOverride: "" + + # Traefik Hub configuration. See https://doc.traefik.io/traefik-hub/ + hub: + # -- Name of `Secret` with key 'token' set to a valid license token. + # It enables API Gateway. +- token: ++ token: "" + apimanagement: + # -- Set to true in order to enable API Management. Requires a valid license token. +- enabled: ++ enabled: false + admission: + # -- WebHook admission server listen address. Default: "0.0.0.0:9943". +- listenAddr: ++ listenAddr: "" + # -- Certificate of the WebHook admission server. Default: "hub-agent-cert". +- secretName: ++ secretName: "" + + ratelimit: + redis: + # -- Enable Redis Cluster. Default: true. +- cluster: ++ cluster: # @schema type:[boolean, null] + # -- Database used to store information. Default: "0". +- database: ++ database: # @schema type:[string, null] + # -- Endpoints of the Redis instances to connect to. Default: "". +- endpoints: ++ endpoints: "" + # -- The username to use when connecting to Redis endpoints. Default: "". +- username: ++ username: "" + # -- The password to use when connecting to Redis endpoints. Default: "". +- password: ++ password: "" + sentinel: + # -- Name of the set of main nodes to use for main selection. Required when using Sentinel. Default: "". +- masterset: ++ masterset: "" + # -- Username to use for sentinel authentication (can be different from endpoint username). Default: "". +- username: ++ username: "" + # -- Password to use for sentinel authentication (can be different from endpoint password). Default: "". +- password: ++ password: "" + # -- Timeout applied on connection with redis. Default: "0s". +- timeout: ++ timeout: "" + tls: + # -- Path to the certificate authority used for the secured connection. +- ca: ++ ca: "" + # -- Path to the public certificate used for the secure connection. +- cert: ++ cert: "" + # -- Path to the private key used for the secure connection. +- key: ++ key: "" + # -- When insecureSkipVerify is set to true, the TLS connection accepts any certificate presented by the server. Default: false. +- insecureSkipVerify: ++ insecureSkipVerify: false + # Enable export of errors logs to the platform. Default: true. +- sendlogs: ++ sendlogs: # @schema type:[boolean, null] +``` + +## 31.0.0 ![AppVersion: v3.1.2](https://img.shields.io/static/v1?label=AppVersion&message=v3.1.2&color=success&logo=) ![Kubernetes: >=1.22.0-0](https://img.shields.io/static/v1?label=Kubernetes&message=%3E%3D1.22.0-0&color=informational&logo=kubernetes) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2024-09-03 + +* fix(Traefik Hub): update CRDs to v1.5.0 +* fix(HTTP3): split udp and tcp Service when service.single is false +* fix!: 🐛 set allowEmptyServices to true by default +* feat(Traefik Hub): update CRDs to v1.7.0 +* chore(release): 🚀 publish v31.0.0 + +### Default value changes + +```diff +diff --git a/traefik/values.yaml b/traefik/values.yaml +index 78eeacf..2232d9e 100644 +--- a/traefik/values.yaml ++++ b/traefik/values.yaml +@@ -260,7 +260,7 @@ providers: + # -- Allows to reference ExternalName services in IngressRoute + allowExternalNameServices: false + # -- Allows to return 503 when there is no endpoints available +- allowEmptyServices: false ++ allowEmptyServices: true + # -- When the parameter is set, only resources containing an annotation with the same value are processed. Otherwise, resources missing the annotation, having an empty value, or the value traefik are processed. It will also set required annotation on Dashboard and Healthcheck IngressRoute when enabled. + ingressClass: + # labelSelector: environment=production,method=traefik +@@ -275,7 +275,7 @@ providers: + # -- Allows to reference ExternalName services in Ingress + allowExternalNameServices: false + # -- Allows to return 503 when there is no endpoints available +- allowEmptyServices: false ++ allowEmptyServices: true + # -- When ingressClass is set, only Ingresses containing an annotation with the same value are processed. Otherwise, Ingresses missing the annotation, having an empty value, or the value traefik are processed. + ingressClass: + # labelSelector: environment=production,method=traefik +``` + +## 30.1.0 ![AppVersion: v3.1.2](https://img.shields.io/static/v1?label=AppVersion&message=v3.1.2&color=success&logo=) ![Kubernetes: >=1.22.0-0](https://img.shields.io/static/v1?label=Kubernetes&message=%3E%3D1.22.0-0&color=informational&logo=kubernetes) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2024-08-14 + +* fix: disable default HTTPS listener for gateway +* fix(Gateway API): wildcard support in hostname +* fix(Gateway API): use Standard channel by default +* feat: ✨ rework namespaced RBAC with `disableClusterScopeResources` +* chore(release): 🚀 publish v30.1.0 +* chore(deps): update traefik docker tag to v3.1.2 +* chore(deps): update traefik docker tag to v3.1.1 + +### Default value changes + +```diff +diff --git a/traefik/values.yaml b/traefik/values.yaml +index 83b6d98..78eeacf 100644 +--- a/traefik/values.yaml ++++ b/traefik/values.yaml +@@ -150,20 +150,22 @@ gateway: + protocol: HTTP + # -- Routes are restricted to namespace of the gateway [by default](https://gateway-api.sigs.k8s.io/reference/spec/#gateway.networking.k8s.io/v1.FromNamespaces + namespacePolicy: +- websecure: +- # -- Port is the network port. Multiple listeners may use the same port, subject to the Listener compatibility rules. +- # The port must match a port declared in ports section. +- port: 8443 +- # -- Optional hostname. See [Hostname](https://gateway-api.sigs.k8s.io/reference/spec/#gateway.networking.k8s.io/v1.Hostname) +- hostname: +- # Specify expected protocol on this listener See [ProtocolType](https://gateway-api.sigs.k8s.io/reference/spec/#gateway.networking.k8s.io/v1.ProtocolType) +- protocol: HTTPS +- # -- Routes are restricted to namespace of the gateway [by default](https://gateway-api.sigs.k8s.io/reference/spec/#gateway.networking.k8s.io/v1.FromNamespaces) +- namespacePolicy: +- # -- Add certificates for TLS or HTTPS protocols. See [GatewayTLSConfig](https://gateway-api.sigs.k8s.io/reference/spec/#gateway.networking.k8s.io%2fv1.GatewayTLSConfig) +- certificateRefs: +- # -- TLS behavior for the TLS session initiated by the client. See [TLSModeType](https://gateway-api.sigs.k8s.io/reference/spec/#gateway.networking.k8s.io/v1.TLSModeType). +- mode: ++ # websecure listener is disabled by default because certificateRefs needs to be added, ++ # or you may specify TLS protocol with Passthrough mode and add "--providers.kubernetesGateway.experimentalChannel=true" in additionalArguments section. ++ # websecure: ++ # # -- Port is the network port. Multiple listeners may use the same port, subject to the Listener compatibility rules. ++ # # The port must match a port declared in ports section. ++ # port: 8443 ++ # # -- Optional hostname. See [Hostname](https://gateway-api.sigs.k8s.io/reference/spec/#gateway.networking.k8s.io/v1.Hostname) ++ # hostname: ++ # # Specify expected protocol on this listener See [ProtocolType](https://gateway-api.sigs.k8s.io/reference/spec/#gateway.networking.k8s.io/v1.ProtocolType) ++ # protocol: HTTPS ++ # # -- Routes are restricted to namespace of the gateway [by default](https://gateway-api.sigs.k8s.io/reference/spec/#gateway.networking.k8s.io/v1.FromNamespaces) ++ # namespacePolicy: ++ # # -- Add certificates for TLS or HTTPS protocols. See [GatewayTLSConfig](https://gateway-api.sigs.k8s.io/reference/spec/#gateway.networking.k8s.io%2fv1.GatewayTLSConfig) ++ # certificateRefs: ++ # # -- TLS behavior for the TLS session initiated by the client. See [TLSModeType](https://gateway-api.sigs.k8s.io/reference/spec/#gateway.networking.k8s.io/v1.TLSModeType). ++ # mode: + + gatewayClass: + # -- When providers.kubernetesGateway.enabled and gateway.enabled, deploy a default gatewayClass +@@ -279,10 +281,6 @@ providers: + # labelSelector: environment=production,method=traefik + # -- Array of namespaces to watch. If left empty, Traefik watches all namespaces. + namespaces: [] +- # - "default" +- # Disable cluster IngressClass Lookup - Requires Traefik V3. +- # When combined with rbac.namespaced: true, ClusterRole will not be created and ingresses must use kubernetes.io/ingress.class annotation instead of spec.ingressClassName. +- disableIngressClassLookup: false + # IP used for Kubernetes Ingress endpoints + publishedService: + enabled: false +@@ -836,9 +834,12 @@ hostNetwork: false + # -- Whether Role Based Access Control objects like roles and rolebindings should be created + rbac: + enabled: true +- # If set to false, installs ClusterRole and ClusterRoleBinding so Traefik can be used across namespaces. +- # If set to true, installs Role and RoleBinding instead of ClusterRole/ClusterRoleBinding. Providers will only watch target namespace. +- # When combined with providers.kubernetesIngress.disableIngressClassLookup: true and Traefik V3, ClusterRole to watch IngressClass is also disabled. ++ # When set to true: ++ # 1. Use `Role` and `RoleBinding` instead of `ClusterRole` and `ClusterRoleBinding`. ++ # 2. Set `disableIngressClassLookup` on Kubernetes Ingress providers with Traefik Proxy v3 until v3.1.1 ++ # 3. Set `disableClusterScopeResources` on Kubernetes Ingress and CRD providers with Traefik Proxy v3.1.2+ ++ # **NOTE**: `IngressClass`, `NodePortLB` and **Gateway** provider cannot be used with namespaced RBAC. ++ # See [upstream documentation](https://doc.traefik.io/traefik/providers/kubernetes-ingress/#disableclusterscoperesources) for more details. + namespaced: false + # Enable user-facing roles + # https://kubernetes.io/docs/reference/access-authn-authz/rbac/#user-facing-roles +``` + +## 30.0.2 ![AppVersion: v3.1.0](https://img.shields.io/static/v1?label=AppVersion&message=v3.1.0&color=success&logo=) ![Kubernetes: >=1.22.0-0](https://img.shields.io/static/v1?label=Kubernetes&message=%3E%3D1.22.0-0&color=informational&logo=kubernetes) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2024-07-30 + +* fix(Traefik Hub): missing RBACs for Traefik Hub +* chore(release): 🚀 publish v30.0.2 + +## 30.0.1 ![AppVersion: v3.1.0](https://img.shields.io/static/v1?label=AppVersion&message=v3.1.0&color=success&logo=) ![Kubernetes: >=1.22.0-0](https://img.shields.io/static/v1?label=Kubernetes&message=%3E%3D1.22.0-0&color=informational&logo=kubernetes) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2024-07-29 + +* fix(Traefik Hub): support new RBACs for upcoming traefik hub release +* fix(Traefik Hub): RBACs missing with API Gateway +* feat: :release: v30.0.1 + +## 30.0.0 ![AppVersion: v3.1.0](https://img.shields.io/static/v1?label=AppVersion&message=v3.1.0&color=success&logo=) ![Kubernetes: >=1.22.0-0](https://img.shields.io/static/v1?label=Kubernetes&message=%3E%3D1.22.0-0&color=informational&logo=kubernetes) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2024-07-24 + +* fix: 🐛 ingressroute default name +* fix: namespaced RBACs hub api gateway +* fix: can't set gateway name +* fix(Gateway API): provide expected roles when using namespaced RBAC +* fix(Gateway API)!: revamp Gateway implementation +* feat: ✨ display release name and image full path in installation notes +* feat: use single ingressRoute template +* feat: handle log filePath and noColor +* chore(release): 🚀 publish v30.0.0 +* chore(deps): update traefik docker tag to v3.1.0 + +**Upgrade Notes** + +There is a breaking upgrade on how to configure Gateway with _values_. +This release supports Traefik Proxy v3.0 **and** v3.1. + +### Default value changes + +```diff +diff --git a/traefik/values.yaml b/traefik/values.yaml +index c8bfd5b..83b6d98 100644 +--- a/traefik/values.yaml ++++ b/traefik/values.yaml +@@ -134,14 +134,36 @@ gateway: + enabled: true + # -- Set a custom name to gateway + name: +- # -- Routes are restricted to namespace of the gateway [by default](https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1beta1.FromNamespaces) +- namespacePolicy: +- # -- See [GatewayTLSConfig](https://gateway-api.sigs.k8s.io/reference/spec/#gateway.networking.k8s.io%2fv1.GatewayTLSConfig) +- certificateRefs: + # -- By default, Gateway is created in the same `Namespace` than Traefik. + namespace: + # -- Additional gateway annotations (e.g. for cert-manager.io/issuer) + annotations: ++ # -- Define listeners ++ listeners: ++ web: ++ # -- Port is the network port. Multiple listeners may use the same port, subject to the Listener compatibility rules. ++ # The port must match a port declared in ports section. ++ port: 8000 ++ # -- Optional hostname. See [Hostname](https://gateway-api.sigs.k8s.io/reference/spec/#gateway.networking.k8s.io/v1.Hostname) ++ hostname: ++ # Specify expected protocol on this listener. See [ProtocolType](https://gateway-api.sigs.k8s.io/reference/spec/#gateway.networking.k8s.io/v1.ProtocolType) ++ protocol: HTTP ++ # -- Routes are restricted to namespace of the gateway [by default](https://gateway-api.sigs.k8s.io/reference/spec/#gateway.networking.k8s.io/v1.FromNamespaces ++ namespacePolicy: ++ websecure: ++ # -- Port is the network port. Multiple listeners may use the same port, subject to the Listener compatibility rules. ++ # The port must match a port declared in ports section. ++ port: 8443 ++ # -- Optional hostname. See [Hostname](https://gateway-api.sigs.k8s.io/reference/spec/#gateway.networking.k8s.io/v1.Hostname) ++ hostname: ++ # Specify expected protocol on this listener See [ProtocolType](https://gateway-api.sigs.k8s.io/reference/spec/#gateway.networking.k8s.io/v1.ProtocolType) ++ protocol: HTTPS ++ # -- Routes are restricted to namespace of the gateway [by default](https://gateway-api.sigs.k8s.io/reference/spec/#gateway.networking.k8s.io/v1.FromNamespaces) ++ namespacePolicy: ++ # -- Add certificates for TLS or HTTPS protocols. See [GatewayTLSConfig](https://gateway-api.sigs.k8s.io/reference/spec/#gateway.networking.k8s.io%2fv1.GatewayTLSConfig) ++ certificateRefs: ++ # -- TLS behavior for the TLS session initiated by the client. See [TLSModeType](https://gateway-api.sigs.k8s.io/reference/spec/#gateway.networking.k8s.io/v1.TLSModeType). ++ mode: + + gatewayClass: + # -- When providers.kubernetesGateway.enabled and gateway.enabled, deploy a default gatewayClass +@@ -161,6 +183,10 @@ ingressRoute: + labels: {} + # -- The router match rule used for the dashboard ingressRoute + matchRule: PathPrefix(`/dashboard`) || PathPrefix(`/api`) ++ # -- The internal service used for the dashboard ingressRoute ++ services: ++ - name: api@internal ++ kind: TraefikService + # -- Specify the allowed entrypoints to use for the dashboard ingress route, (e.g. traefik, web, websecure). + # By default, it's using traefik entrypoint, which is not exposed. + # /!\ Do not expose your dashboard without any protection over the internet /!\ +@@ -178,6 +204,10 @@ ingressRoute: + labels: {} + # -- The router match rule used for the healthcheck ingressRoute + matchRule: PathPrefix(`/ping`) ++ # -- The internal service used for the healthcheck ingressRoute ++ services: ++ - name: ping@internal ++ kind: TraefikService + # -- Specify the allowed entrypoints to use for the healthcheck ingress route, (e.g. traefik, web, websecure). + # By default, it's using traefik entrypoint, which is not exposed. + entryPoints: ["traefik"] +@@ -307,9 +337,12 @@ logs: + # -- Set [logs format](https://doc.traefik.io/traefik/observability/logs/#format) + # @default common + format: +- # By default, the level is set to ERROR. ++ # By default, the level is set to INFO. + # -- Alternative logging levels are DEBUG, PANIC, FATAL, ERROR, WARN, and INFO. + level: INFO ++ # ++ # filePath: "/var/log/traefik/traefik.log ++ # noColor: true + access: + # -- To enable access logs + enabled: false +``` + + +## 29.0.1 ![AppVersion: v3.0.4](https://img.shields.io/static/v1?label=AppVersion&message=v3.0.4&color=success&logo=) ![Kubernetes: >=1.22.0-0](https://img.shields.io/static/v1?label=Kubernetes&message=%3E%3D1.22.0-0&color=informational&logo=kubernetes) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2024-07-09 + +* fix: semverCompare failing on some legitimate tags +* fix: RBACs for hub and disabled namespaced RBACs +* chore(release): 🚀 publish v29.0.1 +* chore(deps): update jnorwood/helm-docs docker tag to v1.14.0 + +## 29.0.0 ![AppVersion: v3.0.4](https://img.shields.io/static/v1?label=AppVersion&message=v3.0.4&color=success&logo=) ![Kubernetes: >=1.22.0-0](https://img.shields.io/static/v1?label=Kubernetes&message=%3E%3D1.22.0-0&color=informational&logo=kubernetes) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Upgrade Notes** + +This is a major breaking upgrade. [Migration guide](https://doc.traefik.io/traefik/v3.1/migration/v3/#v30-to-v31) from v3.0 to v3.1rc has been applied on this chart. + +This release supports both Traefik Proxy v3.0.x and v3.1rc. + +It comes with those breaking changes: + +- Far better support on Gateway API v1.1: Gateway, GatewayClass, CRDs & RBAC (#1107) +- Many changes on CRDs & RBAC (#1072 & #1108) +- Refactor on Prometheus Operator support. Values has changed (#1114) +- Dashboard `IngressRoute` is now disabled by default (#1111) + +CRDs needs to be upgraded: `kubectl apply --server-side --force-conflicts -k https://github.com/traefik/traefik-helm-chart/traefik/crds/` + +**Release date:** 2024-07-05 + +* fix: 🐛 improve error message on additional service without ports +* fix: allow multiples values in the `secretResourceNames` slice +* fix(rbac)!: nodes API permissions for Traefik v3.1+ +* fix(dashboard): Only set ingressClass annotation when kubernetesCRD provider is listening for it +* fix!: prometheus operator settings +* feat: ✨ update CRDs & RBAC for Traefik Proxy +* feat: ✨ migrate to endpointslices rbac +* feat: allow to set hostAliases for traefik pod +* feat(providers): add nativeLBByDefault support +* feat(providers)!: improve kubernetesGateway and Gateway API support +* feat(dashboard)!: dashboard `IngressRoute` should be disabled by default +* docs: fix typos and broken link +* chore: update CRDs to v1.5.0 +* chore: update CRDs to v1.4.0 +* chore(release): publish v29.0.0 +* chore(deps): update traefik docker tag to v3.0.4 +* chore(deps): update traefik docker tag to v3.0.3 + +### Default value changes + +```diff +diff --git a/traefik/values.yaml b/traefik/values.yaml +index e440dcf..c8bfd5b 100644 +--- a/traefik/values.yaml ++++ b/traefik/values.yaml +@@ -8,7 +8,7 @@ image: + # -- Traefik image repository + repository: traefik + # -- defaults to appVersion +- tag: "" ++ tag: + # -- Traefik image pull policy + pullPolicy: IfNotPresent + +@@ -81,19 +81,12 @@ deployment: + shareProcessNamespace: false + # -- Custom pod DNS policy. Apply if `hostNetwork: true` + # dnsPolicy: ClusterFirstWithHostNet ++ # -- Custom pod [DNS config](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.30/#poddnsconfig-v1-core) + dnsConfig: {} +- # nameservers: +- # - 192.0.2.1 # this is an example +- # searches: +- # - ns1.svc.cluster-domain.example +- # - my.dns.search.suffix +- # options: +- # - name: ndots +- # value: "2" +- # - name: edns0 +- # -- Additional imagePullSecrets ++ # -- Custom [host aliases](https://kubernetes.io/docs/tasks/network/customize-hosts-file-for-pods/) ++ hostAliases: [] ++ # -- Pull secret for fetching traefik container image + imagePullSecrets: [] +- # - name: myRegistryKeySecretName + # -- Pod lifecycle actions + lifecycle: {} + # preStop: +@@ -135,24 +128,33 @@ experimental: + kubernetesGateway: + # -- Enable traefik experimental GatewayClass CRD + enabled: false +- ## Routes are restricted to namespace of the gateway by default. +- ## https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1beta1.FromNamespaces +- # namespacePolicy: All +- # certificate: +- # group: "core" +- # kind: "Secret" +- # name: "mysecret" +- # -- By default, Gateway would be created to the Namespace you are deploying Traefik to. +- # You may create that Gateway in another namespace, setting its name below: +- # namespace: default +- # Additional gateway annotations (e.g. for cert-manager.io/issuer) +- # annotations: +- # cert-manager.io/issuer: letsencrypt ++ ++gateway: ++ # -- When providers.kubernetesGateway.enabled, deploy a default gateway ++ enabled: true ++ # -- Set a custom name to gateway ++ name: ++ # -- Routes are restricted to namespace of the gateway [by default](https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1beta1.FromNamespaces) ++ namespacePolicy: ++ # -- See [GatewayTLSConfig](https://gateway-api.sigs.k8s.io/reference/spec/#gateway.networking.k8s.io%2fv1.GatewayTLSConfig) ++ certificateRefs: ++ # -- By default, Gateway is created in the same `Namespace` than Traefik. ++ namespace: ++ # -- Additional gateway annotations (e.g. for cert-manager.io/issuer) ++ annotations: ++ ++gatewayClass: ++ # -- When providers.kubernetesGateway.enabled and gateway.enabled, deploy a default gatewayClass ++ enabled: true ++ # -- Set a custom name to GatewayClass ++ name: ++ # -- Additional gatewayClass labels (e.g. for filtering gateway objects by custom labels) ++ labels: + + ingressRoute: + dashboard: + # -- Create an IngressRoute for the dashboard +- enabled: true ++ enabled: false + # -- Additional ingressRoute annotations (e.g. for kubernetes.io/ingress.class) + annotations: {} + # -- Additional ingressRoute labels (e.g. for filtering IngressRoute by custom labels) +@@ -227,11 +229,13 @@ providers: + allowExternalNameServices: false + # -- Allows to return 503 when there is no endpoints available + allowEmptyServices: false +- # ingressClass: traefik-internal ++ # -- When the parameter is set, only resources containing an annotation with the same value are processed. Otherwise, resources missing the annotation, having an empty value, or the value traefik are processed. It will also set required annotation on Dashboard and Healthcheck IngressRoute when enabled. ++ ingressClass: + # labelSelector: environment=production,method=traefik + # -- Array of namespaces to watch. If left empty, Traefik watches all namespaces. + namespaces: [] +- # - "default" ++ # -- Defines whether to use Native Kubernetes load-balancing mode by default. ++ nativeLBByDefault: + + kubernetesIngress: + # -- Load Kubernetes Ingress provider +@@ -240,7 +244,8 @@ providers: + allowExternalNameServices: false + # -- Allows to return 503 when there is no endpoints available + allowEmptyServices: false +- # ingressClass: traefik-internal ++ # -- When ingressClass is set, only Ingresses containing an annotation with the same value are processed. Otherwise, Ingresses missing the annotation, having an empty value, or the value traefik are processed. ++ ingressClass: + # labelSelector: environment=production,method=traefik + # -- Array of namespaces to watch. If left empty, Traefik watches all namespaces. + namespaces: [] +@@ -254,6 +259,19 @@ providers: + # Published Kubernetes Service to copy status from. Format: namespace/servicename + # By default this Traefik service + # pathOverride: "" ++ # -- Defines whether to use Native Kubernetes load-balancing mode by default. ++ nativeLBByDefault: ++ ++ kubernetesGateway: ++ # -- Enable Traefik Gateway provider for Gateway API ++ enabled: false ++ # -- Toggles support for the Experimental Channel resources (Gateway API release channels documentation). ++ # This option currently enables support for TCPRoute and TLSRoute. ++ experimentalChannel: false ++ # -- Array of namespaces to watch. If left empty, Traefik watches all namespaces. ++ namespaces: [] ++ # -- A label selector can be defined to filter on specific GatewayClass objects only. ++ labelselector: + + file: + # -- Create a file provider +@@ -341,6 +359,34 @@ metrics: + ## When manualRouting is true, it disables the default internal router in + ## order to allow creating a custom router for prometheus@internal service. + # manualRouting: true ++ service: ++ # -- Create a dedicated metrics service to use with ServiceMonitor ++ enabled: ++ labels: ++ annotations: ++ # -- When set to true, it won't check if Prometheus Operator CRDs are deployed ++ disableAPICheck: ++ serviceMonitor: ++ # -- Enable optional CR for Prometheus Operator. See EXAMPLES.md for more details. ++ enabled: false ++ metricRelabelings: ++ relabelings: ++ jobLabel: ++ interval: ++ honorLabels: ++ scrapeTimeout: ++ honorTimestamps: ++ enableHttp2: ++ followRedirects: ++ additionalLabels: ++ namespace: ++ namespaceSelector: ++ prometheusRule: ++ # -- Enable optional CR for Prometheus Operator. See EXAMPLES.md for more details. ++ enabled: false ++ additionalLabels: ++ namespace: ++ + # datadog: + # ## Address instructs exporter to send metrics to datadog-agent at this address. + # address: "127.0.0.1:8125" +@@ -436,55 +482,6 @@ metrics: + # -- When set to true, the TLS connection accepts any certificate presented by the server regardless of the hostnames it covers. + insecureSkipVerify: + +- ## -- enable optional CRDs for Prometheus Operator +- ## +- ## Create a dedicated metrics service for use with ServiceMonitor +- # service: +- # enabled: false +- # labels: {} +- # annotations: {} +- ## When set to true, it won't check if Prometheus Operator CRDs are deployed +- # disableAPICheck: false +- # serviceMonitor: +- # metricRelabelings: [] +- # - sourceLabels: [__name__] +- # separator: ; +- # regex: ^fluentd_output_status_buffer_(oldest|newest)_.+ +- # replacement: $1 +- # action: drop +- # relabelings: [] +- # - sourceLabels: [__meta_kubernetes_pod_node_name] +- # separator: ; +- # regex: ^(.*)$ +- # targetLabel: nodename +- # replacement: $1 +- # action: replace +- # jobLabel: traefik +- # interval: 30s +- # honorLabels: true +- # # (Optional) +- # # scrapeTimeout: 5s +- # # honorTimestamps: true +- # # enableHttp2: true +- # # followRedirects: true +- # # additionalLabels: +- # # foo: bar +- # # namespace: "another-namespace" +- # # namespaceSelector: {} +- # prometheusRule: +- # additionalLabels: {} +- # namespace: "another-namespace" +- # rules: +- # - alert: TraefikDown +- # expr: up{job="traefik"} == 0 +- # for: 5m +- # labels: +- # context: traefik +- # severity: warning +- # annotations: +- # summary: "Traefik Down" +- # description: "{{ $labels.pod }} on {{ $labels.nodename }} is down" +- + ## Tracing + # -- https://doc.traefik.io/traefik/observability/tracing/overview/ + tracing: +``` + +## 28.3.0 ![AppVersion: v3.0.2](https://img.shields.io/static/v1?label=AppVersion&message=v3.0.2&color=success&logo=) ![Kubernetes: >=1.22.0-0](https://img.shields.io/static/v1?label=Kubernetes&message=%3E%3D1.22.0-0&color=informational&logo=kubernetes) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2024-06-14 + +* fix: 🐛 namespaced rbac when kubernetesIngress provider is disabled +* fix: 🐛 add divisor: '1' to GOMAXPROCS and GOMEMLIMIT +* fix(security): 🐛 🔒️ mount service account token on pod level +* fix(Traefik Hub): remove obsolete CRD +* fix(Traefik Hub): remove namespace in mutating webhook +* feat: allow setting permanent on redirectTo +* chore(release): publish v28.3.0 +* chore(deps): update traefik docker tag to v3.0.2 + +### Default value changes + +```diff +diff --git a/traefik/values.yaml b/traefik/values.yaml +index c558c78..e440dcf 100644 +--- a/traefik/values.yaml ++++ b/traefik/values.yaml +@@ -602,6 +602,7 @@ ports: + # port: websecure + # (Optional) + # priority: 10 ++ # permanent: true + # + # -- Trust forwarded headers information (X-Forwarded-*). + # forwardedHeaders: +``` + +## 28.2.0 ![AppVersion: v3.0.1](https://img.shields.io/static/v1?label=AppVersion&message=v3.0.1&color=success&logo=) ![Kubernetes: >=1.22.0-0](https://img.shields.io/static/v1?label=Kubernetes&message=%3E%3D1.22.0-0&color=informational&logo=kubernetes) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2024-05-28 + +* fix(IngressClass): provides annotation on IngressRoutes when it's enabled +* feat: ✨ simplify values and provide more examples +* feat: add deletecollection right on secrets +* chore(release): 🚀 publish v28.2.0 +* chore(deps): update traefik docker tag to v3.0.1 + +### Default value changes + +```diff +diff --git a/traefik/values.yaml b/traefik/values.yaml +index 2fd9282..c558c78 100644 +--- a/traefik/values.yaml ++++ b/traefik/values.yaml +@@ -1,4 +1,7 @@ + # Default values for Traefik ++# This is a YAML-formatted file. ++# Declare variables to be passed into templates ++ + image: + # -- Traefik image host registry + registry: docker.io +@@ -12,9 +15,6 @@ image: + # -- Add additional label to all resources + commonLabels: {} + +-# +-# Configure the deployment +-# + deployment: + # -- Enable deployment + enabled: true +@@ -74,10 +74,6 @@ deployment: + # - name: volume-permissions + # image: busybox:latest + # command: ["sh", "-c", "touch /data/acme.json; chmod -v 600 /data/acme.json"] +- # securityContext: +- # runAsNonRoot: true +- # runAsGroup: 65532 +- # runAsUser: 65532 + # volumeMounts: + # - name: data + # mountPath: /data +@@ -112,13 +108,11 @@ deployment: + # -- Set a runtimeClassName on pod + runtimeClassName: + +-# -- Pod disruption budget ++# -- [Pod Disruption Budget](https://kubernetes.io/docs/reference/kubernetes-api/policy-resources/pod-disruption-budget-v1/) + podDisruptionBudget: +- enabled: false +- # maxUnavailable: 1 +- # maxUnavailable: 33% +- # minAvailable: 0 +- # minAvailable: 25% ++ enabled: ++ maxUnavailable: ++ minAvailable: + + # -- Create a default IngressClass for Traefik + ingressClass: +@@ -155,7 +149,6 @@ experimental: + # annotations: + # cert-manager.io/issuer: letsencrypt + +-## Create an IngressRoute for the dashboard + ingressRoute: + dashboard: + # -- Create an IngressRoute for the dashboard +@@ -221,15 +214,7 @@ livenessProbe: + # -- The number of seconds to wait for a probe response before considering it as failed. + timeoutSeconds: 2 + +-# -- Define Startup Probe for container: https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/#define-startup-probes +-# eg. +-# `startupProbe: +-# exec: +-# command: +-# - mycommand +-# - foo +-# initialDelaySeconds: 5 +-# periodSeconds: 5` ++# -- Define [Startup Probe](https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/#define-startup-probes) + startupProbe: + + providers: +@@ -276,18 +261,8 @@ providers: + # -- Allows Traefik to automatically watch for file changes + watch: true + # -- File content (YAML format, go template supported) (see https://doc.traefik.io/traefik/providers/file/) +- content: "" +- # http: +- # routers: +- # router0: +- # entryPoints: +- # - web +- # middlewares: +- # - my-basic-auth +- # service: service-foo +- # rule: Path(`/foo`) ++ content: + +-# + # -- Add volumes to the traefik pod. The volume name will be passed to tpl. + # This can be used to mount a cert pair or a configmap that holds a config.toml file. + # After the volume has been mounted, add the configs into traefik by using the `additionalArguments` list below, eg: +@@ -311,26 +286,21 @@ additionalVolumeMounts: [] + + logs: + general: +- # -- By default, the logs use a text format (common), but you can +- # also ask for the json format in the format option +- # format: json ++ # -- Set [logs format](https://doc.traefik.io/traefik/observability/logs/#format) ++ # @default common ++ format: + # By default, the level is set to ERROR. + # -- Alternative logging levels are DEBUG, PANIC, FATAL, ERROR, WARN, and INFO. + level: INFO + access: + # -- To enable access logs + enabled: false +- ## By default, logs are written using the Common Log Format (CLF) on stdout. +- ## To write logs in JSON, use json in the format option. +- ## If the given format is unsupported, the default (CLF) is used instead. +- # format: json ++ # -- Set [access log format](https://doc.traefik.io/traefik/observability/access-logs/#format) ++ format: + # filePath: "/var/log/traefik/access.log +- ## To write the logs in an asynchronous fashion, specify a bufferingSize option. +- ## This option represents the number of log lines Traefik will keep in memory before writing +- ## them to the selected output. In some cases, this option can greatly help performances. +- # bufferingSize: 100 +- ## Filtering +- # -- https://docs.traefik.io/observability/access-logs/#filtering ++ # -- Set [bufferingSize](https://doc.traefik.io/traefik/observability/access-logs/#bufferingsize) ++ bufferingSize: ++ # -- Set [filtering](https://docs.traefik.io/observability/access-logs/#filtering) + filters: {} + # statuscodes: "200,300-302" + # retryattempts: true +@@ -345,15 +315,11 @@ logs: + names: {} + ## Examples: + # ClientUsername: drop ++ # -- [Limit logged fields or headers](https://doc.traefik.io/traefik/observability/access-logs/#limiting-the-fieldsincluding-headers) + headers: + # -- Available modes: keep, drop, redact. + defaultmode: drop +- # -- Names of the headers to limit. + names: {} +- ## Examples: +- # User-Agent: redact +- # Authorization: drop +- # Content-Type: keep + + metrics: + ## -- Enable metrics for internal resources. Default: false +@@ -567,16 +533,15 @@ globalArguments: + - "--global.checknewversion" + - "--global.sendanonymoususage" + +-# +-# Configure Traefik static configuration + # -- Additional arguments to be passed at Traefik's binary +-# All available options available on https://docs.traefik.io/reference/static-configuration/cli/ +-## Use curly braces to pass values: `helm install --set="additionalArguments={--providers.kubernetesingress.ingressclass=traefik-internal,--log.level=DEBUG}"` ++# See [CLI Reference](https://docs.traefik.io/reference/static-configuration/cli/) ++# Use curly braces to pass values: `helm install --set="additionalArguments={--providers.kubernetesingress.ingressclass=traefik-internal,--log.level=DEBUG}"` + additionalArguments: [] + # - "--providers.kubernetesingress.ingressclass=traefik-internal" + # - "--log.level=DEBUG" + + # -- Environment variables to be passed to Traefik's binary ++# @default -- See _values.yaml_ + env: + - name: POD_NAME + valueFrom: +@@ -586,25 +551,9 @@ env: + valueFrom: + fieldRef: + fieldPath: metadata.namespace +-# - name: SOME_VAR +-# value: some-var-value +-# - name: SOME_VAR_FROM_CONFIG_MAP +-# valueFrom: +-# configMapRef: +-# name: configmap-name +-# key: config-key +-# - name: SOME_SECRET +-# valueFrom: +-# secretKeyRef: +-# name: secret-name +-# key: secret-key + + # -- Environment variables to be passed to Traefik's binary from configMaps or secrets + envFrom: [] +-# - configMapRef: +-# name: config-map-name +-# - secretRef: +-# name: secret-name + + ports: + traefik: +@@ -766,28 +715,12 @@ ports: + # -- The port protocol (TCP/UDP) + protocol: TCP + +-# -- TLS Options are created as TLSOption CRDs +-# https://doc.traefik.io/traefik/https/tls/#tls-options ++# -- TLS Options are created as [TLSOption CRDs](https://doc.traefik.io/traefik/https/tls/#tls-options) + # When using `labelSelector`, you'll need to set labels on tlsOption accordingly. +-# Example: +-# tlsOptions: +-# default: +-# labels: {} +-# sniStrict: true +-# custom-options: +-# labels: {} +-# curvePreferences: +-# - CurveP521 +-# - CurveP384 ++# See EXAMPLE.md for details. + tlsOptions: {} + +-# -- TLS Store are created as TLSStore CRDs. This is useful if you want to set a default certificate +-# https://doc.traefik.io/traefik/https/tls/#default-certificate +-# Example: +-# tlsStore: +-# default: +-# defaultCertificate: +-# secretName: tls-cert ++# -- TLS Store are created as [TLSStore CRDs](https://doc.traefik.io/traefik/https/tls/#default-certificate). This is useful if you want to set a default certificate. See EXAMPLE.md for details. + tlsStore: {} + + service: +@@ -839,29 +772,8 @@ service: + + autoscaling: + # -- Create HorizontalPodAutoscaler object. ++ # See EXAMPLES.md for more details. + enabled: false +-# minReplicas: 1 +-# maxReplicas: 10 +-# metrics: +-# - type: Resource +-# resource: +-# name: cpu +-# target: +-# type: Utilization +-# averageUtilization: 60 +-# - type: Resource +-# resource: +-# name: memory +-# target: +-# type: Utilization +-# averageUtilization: 60 +-# behavior: +-# scaleDown: +-# stabilizationWindowSeconds: 300 +-# policies: +-# - type: Pods +-# value: 1 +-# periodSeconds: 60 + + persistence: + # -- Enable persistence using Persistent Volume Claims +@@ -879,27 +791,10 @@ persistence: + # -- Only mount a subpath of the Volume into the pod + # subPath: "" + +-# -- Certificates resolvers configuration ++# -- Certificates resolvers configuration. ++# Ref: https://doc.traefik.io/traefik/https/acme/#certificate-resolvers ++# See EXAMPLES.md for more details. + certResolvers: {} +-# letsencrypt: +-# # for challenge options cf. https://doc.traefik.io/traefik/https/acme/ +-# email: email@example.com +-# dnsChallenge: +-# # also add the provider's required configuration under env +-# # or expand then from secrets/configmaps with envfrom +-# # cf. https://doc.traefik.io/traefik/https/acme/#providers +-# provider: digitalocean +-# # add futher options for the dns challenge as needed +-# # cf. https://doc.traefik.io/traefik/https/acme/#dnschallenge +-# delayBeforeCheck: 30 +-# resolvers: +-# - 1.1.1.1 +-# - 8.8.8.8 +-# tlsChallenge: true +-# httpChallenge: +-# entryPoint: "web" +-# # It has to match the path with a persistent volume +-# storage: /data/acme.json + + # -- If hostNetwork is true, runs traefik in the host network namespace + # To prevent unschedulabel pods due to port collisions, if hostNetwork=true +@@ -933,14 +828,8 @@ serviceAccount: + # -- Additional serviceAccount annotations (e.g. for oidc authentication) + serviceAccountAnnotations: {} + +-# -- The resources parameter defines CPU and memory requirements and limits for Traefik's containers. ++# -- [Resources](https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/) for `traefik` container. + resources: {} +-# requests: +-# cpu: "100m" +-# memory: "50Mi" +-# limits: +-# cpu: "300m" +-# memory: "150Mi" + + # -- This example pod anti-affinity forces the scheduler to put traefik pods + # -- on nodes where no other traefik pods are scheduled. +@@ -970,30 +859,22 @@ topologySpreadConstraints: [] + # topologyKey: kubernetes.io/hostname + # whenUnsatisfiable: DoNotSchedule + +-# -- Pods can have priority. +-# -- Priority indicates the importance of a Pod relative to other Pods. ++# -- [Pod Priority and Preemption](https://kubernetes.io/docs/concepts/scheduling-eviction/pod-priority-preemption/) + priorityClassName: "" + +-# -- Set the container security context +-# -- To run the container with ports below 1024 this will need to be adjusted to run as root ++# -- [SecurityContext](https://kubernetes.io/docs/reference/kubernetes-api/workload-resources/pod-v1/#security-context-1) ++# @default -- See _values.yaml_ + securityContext: ++ allowPrivilegeEscalation: false + capabilities: + drop: [ALL] + readOnlyRootFilesystem: true +- allowPrivilegeEscalation: false + ++# -- [Pod Security Context](https://kubernetes.io/docs/reference/kubernetes-api/workload-resources/pod-v1/#security-context) ++# @default -- See _values.yaml_ + podSecurityContext: +- # /!\ When setting fsGroup, Kubernetes will recursively change ownership and +- # permissions for the contents of each volume to match the fsGroup. This can +- # be an issue when storing sensitive content like TLS Certificates /!\ +- # fsGroup: 65532 +- # -- Specifies the policy for changing ownership and permissions of volume contents to match the fsGroup. +- fsGroupChangePolicy: "OnRootMismatch" +- # -- The ID of the group for all containers in the pod to run as. + runAsGroup: 65532 +- # -- Specifies whether the containers should run as a non-root user. + runAsNonRoot: true +- # -- The ID of the user for all containers in the pod to run as. + runAsUser: 65532 + + # +@@ -1003,16 +884,16 @@ podSecurityContext: + # See #595 for more details and traefik/tests/values/extra.yaml for example. + extraObjects: [] + +-# This will override the default Release Namespace for Helm. ++# -- This field override the default Release Namespace for Helm. + # It will not affect optional CRDs such as `ServiceMonitor` and `PrometheusRules` +-# namespaceOverride: traefik +-# +-## -- This will override the default app.kubernetes.io/instance label for all Objects. +-# instanceLabelOverride: traefik ++namespaceOverride: ++ ++## -- This field override the default app.kubernetes.io/instance label for all Objects. ++instanceLabelOverride: + +-# -- Traefik Hub configuration. See https://doc.traefik.io/traefik-hub/ ++# Traefik Hub configuration. See https://doc.traefik.io/traefik-hub/ + hub: +- # Name of Secret with key 'token' set to a valid license token. ++ # -- Name of `Secret` with key 'token' set to a valid license token. + # It enables API Gateway. + token: + apimanagement: +``` + +## 28.1.0 ![AppVersion: v3.0.0](https://img.shields.io/static/v1?label=AppVersion&message=v3.0.0&color=success&logo=) ![Kubernetes: >=1.22.0-0](https://img.shields.io/static/v1?label=Kubernetes&message=%3E%3D1.22.0-0&color=informational&logo=kubernetes) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +* fix(Traefik Hub): do not deploy mutating webhook when enabling only API Gateway +* feat(Traefik Hub): use Traefik Proxy otlp config +* chore: 🔧 update Traefik Hub CRD to v1.3.3 + +### Default value changes + +```diff +diff --git a/traefik/values.yaml b/traefik/values.yaml +index 70297f6..2fd9282 100644 +--- a/traefik/values.yaml ++++ b/traefik/values.yaml +@@ -1010,3 +1010,49 @@ + ## -- This will override the default app.kubernetes.io/instance label for all Objects. + # instanceLabelOverride: traefik + ++# -- Traefik Hub configuration. See https://doc.traefik.io/traefik-hub/ ++hub: ++ # Name of Secret with key 'token' set to a valid license token. ++ # It enables API Gateway. ++ token: ++ apimanagement: ++ # -- Set to true in order to enable API Management. Requires a valid license token. ++ enabled: ++ admission: ++ # -- WebHook admission server listen address. Default: "0.0.0.0:9943". ++ listenAddr: ++ # -- Certificate of the WebHook admission server. Default: "hub-agent-cert". ++ secretName: ++ ++ ratelimit: ++ redis: ++ # -- Enable Redis Cluster. Default: true. ++ cluster: ++ # -- Database used to store information. Default: "0". ++ database: ++ # -- Endpoints of the Redis instances to connect to. Default: "". ++ endpoints: ++ # -- The username to use when connecting to Redis endpoints. Default: "". ++ username: ++ # -- The password to use when connecting to Redis endpoints. Default: "". ++ password: ++ sentinel: ++ # -- Name of the set of main nodes to use for main selection. Required when using Sentinel. Default: "". ++ masterset: ++ # -- Username to use for sentinel authentication (can be different from endpoint username). Default: "". ++ username: ++ # -- Password to use for sentinel authentication (can be different from endpoint password). Default: "". ++ password: ++ # -- Timeout applied on connection with redis. Default: "0s". ++ timeout: ++ tls: ++ # -- Path to the certificate authority used for the secured connection. ++ ca: ++ # -- Path to the public certificate used for the secure connection. ++ cert: ++ # -- Path to the private key used for the secure connection. ++ key: ++ # -- When insecureSkipVerify is set to true, the TLS connection accepts any certificate presented by the server. Default: false. ++ insecureSkipVerify: ++ # Enable export of errors logs to the platform. Default: true. ++ sendlogs: +``` + +## 28.1.0-beta.3 ![AppVersion: v3.0.0](https://img.shields.io/static/v1?label=AppVersion&message=v3.0.0&color=success&logo=) ![Kubernetes: >=1.22.0-0](https://img.shields.io/static/v1?label=Kubernetes&message=%3E%3D1.22.0-0&color=informational&logo=kubernetes) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2024-05-03 + +* chore: 🔧 update Traefik Hub CRD to v1.3.2 +* chore(release): 🚀 publish v28.1.0-beta.3 + +## 28.1.0-beta.2 ![AppVersion: v3.0.0](https://img.shields.io/static/v1?label=AppVersion&message=v3.0.0&color=success&logo=) ![Kubernetes: >=1.22.0-0](https://img.shields.io/static/v1?label=Kubernetes&message=%3E%3D1.22.0-0&color=informational&logo=kubernetes) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2024-05-02 + +* fix: 🐛 refine Traefik Hub support +* chore(release): 🚀 publish v28.1.0-beta.2 + +### Default value changes + +```diff +diff --git a/traefik/values.yaml b/traefik/values.yaml +index ce0a7a3..70297f6 100644 +--- a/traefik/values.yaml ++++ b/traefik/values.yaml +@@ -1015,13 +1015,15 @@ hub: + # Name of Secret with key 'token' set to a valid license token. + # It enables API Gateway. + token: +- admission: +- # -- WebHook admission server listen address. Default: "0.0.0.0:9943". +- listenAddr: +- # -- Certificate of the WebHook admission server. Default: "hub-agent-cert". +- secretName: +- # -- Set to true in order to enable API Management. Requires a valid license token. + apimanagement: ++ # -- Set to true in order to enable API Management. Requires a valid license token. ++ enabled: ++ admission: ++ # -- WebHook admission server listen address. Default: "0.0.0.0:9943". ++ listenAddr: ++ # -- Certificate of the WebHook admission server. Default: "hub-agent-cert". ++ secretName: ++ + metrics: + opentelemetry: + # -- Set to true to enable OpenTelemetry metrics exporter of Traefik Hub. +``` + +## 28.1.0-beta.1 ![AppVersion: v3.0.0](https://img.shields.io/static/v1?label=AppVersion&message=v3.0.0&color=success&logo=) ![Kubernetes: >=1.22.0-0](https://img.shields.io/static/v1?label=Kubernetes&message=%3E%3D1.22.0-0&color=informational&logo=kubernetes) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2024-04-30 + +* feat: :rocket: add initial support for Traefik Hub Api Gateway +* chore(release): 🚀 publish v28.1.0-beta.1 + +## 28.0.0 ![AppVersion: v3.0.0](https://img.shields.io/static/v1?label=AppVersion&message=v3.0.0&color=success&logo=) ![Kubernetes: >=1.22.0-0](https://img.shields.io/static/v1?label=Kubernetes&message=%3E%3D1.22.0-0&color=informational&logo=kubernetes) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2024-04-30 + +* style: 🎨 consistent capitalization on `--entryPoints` CLI flag +* fix: 🐛 only expose http3 port on service when TCP variant is exposed +* fix: 🐛 logs filters on status codes +* feat: ✨ add support of `experimental-v3.0` unstable version +* feat: ability to override liveness and readiness probe paths +* feat(ports): add transport options +* chore(release): publish v28.0.0 + +### Default value changes + +```diff +diff --git a/traefik/values.yaml b/traefik/values.yaml +index c0d72d8..2bff10d 100644 +--- a/traefik/values.yaml ++++ b/traefik/values.yaml +@@ -38,6 +38,12 @@ deployment: + ## Override the liveness/readiness scheme. Useful for getting ping to + ## respond on websecure entryPoint. + # healthchecksScheme: HTTPS ++ ## Override the readiness path. ++ ## Default: /ping ++ # readinessPath: /ping ++ # Override the liveness path. ++ # Default: /ping ++ # livenessPath: /ping + # -- Additional deployment annotations (e.g. for jaeger-operator sidecar injection) + annotations: {} + # -- Additional deployment labels (e.g. for filtering deployment by custom labels) +@@ -648,15 +654,28 @@ ports: + # (Optional) + # priority: 10 + # +- # Trust forwarded headers information (X-Forwarded-*). ++ # -- Trust forwarded headers information (X-Forwarded-*). + # forwardedHeaders: + # trustedIPs: [] + # insecure: false + # +- # Enable the Proxy Protocol header parsing for the entry point ++ # -- Enable the Proxy Protocol header parsing for the entry point + # proxyProtocol: + # trustedIPs: [] + # insecure: false ++ # ++ # -- Set transport settings for the entrypoint; see also ++ # https://doc.traefik.io/traefik/routing/entrypoints/#transport ++ transport: ++ respondingTimeouts: ++ readTimeout: ++ writeTimeout: ++ idleTimeout: ++ lifeCycle: ++ requestAcceptGraceTimeout: ++ graceTimeOut: ++ keepAliveMaxRequests: ++ keepAliveMaxTime: + websecure: + ## -- Enable this entrypoint as a default entrypoint. When a service doesn't explicitly set an entrypoint it will only use this entrypoint. + # asDefault: true +@@ -684,16 +703,29 @@ ports: + enabled: false + # advertisedPort: 4443 + # +- ## -- Trust forwarded headers information (X-Forwarded-*). ++ # -- Trust forwarded headers information (X-Forwarded-*). + # forwardedHeaders: + # trustedIPs: [] + # insecure: false + # +- ## -- Enable the Proxy Protocol header parsing for the entry point ++ # -- Enable the Proxy Protocol header parsing for the entry point + # proxyProtocol: + # trustedIPs: [] + # insecure: false + # ++ # -- Set transport settings for the entrypoint; see also ++ # https://doc.traefik.io/traefik/routing/entrypoints/#transport ++ transport: ++ respondingTimeouts: ++ readTimeout: ++ writeTimeout: ++ idleTimeout: ++ lifeCycle: ++ requestAcceptGraceTimeout: ++ graceTimeOut: ++ keepAliveMaxRequests: ++ keepAliveMaxTime: ++ # + ## Set TLS at the entrypoint + ## https://doc.traefik.io/traefik/routing/entrypoints/#tls + tls: +``` + +## 28.0.0-rc1 ![AppVersion: v3.0.0-rc5](https://img.shields.io/static/v1?label=AppVersion&message=v3.0.0-rc5&color=success&logo=) ![Kubernetes: >=1.16.0-0](https://img.shields.io/static/v1?label=Kubernetes&message=%3E%3D1.16.0-0&color=informational&logo=kubernetes) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2024-04-17 + +**Upgrade Notes** + +This is a major breaking upgrade. [Migration guide](https://doc.traefik.io/traefik/v3.0/migration/v2-to-v3/) have been applied on the chart. + +It needs a Kubernetes v1.22 or higher. +All CRDs using _API Group_ `traefik.containo.us` are not supported anymore in Traefik Proxy v3 + +CRDs needs to be upgraded: `kubectl apply --server-side --force-conflicts -k https://github.com/traefik/traefik-helm-chart/traefik/crds/` + +After upgrade, CRDs with _API Group_ `traefik.containo.us` can be removed: + +```shell +kubectl delete crds \ + ingressroutes.traefik.containo.us \ + ingressroutetcps.traefik.containo.us \ + ingressrouteudps.traefik.containo.us \ + middlewares.traefik.containo.us \ + middlewaretcps.traefik.containo.us \ + serverstransports.traefik.containo.us \ + tlsoptions.traefik.containo.us \ + tlsstores.traefik.containo.us \ + traefikservices.traefik.containo.us +``` + +**Changes** + +* feat(podtemplate): set GOMEMLIMIT, GOMAXPROCS when limits are defined +* feat: ✨ fail gracefully when required port number is not set +* feat!: :boom: initial support of Traefik Proxy v3 +* docs: 📚️ improve EXAMPLES on acme resolver +* chore(release): 🚀 publish v28 rc1 + +### Default value changes + +```diff +diff --git a/traefik/values.yaml b/traefik/values.yaml +index cd9fb6e..c0d72d8 100644 +--- a/traefik/values.yaml ++++ b/traefik/values.yaml +@@ -120,12 +120,13 @@ ingressClass: + isDefaultClass: true + # name: my-custom-class + ++core: ++ # -- Can be used to use globally v2 router syntax ++ # See https://doc.traefik.io/traefik/v3.0/migration/v2-to-v3/#new-v3-syntax-notable-changes ++ defaultRuleSyntax: ++ + # Traefik experimental features + experimental: +- # This value is no longer used, set the image.tag to a semver higher than 3.0, e.g. "v3.0.0-beta3" +- # v3: +- # -- Enable traefik version 3 +- + # -- Enable traefik experimental plugins + plugins: {} + # demo: +@@ -309,7 +310,7 @@ logs: + # format: json + # By default, the level is set to ERROR. + # -- Alternative logging levels are DEBUG, PANIC, FATAL, ERROR, WARN, and INFO. +- level: ERROR ++ level: INFO + access: + # -- To enable access logs + enabled: false +@@ -328,6 +329,8 @@ logs: + # statuscodes: "200,300-302" + # retryattempts: true + # minduration: 10ms ++ # -- Enables accessLogs for internal resources. Default: false. ++ addInternals: + fields: + general: + # -- Available modes: keep, drop, redact. +@@ -347,6 +350,9 @@ logs: + # Content-Type: keep + + metrics: ++ ## -- Enable metrics for internal resources. Default: false ++ addInternals: ++ + ## -- Prometheus is enabled by default. + ## -- It can be disabled by setting "prometheus: null" + prometheus: +@@ -376,31 +382,6 @@ metrics: + # # addRoutersLabels: true + # ## Enable metrics on services. Default=true + # # addServicesLabels: false +- # influxdb: +- # ## Address instructs exporter to send metrics to influxdb at this address. +- # address: localhost:8089 +- # ## InfluxDB's address protocol (udp or http). Default="udp" +- # protocol: udp +- # ## InfluxDB database used when protocol is http. Default="" +- # # database: "" +- # ## InfluxDB retention policy used when protocol is http. Default="" +- # # retentionPolicy: "" +- # ## InfluxDB username (only with http). Default="" +- # # username: "" +- # ## InfluxDB password (only with http). Default="" +- # # password: "" +- # ## The interval used by the exporter to push metrics to influxdb. Default=10s +- # # pushInterval: 30s +- # ## Additional labels (influxdb tags) on all metrics. +- # # additionalLabels: +- # # env: production +- # # foo: bar +- # ## Enable metrics on entry points. Default=true +- # # addEntryPointsLabels: false +- # ## Enable metrics on routers. Default=false +- # # addRoutersLabels: true +- # ## Enable metrics on services. Default=true +- # # addServicesLabels: false + # influxdb2: + # ## Address instructs exporter to send metrics to influxdb v2 at this address. + # address: localhost:8086 +@@ -435,43 +416,53 @@ metrics: + # # addRoutersLabels: true + # ## Enable metrics on services. Default=true + # # addServicesLabels: false +- # openTelemetry: +- # ## Address of the OpenTelemetry Collector to send metrics to. +- # address: "localhost:4318" +- # ## Enable metrics on entry points. +- # addEntryPointsLabels: true +- # ## Enable metrics on routers. +- # addRoutersLabels: true +- # ## Enable metrics on services. +- # addServicesLabels: true +- # ## Explicit boundaries for Histogram data points. +- # explicitBoundaries: +- # - "0.1" +- # - "0.3" +- # - "1.2" +- # - "5.0" +- # ## Additional headers sent with metrics by the reporter to the OpenTelemetry Collector. +- # headers: +- # foo: bar +- # test: test +- # ## Allows reporter to send metrics to the OpenTelemetry Collector without using a secured protocol. +- # insecure: true +- # ## Interval at which metrics are sent to the OpenTelemetry Collector. +- # pushInterval: 10s +- # ## Allows to override the default URL path used for sending metrics. This option has no effect when using gRPC transport. +- # path: /foo/v1/traces +- # ## Defines the TLS configuration used by the reporter to send metrics to the OpenTelemetry Collector. +- # tls: +- # ## The path to the certificate authority, it defaults to the system bundle. +- # ca: path/to/ca.crt +- # ## The path to the public certificate. When using this option, setting the key option is required. +- # cert: path/to/foo.cert +- # ## The path to the private key. When using this option, setting the cert option is required. +- # key: path/to/key.key +- # ## If set to true, the TLS connection accepts any certificate presented by the server regardless of the hostnames it covers. +- # insecureSkipVerify: true +- # ## This instructs the reporter to send metrics to the OpenTelemetry Collector using gRPC. +- # grpc: true ++ otlp: ++ # -- Set to true in order to enable the OpenTelemetry metrics ++ enabled: false ++ # -- Enable metrics on entry points. Default: true ++ addEntryPointsLabels: ++ # -- Enable metrics on routers. Default: false ++ addRoutersLabels: ++ # -- Enable metrics on services. Default: true ++ addServicesLabels: ++ # -- Explicit boundaries for Histogram data points. Default: [.005, .01, .025, .05, .1, .25, .5, 1, 2.5, 5, 10] ++ explicitBoundaries: ++ # -- Interval at which metrics are sent to the OpenTelemetry Collector. Default: 10s ++ pushInterval: ++ http: ++ # -- Set to true in order to send metrics to the OpenTelemetry Collector using HTTP. ++ enabled: false ++ # -- Format: ://:. Default: http://localhost:4318/v1/metrics ++ endpoint: ++ # -- Additional headers sent with metrics by the reporter to the OpenTelemetry Collector. ++ headers: ++ ## Defines the TLS configuration used by the reporter to send metrics to the OpenTelemetry Collector. ++ tls: ++ # -- The path to the certificate authority, it defaults to the system bundle. ++ ca: ++ # -- The path to the public certificate. When using this option, setting the key option is required. ++ cert: ++ # -- The path to the private key. When using this option, setting the cert option is required. ++ key: ++ # -- When set to true, the TLS connection accepts any certificate presented by the server regardless of the hostnames it covers. ++ insecureSkipVerify: ++ grpc: ++ # -- Set to true in order to send metrics to the OpenTelemetry Collector using gRPC ++ enabled: false ++ # -- Format: ://:. Default: http://localhost:4318/v1/metrics ++ endpoint: ++ # -- Allows reporter to send metrics to the OpenTelemetry Collector without using a secured protocol. ++ insecure: ++ ## Defines the TLS configuration used by the reporter to send metrics to the OpenTelemetry Collector. ++ tls: ++ # -- The path to the certificate authority, it defaults to the system bundle. ++ ca: ++ # -- The path to the public certificate. When using this option, setting the key option is required. ++ cert: ++ # -- The path to the private key. When using this option, setting the cert option is required. ++ key: ++ # -- When set to true, the TLS connection accepts any certificate presented by the server regardless of the hostnames it covers. ++ insecureSkipVerify: + + ## -- enable optional CRDs for Prometheus Operator + ## +@@ -524,51 +515,46 @@ metrics: + + ## Tracing + # -- https://doc.traefik.io/traefik/observability/tracing/overview/ +-tracing: {} +-# openTelemetry: # traefik v3+ only +-# grpc: true +-# insecure: true +-# address: localhost:4317 +-# instana: +-# localAgentHost: 127.0.0.1 +-# localAgentPort: 42699 +-# logLevel: info +-# enableAutoProfile: true +-# datadog: +-# localAgentHostPort: 127.0.0.1:8126 +-# debug: false +-# globalTag: "" +-# prioritySampling: false +-# jaeger: +-# samplingServerURL: http://localhost:5778/sampling +-# samplingType: const +-# samplingParam: 1.0 +-# localAgentHostPort: 127.0.0.1:6831 +-# gen128Bit: false +-# propagation: jaeger +-# traceContextHeaderName: uber-trace-id +-# disableAttemptReconnecting: true +-# collector: +-# endpoint: "" +-# user: "" +-# password: "" +-# zipkin: +-# httpEndpoint: http://localhost:9411/api/v2/spans +-# sameSpan: false +-# id128Bit: true +-# sampleRate: 1.0 +-# haystack: +-# localAgentHost: 127.0.0.1 +-# localAgentPort: 35000 +-# globalTag: "" +-# traceIDHeaderName: "" +-# parentIDHeaderName: "" +-# spanIDHeaderName: "" +-# baggagePrefixHeaderName: "" +-# elastic: +-# serverURL: http://localhost:8200 +-# secretToken: "" +-# serviceEnvironment: "" ++tracing: ++ # -- Enables tracing for internal resources. Default: false. ++ addInternals: ++ otlp: ++ # -- See https://doc.traefik.io/traefik/v3.0/observability/tracing/opentelemetry/ ++ enabled: false ++ http: ++ # -- Set to true in order to send metrics to the OpenTelemetry Collector using HTTP. ++ enabled: false ++ # -- Format: ://:. Default: http://localhost:4318/v1/metrics ++ endpoint: ++ # -- Additional headers sent with metrics by the reporter to the OpenTelemetry Collector. ++ headers: ++ ## Defines the TLS configuration used by the reporter to send metrics to the OpenTelemetry Collector. ++ tls: ++ # -- The path to the certificate authority, it defaults to the system bundle. ++ ca: ++ # -- The path to the public certificate. When using this option, setting the key option is required. ++ cert: ++ # -- The path to the private key. When using this option, setting the cert option is required. ++ key: ++ # -- When set to true, the TLS connection accepts any certificate presented by the server regardless of the hostnames it covers. ++ insecureSkipVerify: ++ grpc: ++ # -- Set to true in order to send metrics to the OpenTelemetry Collector using gRPC ++ enabled: false ++ # -- Format: ://:. Default: http://localhost:4318/v1/metrics ++ endpoint: ++ # -- Allows reporter to send metrics to the OpenTelemetry Collector without using a secured protocol. ++ insecure: ++ ## Defines the TLS configuration used by the reporter to send metrics to the OpenTelemetry Collector. ++ tls: ++ # -- The path to the certificate authority, it defaults to the system bundle. ++ ca: ++ # -- The path to the public certificate. When using this option, setting the key option is required. ++ cert: ++ # -- The path to the private key. When using this option, setting the cert option is required. ++ key: ++ # -- When set to true, the TLS connection accepts any certificate presented by the server regardless of the hostnames it covers. ++ insecureSkipVerify: + + # -- Global command arguments to be passed to all traefik's pods + globalArguments: +@@ -756,7 +742,6 @@ ports: + # default: + # labels: {} + # sniStrict: true +-# preferServerCipherSuites: true + # custom-options: + # labels: {} + # curvePreferences: +``` + +## 27.0.0 ![AppVersion: v2.11.0](https://img.shields.io/static/v1?label=AppVersion&message=v2.11.0&color=success&logo=) ![Kubernetes: >=1.16.0-0](https://img.shields.io/static/v1?label=Kubernetes&message=%3E%3D1.16.0-0&color=informational&logo=kubernetes) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2024-04-02 + +**Upgrade notes** + +Custom services and port exposure have been redesigned, requiring the following changes: +- if you were overriding port exposure behavior using the `expose` or `exposeInternal` flags, you should replace them with a service name to boolean mapping, i.e. replace this: + +```yaml +ports: + web: + expose: false + exposeInternal: true +``` + +with this: + +```yaml +ports: + web: + expose: + default: false + internal: true +``` + +- if you were previously using the `service.internal` value, you should migrate the values to the `service.additionalServices.internal` value instead; this should yield the same results, but make sure to carefully check for any changes! + +**Changes** + +* fix: remove null annotations on dashboard `IngressRoute` +* fix(rbac): do not create clusterrole for namespace deployment on Traefik v3 +* feat: restrict access to secrets +* feat!: :boom: refactor custom services and port exposure +* chore(release): 🚀 publish v27.0.0 + +### Default value changes + +```diff +diff --git a/traefik/values.yaml b/traefik/values.yaml +index dbd078f..363871d 100644 +--- a/traefik/values.yaml ++++ b/traefik/values.yaml +@@ -250,6 +250,9 @@ providers: + # -- Array of namespaces to watch. If left empty, Traefik watches all namespaces. + namespaces: [] + # - "default" ++ # Disable cluster IngressClass Lookup - Requires Traefik V3. ++ # When combined with rbac.namespaced: true, ClusterRole will not be created and ingresses must use kubernetes.io/ingress.class annotation instead of spec.ingressClassName. ++ disableIngressClassLookup: false + # IP used for Kubernetes Ingress endpoints + publishedService: + enabled: false +@@ -626,22 +629,20 @@ ports: + # -- You SHOULD NOT expose the traefik port on production deployments. + # If you want to access it from outside your cluster, + # use `kubectl port-forward` or create a secure ingress +- expose: false ++ expose: ++ default: false + # -- The exposed port for this service + exposedPort: 9000 + # -- The port protocol (TCP/UDP) + protocol: TCP +- # -- Defines whether the port is exposed on the internal service; +- # note that ports exposed on the default service are exposed on the internal +- # service by default as well. +- exposeInternal: false + web: + ## -- Enable this entrypoint as a default entrypoint. When a service doesn't explicitly set an entrypoint it will only use this entrypoint. + # asDefault: true + port: 8000 + # hostPort: 8000 + # containerPort: 8000 +- expose: true ++ expose: ++ default: true + exposedPort: 80 + ## -- Different target traefik port on the cluster, useful for IP type LB + # targetPort: 80 +@@ -650,10 +651,6 @@ ports: + # -- Use nodeport if set. This is useful if you have configured Traefik in a + # LoadBalancer. + # nodePort: 32080 +- # -- Defines whether the port is exposed on the internal service; +- # note that ports exposed on the default service are exposed on the internal +- # service by default as well. +- exposeInternal: false + # Port Redirections + # Added in 2.2, you can make permanent redirects via entrypoints. + # https://docs.traefik.io/routing/entrypoints/#redirection +@@ -677,17 +674,14 @@ ports: + port: 8443 + # hostPort: 8443 + # containerPort: 8443 +- expose: true ++ expose: ++ default: true + exposedPort: 443 + ## -- Different target traefik port on the cluster, useful for IP type LB + # targetPort: 80 + ## -- The port protocol (TCP/UDP) + protocol: TCP + # nodePort: 32443 +- # -- Defines whether the port is exposed on the internal service; +- # note that ports exposed on the default service are exposed on the internal +- # service by default as well. +- exposeInternal: false + ## -- Specify an application protocol. This may be used as a hint for a Layer 7 load balancer. + # appProtocol: https + # +@@ -744,15 +738,12 @@ ports: + # -- You may not want to expose the metrics port on production deployments. + # If you want to access it from outside your cluster, + # use `kubectl port-forward` or create a secure ingress +- expose: false ++ expose: ++ default: false + # -- The exposed port for this service + exposedPort: 9100 + # -- The port protocol (TCP/UDP) + protocol: TCP +- # -- Defines whether the port is exposed on the internal service; +- # note that ports exposed on the default service are exposed on the internal +- # service by default as well. +- exposeInternal: false + + # -- TLS Options are created as TLSOption CRDs + # https://doc.traefik.io/traefik/https/tls/#tls-options +@@ -814,6 +805,7 @@ service: + # - IPv4 + # - IPv6 + ## ++ additionalServices: {} + ## -- An additional and optional internal Service. + ## Same parameters as external Service + # internal: +@@ -899,11 +891,14 @@ hostNetwork: false + rbac: + enabled: true + # If set to false, installs ClusterRole and ClusterRoleBinding so Traefik can be used across namespaces. +- # If set to true, installs Role and RoleBinding. Providers will only watch target namespace. ++ # If set to true, installs Role and RoleBinding instead of ClusterRole/ClusterRoleBinding. Providers will only watch target namespace. ++ # When combined with providers.kubernetesIngress.disableIngressClassLookup: true and Traefik V3, ClusterRole to watch IngressClass is also disabled. + namespaced: false + # Enable user-facing roles + # https://kubernetes.io/docs/reference/access-authn-authz/rbac/#user-facing-roles + # aggregateTo: [ "admin" ] ++ # List of Kubernetes secrets that are accessible for Traefik. If empty, then access is granted to every secret. ++ secretResourceNames: [] + + # -- Enable to create a PodSecurityPolicy and assign it to the Service Account via RoleBinding or ClusterRoleBinding + podSecurityPolicy: +``` + +## 26.1.0 ![AppVersion: v2.11.0](https://img.shields.io/static/v1?label=AppVersion&message=v2.11.0&color=success&logo=) ![Kubernetes: >=1.16.0-0](https://img.shields.io/static/v1?label=Kubernetes&message=%3E%3D1.16.0-0&color=informational&logo=kubernetes) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2024-02-19 + +* fix: 🐛 set runtimeClassName at pod level +* fix: 🐛 missing quote on experimental plugin args +* fix: update traefik v3 serverstransporttcps CRD +* feat: set runtimeClassName on pod spec +* feat: create v1 Gateway and GatewayClass Version for Traefik v3 +* feat: allow exposure of ports on internal service only +* doc: fix invalid suggestion on TLSOption (#996) +* chore: 🔧 update maintainers +* chore: 🔧 promote jnoordsij to Traefik Helm Chart maintainer +* chore(release): 🚀 publish v26.1.0 +* chore(deps): update traefik docker tag to v2.11.0 +* chore(deps): update traefik docker tag to v2.10.7 +* chore(crds): update definitions for traefik v2.11 + +### Default value changes + +```diff +diff --git a/traefik/values.yaml b/traefik/values.yaml +index f9dac91..dbd078f 100644 +--- a/traefik/values.yaml ++++ b/traefik/values.yaml +@@ -100,6 +100,8 @@ deployment: + # port: 9000 + # host: localhost + # scheme: HTTP ++ # -- Set a runtimeClassName on pod ++ runtimeClassName: + + # -- Pod disruption budget + podDisruptionBudget: +@@ -629,6 +631,10 @@ ports: + exposedPort: 9000 + # -- The port protocol (TCP/UDP) + protocol: TCP ++ # -- Defines whether the port is exposed on the internal service; ++ # note that ports exposed on the default service are exposed on the internal ++ # service by default as well. ++ exposeInternal: false + web: + ## -- Enable this entrypoint as a default entrypoint. When a service doesn't explicitly set an entrypoint it will only use this entrypoint. + # asDefault: true +@@ -644,6 +650,10 @@ ports: + # -- Use nodeport if set. This is useful if you have configured Traefik in a + # LoadBalancer. + # nodePort: 32080 ++ # -- Defines whether the port is exposed on the internal service; ++ # note that ports exposed on the default service are exposed on the internal ++ # service by default as well. ++ exposeInternal: false + # Port Redirections + # Added in 2.2, you can make permanent redirects via entrypoints. + # https://docs.traefik.io/routing/entrypoints/#redirection +@@ -674,6 +684,10 @@ ports: + ## -- The port protocol (TCP/UDP) + protocol: TCP + # nodePort: 32443 ++ # -- Defines whether the port is exposed on the internal service; ++ # note that ports exposed on the default service are exposed on the internal ++ # service by default as well. ++ exposeInternal: false + ## -- Specify an application protocol. This may be used as a hint for a Layer 7 load balancer. + # appProtocol: https + # +@@ -735,6 +749,10 @@ ports: + exposedPort: 9100 + # -- The port protocol (TCP/UDP) + protocol: TCP ++ # -- Defines whether the port is exposed on the internal service; ++ # note that ports exposed on the default service are exposed on the internal ++ # service by default as well. ++ exposeInternal: false + + # -- TLS Options are created as TLSOption CRDs + # https://doc.traefik.io/traefik/https/tls/#tls-options +@@ -745,7 +763,7 @@ ports: + # labels: {} + # sniStrict: true + # preferServerCipherSuites: true +-# customOptions: ++# custom-options: + # labels: {} + # curvePreferences: + # - CurveP521 +@@ -796,7 +814,7 @@ service: + # - IPv4 + # - IPv6 + ## +- ## -- An additionnal and optional internal Service. ++ ## -- An additional and optional internal Service. + ## Same parameters as external Service + # internal: + # type: ClusterIP +``` + +## 26.0.0 ![AppVersion: v2.10.6](https://img.shields.io/static/v1?label=AppVersion&message=v2.10.6&color=success&logo=) ![Kubernetes: >=1.16.0-0](https://img.shields.io/static/v1?label=Kubernetes&message=%3E%3D1.16.0-0&color=informational&logo=kubernetes) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2023-12-05 + +* fix: 🐛 improve confusing suggested value on openTelemetry.grpc +* fix: 🐛 declare http3 udp port, with or without hostport +* feat: 💥 deployment.podannotations support interpolation with tpl +* feat: allow update of namespace policy for websecure listener +* feat: allow defining startupProbe +* feat: add file provider +* feat: :boom: unify plugin import between traefik and this chart +* chore(release): 🚀 publish v26 +* chore(deps): update traefik docker tag to v2.10.6 +* Release namespace for Prometheus Operator resources + +### Default value changes + +```diff +diff --git a/traefik/values.yaml b/traefik/values.yaml +index 71e377e..f9dac91 100644 +--- a/traefik/values.yaml ++++ b/traefik/values.yaml +@@ -40,6 +40,7 @@ deployment: + # -- Additional deployment labels (e.g. for filtering deployment by custom labels) + labels: {} + # -- Additional pod annotations (e.g. for mesh injection or prometheus scraping) ++ # It supports templating. One can set it with values like traefik/name: '{{ template "traefik.name" . }}' + podAnnotations: {} + # -- Additional Pod labels (e.g. for filtering Pod by custom labels) + podLabels: {} +@@ -119,10 +120,12 @@ experimental: + # This value is no longer used, set the image.tag to a semver higher than 3.0, e.g. "v3.0.0-beta3" + # v3: + # -- Enable traefik version 3 +- # enabled: false +- plugins: +- # -- Enable traefik experimental plugins +- enabled: false ++ ++ # -- Enable traefik experimental plugins ++ plugins: {} ++ # demo: ++ # moduleName: github.com/traefik/plugindemo ++ # version: v0.2.1 + kubernetesGateway: + # -- Enable traefik experimental GatewayClass CRD + enabled: false +@@ -206,6 +209,17 @@ livenessProbe: + # -- The number of seconds to wait for a probe response before considering it as failed. + timeoutSeconds: 2 + ++# -- Define Startup Probe for container: https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/#define-startup-probes ++# eg. ++# `startupProbe: ++# exec: ++# command: ++# - mycommand ++# - foo ++# initialDelaySeconds: 5 ++# periodSeconds: 5` ++startupProbe: ++ + providers: + kubernetesCRD: + # -- Load Kubernetes IngressRoute provider +@@ -241,6 +255,23 @@ providers: + # By default this Traefik service + # pathOverride: "" + ++ file: ++ # -- Create a file provider ++ enabled: false ++ # -- Allows Traefik to automatically watch for file changes ++ watch: true ++ # -- File content (YAML format, go template supported) (see https://doc.traefik.io/traefik/providers/file/) ++ content: "" ++ # http: ++ # routers: ++ # router0: ++ # entryPoints: ++ # - web ++ # middlewares: ++ # - my-basic-auth ++ # service: service-foo ++ # rule: Path(`/foo`) ++ + # + # -- Add volumes to the traefik pod. The volume name will be passed to tpl. + # This can be used to mount a cert pair or a configmap that holds a config.toml file. +@@ -487,7 +518,7 @@ metrics: + # -- https://doc.traefik.io/traefik/observability/tracing/overview/ + tracing: {} + # openTelemetry: # traefik v3+ only +-# grpc: {} ++# grpc: true + # insecure: true + # address: localhost:4317 + # instana: +``` + +## 25.0.0 ![AppVersion: v2.10.5](https://img.shields.io/static/v1?label=AppVersion&message=v2.10.5&color=success&logo=) ![Kubernetes: >=1.16.0-0](https://img.shields.io/static/v1?label=Kubernetes&message=%3E%3D1.16.0-0&color=informational&logo=kubernetes) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2023-10-23 + +* revert: "fix: 🐛 remove old CRDs using traefik.containo.us" +* fix: 🐛 remove old CRDs using traefik.containo.us +* fix: disable ClusterRole and ClusterRoleBinding when not needed +* fix: detect correctly v3 version when using sha in `image.tag` +* fix: allow updateStrategy.rollingUpdate.maxUnavailable to be passed in as an int or string +* fix: add missing separator in crds +* fix: add Prometheus scraping annotations only if serviceMonitor not created +* feat: ✨ add healthcheck ingressRoute +* feat: :boom: support http redirections and http challenges with cert-manager +* feat: :boom: rework and allow update of namespace policy for Gateway +* docs: Fix typo in the default values file +* chore: remove label whitespace at TLSOption +* chore(release): publish v25.0.0 +* chore(deps): update traefik docker tag to v2.10.5 +* chore(deps): update docker.io/helmunittest/helm-unittest docker tag to v3.12.3 +* chore(ci): 🔧 👷 add e2e test when releasing + +### Default value changes + +```diff +diff --git a/traefik/values.yaml b/traefik/values.yaml +index aeec85c..71e377e 100644 +--- a/traefik/values.yaml ++++ b/traefik/values.yaml +@@ -45,60 +45,60 @@ deployment: + podLabels: {} + # -- Additional containers (e.g. for metric offloading sidecars) + additionalContainers: [] +- # https://docs.datadoghq.com/developers/dogstatsd/unix_socket/?tab=host +- # - name: socat-proxy +- # image: alpine/socat:1.0.5 +- # args: ["-s", "-u", "udp-recv:8125", "unix-sendto:/socket/socket"] +- # volumeMounts: +- # - name: dsdsocket +- # mountPath: /socket ++ # https://docs.datadoghq.com/developers/dogstatsd/unix_socket/?tab=host ++ # - name: socat-proxy ++ # image: alpine/socat:1.0.5 ++ # args: ["-s", "-u", "udp-recv:8125", "unix-sendto:/socket/socket"] ++ # volumeMounts: ++ # - name: dsdsocket ++ # mountPath: /socket + # -- Additional volumes available for use with initContainers and additionalContainers + additionalVolumes: [] +- # - name: dsdsocket +- # hostPath: +- # path: /var/run/statsd-exporter ++ # - name: dsdsocket ++ # hostPath: ++ # path: /var/run/statsd-exporter + # -- Additional initContainers (e.g. for setting file permission as shown below) + initContainers: [] +- # The "volume-permissions" init container is required if you run into permission issues. +- # Related issue: https://github.com/traefik/traefik-helm-chart/issues/396 +- # - name: volume-permissions +- # image: busybox:latest +- # command: ["sh", "-c", "touch /data/acme.json; chmod -v 600 /data/acme.json"] +- # securityContext: +- # runAsNonRoot: true +- # runAsGroup: 65532 +- # runAsUser: 65532 +- # volumeMounts: +- # - name: data +- # mountPath: /data ++ # The "volume-permissions" init container is required if you run into permission issues. ++ # Related issue: https://github.com/traefik/traefik-helm-chart/issues/396 ++ # - name: volume-permissions ++ # image: busybox:latest ++ # command: ["sh", "-c", "touch /data/acme.json; chmod -v 600 /data/acme.json"] ++ # securityContext: ++ # runAsNonRoot: true ++ # runAsGroup: 65532 ++ # runAsUser: 65532 ++ # volumeMounts: ++ # - name: data ++ # mountPath: /data + # -- Use process namespace sharing + shareProcessNamespace: false + # -- Custom pod DNS policy. Apply if `hostNetwork: true` + # dnsPolicy: ClusterFirstWithHostNet + dnsConfig: {} +- # nameservers: +- # - 192.0.2.1 # this is an example +- # searches: +- # - ns1.svc.cluster-domain.example +- # - my.dns.search.suffix +- # options: +- # - name: ndots +- # value: "2" +- # - name: edns0 ++ # nameservers: ++ # - 192.0.2.1 # this is an example ++ # searches: ++ # - ns1.svc.cluster-domain.example ++ # - my.dns.search.suffix ++ # options: ++ # - name: ndots ++ # value: "2" ++ # - name: edns0 + # -- Additional imagePullSecrets + imagePullSecrets: [] +- # - name: myRegistryKeySecretName ++ # - name: myRegistryKeySecretName + # -- Pod lifecycle actions + lifecycle: {} +- # preStop: +- # exec: +- # command: ["/bin/sh", "-c", "sleep 40"] +- # postStart: +- # httpGet: +- # path: /ping +- # port: 9000 +- # host: localhost +- # scheme: HTTP ++ # preStop: ++ # exec: ++ # command: ["/bin/sh", "-c", "sleep 40"] ++ # postStart: ++ # httpGet: ++ # path: /ping ++ # port: 9000 ++ # host: localhost ++ # scheme: HTTP + + # -- Pod disruption budget + podDisruptionBudget: +@@ -116,9 +116,9 @@ ingressClass: + + # Traefik experimental features + experimental: +- #This value is no longer used, set the image.tag to a semver higher than 3.0, e.g. "v3.0.0-beta3" +- #v3: +- # -- Enable traefik version 3 ++ # This value is no longer used, set the image.tag to a semver higher than 3.0, e.g. "v3.0.0-beta3" ++ # v3: ++ # -- Enable traefik version 3 + # enabled: false + plugins: + # -- Enable traefik experimental plugins +@@ -126,9 +126,9 @@ experimental: + kubernetesGateway: + # -- Enable traefik experimental GatewayClass CRD + enabled: false +- gateway: +- # -- Enable traefik regular kubernetes gateway +- enabled: true ++ ## Routes are restricted to namespace of the gateway by default. ++ ## https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1beta1.FromNamespaces ++ # namespacePolicy: All + # certificate: + # group: "core" + # kind: "Secret" +@@ -159,6 +159,22 @@ ingressRoute: + middlewares: [] + # -- TLS options (e.g. secret containing certificate) + tls: {} ++ healthcheck: ++ # -- Create an IngressRoute for the healthcheck probe ++ enabled: false ++ # -- Additional ingressRoute annotations (e.g. for kubernetes.io/ingress.class) ++ annotations: {} ++ # -- Additional ingressRoute labels (e.g. for filtering IngressRoute by custom labels) ++ labels: {} ++ # -- The router match rule used for the healthcheck ingressRoute ++ matchRule: PathPrefix(`/ping`) ++ # -- Specify the allowed entrypoints to use for the healthcheck ingress route, (e.g. traefik, web, websecure). ++ # By default, it's using traefik entrypoint, which is not exposed. ++ entryPoints: ["traefik"] ++ # -- Additional ingressRoute middlewares (e.g. for authentication) ++ middlewares: [] ++ # -- TLS options (e.g. secret containing certificate) ++ tls: {} + + updateStrategy: + # -- Customize updateStrategy: RollingUpdate or OnDelete +@@ -204,10 +220,10 @@ providers: + # labelSelector: environment=production,method=traefik + # -- Array of namespaces to watch. If left empty, Traefik watches all namespaces. + namespaces: [] +- # - "default" ++ # - "default" + + kubernetesIngress: +- # -- Load Kubernetes IngressRoute provider ++ # -- Load Kubernetes Ingress provider + enabled: true + # -- Allows to reference ExternalName services in Ingress + allowExternalNameServices: false +@@ -217,7 +233,7 @@ providers: + # labelSelector: environment=production,method=traefik + # -- Array of namespaces to watch. If left empty, Traefik watches all namespaces. + namespaces: [] +- # - "default" ++ # - "default" + # IP used for Kubernetes Ingress endpoints + publishedService: + enabled: false +@@ -243,9 +259,9 @@ volumes: [] + + # -- Additional volumeMounts to add to the Traefik container + additionalVolumeMounts: [] +- # -- For instance when using a logshipper for access logs +- # - name: traefik-logs +- # mountPath: /var/log/traefik ++# -- For instance when using a logshipper for access logs ++# - name: traefik-logs ++# mountPath: /var/log/traefik + + logs: + general: +@@ -270,26 +286,26 @@ logs: + ## Filtering + # -- https://docs.traefik.io/observability/access-logs/#filtering + filters: {} +- # statuscodes: "200,300-302" +- # retryattempts: true +- # minduration: 10ms ++ # statuscodes: "200,300-302" ++ # retryattempts: true ++ # minduration: 10ms + fields: + general: + # -- Available modes: keep, drop, redact. + defaultmode: keep + # -- Names of the fields to limit. + names: {} +- ## Examples: +- # ClientUsername: drop ++ ## Examples: ++ # ClientUsername: drop + headers: + # -- Available modes: keep, drop, redact. + defaultmode: drop + # -- Names of the headers to limit. + names: {} +- ## Examples: +- # User-Agent: redact +- # Authorization: drop +- # Content-Type: keep ++ ## Examples: ++ # User-Agent: redact ++ # Authorization: drop ++ # Content-Type: keep + + metrics: + ## -- Prometheus is enabled by default. +@@ -308,118 +324,118 @@ metrics: + ## When manualRouting is true, it disables the default internal router in + ## order to allow creating a custom router for prometheus@internal service. + # manualRouting: true +-# datadog: +-# ## Address instructs exporter to send metrics to datadog-agent at this address. +-# address: "127.0.0.1:8125" +-# ## The interval used by the exporter to push metrics to datadog-agent. Default=10s +-# # pushInterval: 30s +-# ## The prefix to use for metrics collection. Default="traefik" +-# # prefix: traefik +-# ## Enable metrics on entry points. Default=true +-# # addEntryPointsLabels: false +-# ## Enable metrics on routers. Default=false +-# # addRoutersLabels: true +-# ## Enable metrics on services. Default=true +-# # addServicesLabels: false +-# influxdb: +-# ## Address instructs exporter to send metrics to influxdb at this address. +-# address: localhost:8089 +-# ## InfluxDB's address protocol (udp or http). Default="udp" +-# protocol: udp +-# ## InfluxDB database used when protocol is http. Default="" +-# # database: "" +-# ## InfluxDB retention policy used when protocol is http. Default="" +-# # retentionPolicy: "" +-# ## InfluxDB username (only with http). Default="" +-# # username: "" +-# ## InfluxDB password (only with http). Default="" +-# # password: "" +-# ## The interval used by the exporter to push metrics to influxdb. Default=10s +-# # pushInterval: 30s +-# ## Additional labels (influxdb tags) on all metrics. +-# # additionalLabels: +-# # env: production +-# # foo: bar +-# ## Enable metrics on entry points. Default=true +-# # addEntryPointsLabels: false +-# ## Enable metrics on routers. Default=false +-# # addRoutersLabels: true +-# ## Enable metrics on services. Default=true +-# # addServicesLabels: false +-# influxdb2: +-# ## Address instructs exporter to send metrics to influxdb v2 at this address. +-# address: localhost:8086 +-# ## Token with which to connect to InfluxDB v2. +-# token: xxx +-# ## Organisation where metrics will be stored. +-# org: "" +-# ## Bucket where metrics will be stored. +-# bucket: "" +-# ## The interval used by the exporter to push metrics to influxdb. Default=10s +-# # pushInterval: 30s +-# ## Additional labels (influxdb tags) on all metrics. +-# # additionalLabels: +-# # env: production +-# # foo: bar +-# ## Enable metrics on entry points. Default=true +-# # addEntryPointsLabels: false +-# ## Enable metrics on routers. Default=false +-# # addRoutersLabels: true +-# ## Enable metrics on services. Default=true +-# # addServicesLabels: false +-# statsd: +-# ## Address instructs exporter to send metrics to statsd at this address. +-# address: localhost:8125 +-# ## The interval used by the exporter to push metrics to influxdb. Default=10s +-# # pushInterval: 30s +-# ## The prefix to use for metrics collection. Default="traefik" +-# # prefix: traefik +-# ## Enable metrics on entry points. Default=true +-# # addEntryPointsLabels: false +-# ## Enable metrics on routers. Default=false +-# # addRoutersLabels: true +-# ## Enable metrics on services. Default=true +-# # addServicesLabels: false +-# openTelemetry: +-# ## Address of the OpenTelemetry Collector to send metrics to. +-# address: "localhost:4318" +-# ## Enable metrics on entry points. +-# addEntryPointsLabels: true +-# ## Enable metrics on routers. +-# addRoutersLabels: true +-# ## Enable metrics on services. +-# addServicesLabels: true +-# ## Explicit boundaries for Histogram data points. +-# explicitBoundaries: +-# - "0.1" +-# - "0.3" +-# - "1.2" +-# - "5.0" +-# ## Additional headers sent with metrics by the reporter to the OpenTelemetry Collector. +-# headers: +-# foo: bar +-# test: test +-# ## Allows reporter to send metrics to the OpenTelemetry Collector without using a secured protocol. +-# insecure: true +-# ## Interval at which metrics are sent to the OpenTelemetry Collector. +-# pushInterval: 10s +-# ## Allows to override the default URL path used for sending metrics. This option has no effect when using gRPC transport. +-# path: /foo/v1/traces +-# ## Defines the TLS configuration used by the reporter to send metrics to the OpenTelemetry Collector. +-# tls: +-# ## The path to the certificate authority, it defaults to the system bundle. +-# ca: path/to/ca.crt +-# ## The path to the public certificate. When using this option, setting the key option is required. +-# cert: path/to/foo.cert +-# ## The path to the private key. When using this option, setting the cert option is required. +-# key: path/to/key.key +-# ## If set to true, the TLS connection accepts any certificate presented by the server regardless of the hostnames it covers. +-# insecureSkipVerify: true +-# ## This instructs the reporter to send metrics to the OpenTelemetry Collector using gRPC. +-# grpc: true +- +-## -- enable optional CRDs for Prometheus Operator +-## ++ # datadog: ++ # ## Address instructs exporter to send metrics to datadog-agent at this address. ++ # address: "127.0.0.1:8125" ++ # ## The interval used by the exporter to push metrics to datadog-agent. Default=10s ++ # # pushInterval: 30s ++ # ## The prefix to use for metrics collection. Default="traefik" ++ # # prefix: traefik ++ # ## Enable metrics on entry points. Default=true ++ # # addEntryPointsLabels: false ++ # ## Enable metrics on routers. Default=false ++ # # addRoutersLabels: true ++ # ## Enable metrics on services. Default=true ++ # # addServicesLabels: false ++ # influxdb: ++ # ## Address instructs exporter to send metrics to influxdb at this address. ++ # address: localhost:8089 ++ # ## InfluxDB's address protocol (udp or http). Default="udp" ++ # protocol: udp ++ # ## InfluxDB database used when protocol is http. Default="" ++ # # database: "" ++ # ## InfluxDB retention policy used when protocol is http. Default="" ++ # # retentionPolicy: "" ++ # ## InfluxDB username (only with http). Default="" ++ # # username: "" ++ # ## InfluxDB password (only with http). Default="" ++ # # password: "" ++ # ## The interval used by the exporter to push metrics to influxdb. Default=10s ++ # # pushInterval: 30s ++ # ## Additional labels (influxdb tags) on all metrics. ++ # # additionalLabels: ++ # # env: production ++ # # foo: bar ++ # ## Enable metrics on entry points. Default=true ++ # # addEntryPointsLabels: false ++ # ## Enable metrics on routers. Default=false ++ # # addRoutersLabels: true ++ # ## Enable metrics on services. Default=true ++ # # addServicesLabels: false ++ # influxdb2: ++ # ## Address instructs exporter to send metrics to influxdb v2 at this address. ++ # address: localhost:8086 ++ # ## Token with which to connect to InfluxDB v2. ++ # token: xxx ++ # ## Organisation where metrics will be stored. ++ # org: "" ++ # ## Bucket where metrics will be stored. ++ # bucket: "" ++ # ## The interval used by the exporter to push metrics to influxdb. Default=10s ++ # # pushInterval: 30s ++ # ## Additional labels (influxdb tags) on all metrics. ++ # # additionalLabels: ++ # # env: production ++ # # foo: bar ++ # ## Enable metrics on entry points. Default=true ++ # # addEntryPointsLabels: false ++ # ## Enable metrics on routers. Default=false ++ # # addRoutersLabels: true ++ # ## Enable metrics on services. Default=true ++ # # addServicesLabels: false ++ # statsd: ++ # ## Address instructs exporter to send metrics to statsd at this address. ++ # address: localhost:8125 ++ # ## The interval used by the exporter to push metrics to influxdb. Default=10s ++ # # pushInterval: 30s ++ # ## The prefix to use for metrics collection. Default="traefik" ++ # # prefix: traefik ++ # ## Enable metrics on entry points. Default=true ++ # # addEntryPointsLabels: false ++ # ## Enable metrics on routers. Default=false ++ # # addRoutersLabels: true ++ # ## Enable metrics on services. Default=true ++ # # addServicesLabels: false ++ # openTelemetry: ++ # ## Address of the OpenTelemetry Collector to send metrics to. ++ # address: "localhost:4318" ++ # ## Enable metrics on entry points. ++ # addEntryPointsLabels: true ++ # ## Enable metrics on routers. ++ # addRoutersLabels: true ++ # ## Enable metrics on services. ++ # addServicesLabels: true ++ # ## Explicit boundaries for Histogram data points. ++ # explicitBoundaries: ++ # - "0.1" ++ # - "0.3" ++ # - "1.2" ++ # - "5.0" ++ # ## Additional headers sent with metrics by the reporter to the OpenTelemetry Collector. ++ # headers: ++ # foo: bar ++ # test: test ++ # ## Allows reporter to send metrics to the OpenTelemetry Collector without using a secured protocol. ++ # insecure: true ++ # ## Interval at which metrics are sent to the OpenTelemetry Collector. ++ # pushInterval: 10s ++ # ## Allows to override the default URL path used for sending metrics. This option has no effect when using gRPC transport. ++ # path: /foo/v1/traces ++ # ## Defines the TLS configuration used by the reporter to send metrics to the OpenTelemetry Collector. ++ # tls: ++ # ## The path to the certificate authority, it defaults to the system bundle. ++ # ca: path/to/ca.crt ++ # ## The path to the public certificate. When using this option, setting the key option is required. ++ # cert: path/to/foo.cert ++ # ## The path to the private key. When using this option, setting the cert option is required. ++ # key: path/to/key.key ++ # ## If set to true, the TLS connection accepts any certificate presented by the server regardless of the hostnames it covers. ++ # insecureSkipVerify: true ++ # ## This instructs the reporter to send metrics to the OpenTelemetry Collector using gRPC. ++ # grpc: true ++ ++ ## -- enable optional CRDs for Prometheus Operator ++ ## + ## Create a dedicated metrics service for use with ServiceMonitor + # service: + # enabled: false +@@ -470,55 +486,55 @@ metrics: + ## Tracing + # -- https://doc.traefik.io/traefik/observability/tracing/overview/ + tracing: {} +- # openTelemetry: # traefik v3+ only +- # grpc: {} +- # insecure: true +- # address: localhost:4317 +- # instana: +- # localAgentHost: 127.0.0.1 +- # localAgentPort: 42699 +- # logLevel: info +- # enableAutoProfile: true +- # datadog: +- # localAgentHostPort: 127.0.0.1:8126 +- # debug: false +- # globalTag: "" +- # prioritySampling: false +- # jaeger: +- # samplingServerURL: http://localhost:5778/sampling +- # samplingType: const +- # samplingParam: 1.0 +- # localAgentHostPort: 127.0.0.1:6831 +- # gen128Bit: false +- # propagation: jaeger +- # traceContextHeaderName: uber-trace-id +- # disableAttemptReconnecting: true +- # collector: +- # endpoint: "" +- # user: "" +- # password: "" +- # zipkin: +- # httpEndpoint: http://localhost:9411/api/v2/spans +- # sameSpan: false +- # id128Bit: true +- # sampleRate: 1.0 +- # haystack: +- # localAgentHost: 127.0.0.1 +- # localAgentPort: 35000 +- # globalTag: "" +- # traceIDHeaderName: "" +- # parentIDHeaderName: "" +- # spanIDHeaderName: "" +- # baggagePrefixHeaderName: "" +- # elastic: +- # serverURL: http://localhost:8200 +- # secretToken: "" +- # serviceEnvironment: "" ++# openTelemetry: # traefik v3+ only ++# grpc: {} ++# insecure: true ++# address: localhost:4317 ++# instana: ++# localAgentHost: 127.0.0.1 ++# localAgentPort: 42699 ++# logLevel: info ++# enableAutoProfile: true ++# datadog: ++# localAgentHostPort: 127.0.0.1:8126 ++# debug: false ++# globalTag: "" ++# prioritySampling: false ++# jaeger: ++# samplingServerURL: http://localhost:5778/sampling ++# samplingType: const ++# samplingParam: 1.0 ++# localAgentHostPort: 127.0.0.1:6831 ++# gen128Bit: false ++# propagation: jaeger ++# traceContextHeaderName: uber-trace-id ++# disableAttemptReconnecting: true ++# collector: ++# endpoint: "" ++# user: "" ++# password: "" ++# zipkin: ++# httpEndpoint: http://localhost:9411/api/v2/spans ++# sameSpan: false ++# id128Bit: true ++# sampleRate: 1.0 ++# haystack: ++# localAgentHost: 127.0.0.1 ++# localAgentPort: 35000 ++# globalTag: "" ++# traceIDHeaderName: "" ++# parentIDHeaderName: "" ++# spanIDHeaderName: "" ++# baggagePrefixHeaderName: "" ++# elastic: ++# serverURL: http://localhost:8200 ++# secretToken: "" ++# serviceEnvironment: "" + + # -- Global command arguments to be passed to all traefik's pods + globalArguments: +- - "--global.checknewversion" +- - "--global.sendanonymoususage" ++- "--global.checknewversion" ++- "--global.sendanonymoususage" + + # + # Configure Traefik static configuration +@@ -531,14 +547,14 @@ additionalArguments: [] + + # -- Environment variables to be passed to Traefik's binary + env: +- - name: POD_NAME +- valueFrom: +- fieldRef: +- fieldPath: metadata.name +- - name: POD_NAMESPACE +- valueFrom: +- fieldRef: +- fieldPath: metadata.namespace ++- name: POD_NAME ++ valueFrom: ++ fieldRef: ++ fieldPath: metadata.name ++- name: POD_NAMESPACE ++ valueFrom: ++ fieldRef: ++ fieldPath: metadata.namespace + # - name: SOME_VAR + # value: some-var-value + # - name: SOME_VAR_FROM_CONFIG_MAP +@@ -600,7 +616,10 @@ ports: + # Port Redirections + # Added in 2.2, you can make permanent redirects via entrypoints. + # https://docs.traefik.io/routing/entrypoints/#redirection +- # redirectTo: websecure ++ # redirectTo: ++ # port: websecure ++ # (Optional) ++ # priority: 10 + # + # Trust forwarded headers information (X-Forwarded-*). + # forwardedHeaders: +@@ -638,14 +657,14 @@ ports: + # advertisedPort: 4443 + # + ## -- Trust forwarded headers information (X-Forwarded-*). +- #forwardedHeaders: +- # trustedIPs: [] +- # insecure: false ++ # forwardedHeaders: ++ # trustedIPs: [] ++ # insecure: false + # + ## -- Enable the Proxy Protocol header parsing for the entry point +- #proxyProtocol: +- # trustedIPs: [] +- # insecure: false ++ # proxyProtocol: ++ # trustedIPs: [] ++ # insecure: false + # + ## Set TLS at the entrypoint + ## https://doc.traefik.io/traefik/routing/entrypoints/#tls +@@ -728,16 +747,16 @@ service: + # -- Additional entries here will be added to the service spec. + # -- Cannot contain type, selector or ports entries. + spec: {} +- # externalTrafficPolicy: Cluster +- # loadBalancerIP: "1.2.3.4" +- # clusterIP: "2.3.4.5" ++ # externalTrafficPolicy: Cluster ++ # loadBalancerIP: "1.2.3.4" ++ # clusterIP: "2.3.4.5" + loadBalancerSourceRanges: [] +- # - 192.168.0.1/32 +- # - 172.16.0.0/16 ++ # - 192.168.0.1/32 ++ # - 172.16.0.0/16 + ## -- Class of the load balancer implementation + # loadBalancerClass: service.k8s.aws/nlb + externalIPs: [] +- # - 1.2.3.4 ++ # - 1.2.3.4 + ## One of SingleStack, PreferDualStack, or RequireDualStack. + # ipFamilyPolicy: SingleStack + ## List of IP families (e.g. IPv4 and/or IPv6). +@@ -789,7 +808,7 @@ persistence: + # It can be used to store TLS certificates, see `storage` in certResolvers + enabled: false + name: data +-# existingClaim: "" ++ # existingClaim: "" + accessMode: ReadWriteOnce + size: 128Mi + # storageClass: "" +@@ -852,12 +871,12 @@ serviceAccountAnnotations: {} + + # -- The resources parameter defines CPU and memory requirements and limits for Traefik's containers. + resources: {} +- # requests: +- # cpu: "100m" +- # memory: "50Mi" +- # limits: +- # cpu: "300m" +- # memory: "150Mi" ++# requests: ++# cpu: "100m" ++# memory: "50Mi" ++# limits: ++# cpu: "300m" ++# memory: "150Mi" + + # -- This example pod anti-affinity forces the scheduler to put traefik pods + # -- on nodes where no other traefik pods are scheduled. +``` + +## 24.0.0 ![AppVersion: v2.10.4](https://img.shields.io/static/v1?label=AppVersion&message=v2.10.4&color=success&logo=) ![Kubernetes: >=1.16.0-0](https://img.shields.io/static/v1?label=Kubernetes&message=%3E%3D1.16.0-0&color=informational&logo=kubernetes) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2023-08-10 + +* fix: 💥 BREAKING CHANGE on healthchecks and traefik port +* fix: tracing.opentelemetry.tls is optional for all values +* fix: http3 support broken when advertisedPort set +* feat: multi namespace RBAC manifests +* chore(tests): 🔧 fix typo on tracing test +* chore(release): 🚀 publish v24.0.0 +* chore(deps): update docker.io/helmunittest/helm-unittest docker tag to v3.12.2 + +### Default value changes + +```diff +diff --git a/traefik/values.yaml b/traefik/values.yaml +index 947ba56..aeec85c 100644 +--- a/traefik/values.yaml ++++ b/traefik/values.yaml +@@ -28,6 +28,13 @@ deployment: + terminationGracePeriodSeconds: 60 + # -- The minimum number of seconds Traefik needs to be up and running before the DaemonSet/Deployment controller considers it available + minReadySeconds: 0 ++ ## Override the liveness/readiness port. This is useful to integrate traefik ++ ## with an external Load Balancer that performs healthchecks. ++ ## Default: ports.traefik.port ++ # healthchecksPort: 9000 ++ ## Override the liveness/readiness scheme. Useful for getting ping to ++ ## respond on websecure entryPoint. ++ # healthchecksScheme: HTTPS + # -- Additional deployment annotations (e.g. for jaeger-operator sidecar injection) + annotations: {} + # -- Additional deployment labels (e.g. for filtering deployment by custom labels) +@@ -112,7 +119,7 @@ experimental: + #This value is no longer used, set the image.tag to a semver higher than 3.0, e.g. "v3.0.0-beta3" + #v3: + # -- Enable traefik version 3 +- # enabled: false ++ # enabled: false + plugins: + # -- Enable traefik experimental plugins + enabled: false +@@ -564,15 +571,6 @@ ports: + # only. + # hostIP: 192.168.100.10 + +- # Override the liveness/readiness port. This is useful to integrate traefik +- # with an external Load Balancer that performs healthchecks. +- # Default: ports.traefik.port +- # healthchecksPort: 9000 +- +- # Override the liveness/readiness scheme. Useful for getting ping to +- # respond on websecure entryPoint. +- # healthchecksScheme: HTTPS +- + # Defines whether the port is exposed if service.type is LoadBalancer or + # NodePort. + # +@@ -877,7 +875,7 @@ affinity: {} + nodeSelector: {} + # -- Tolerations allow the scheduler to schedule pods with matching taints. + tolerations: [] +-# -- You can use topology spread constraints to control ++# -- You can use topology spread constraints to control + # how Pods are spread across your cluster among failure-domains. + topologySpreadConstraints: [] + # This example topologySpreadConstraints forces the scheduler to put traefik pods +``` + +## 23.2.0 ![AppVersion: v2.10.4](https://img.shields.io/static/v1?label=AppVersion&message=v2.10.4&color=success&logo=) ![Kubernetes: >=1.16.0-0](https://img.shields.io/static/v1?label=Kubernetes&message=%3E%3D1.16.0-0&color=informational&logo=kubernetes) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2023-07-27 + +* ⬆️ Upgrade traefik Docker tag to v2.10.3 +* release: :rocket: publish v23.2.0 +* fix: 🐛 update traefik.containo.us CRDs to v2.10 +* fix: 🐛 traefik or metrics port can be disabled +* fix: ingressclass name should be customizable (#864) +* feat: ✨ add support for traefik v3.0.0-beta3 and openTelemetry +* feat: disable allowPrivilegeEscalation +* feat: add pod_name as default in values.yaml +* chore(tests): 🔧 use more accurate asserts on refactor'd isNull test +* chore(deps): update traefik docker tag to v2.10.4 +* chore(deps): update docker.io/helmunittest/helm-unittest docker tag to v3.11.3 + +### Default value changes + +```diff +diff --git a/traefik/values.yaml b/traefik/values.yaml +index 345bbd8..947ba56 100644 +--- a/traefik/values.yaml ++++ b/traefik/values.yaml +@@ -105,12 +105,14 @@ podDisruptionBudget: + ingressClass: + enabled: true + isDefaultClass: true ++ # name: my-custom-class + + # Traefik experimental features + experimental: +- v3: ++ #This value is no longer used, set the image.tag to a semver higher than 3.0, e.g. "v3.0.0-beta3" ++ #v3: + # -- Enable traefik version 3 +- enabled: false ++ # enabled: false + plugins: + # -- Enable traefik experimental plugins + enabled: false +@@ -461,6 +463,10 @@ metrics: + ## Tracing + # -- https://doc.traefik.io/traefik/observability/tracing/overview/ + tracing: {} ++ # openTelemetry: # traefik v3+ only ++ # grpc: {} ++ # insecure: true ++ # address: localhost:4317 + # instana: + # localAgentHost: 127.0.0.1 + # localAgentPort: 42699 +@@ -517,7 +523,15 @@ additionalArguments: [] + # - "--log.level=DEBUG" + + # -- Environment variables to be passed to Traefik's binary +-env: [] ++env: ++ - name: POD_NAME ++ valueFrom: ++ fieldRef: ++ fieldPath: metadata.name ++ - name: POD_NAMESPACE ++ valueFrom: ++ fieldRef: ++ fieldPath: metadata.namespace + # - name: SOME_VAR + # value: some-var-value + # - name: SOME_VAR_FROM_CONFIG_MAP +@@ -563,7 +577,7 @@ ports: + # NodePort. + # + # -- You SHOULD NOT expose the traefik port on production deployments. +- # If you want to access it from outside of your cluster, ++ # If you want to access it from outside your cluster, + # use `kubectl port-forward` or create a secure ingress + expose: false + # -- The exposed port for this service +@@ -571,7 +585,7 @@ ports: + # -- The port protocol (TCP/UDP) + protocol: TCP + web: +- ## -- Enable this entrypoint as a default entrypoint. When a service doesn't explicity set an entrypoint it will only use this entrypoint. ++ ## -- Enable this entrypoint as a default entrypoint. When a service doesn't explicitly set an entrypoint it will only use this entrypoint. + # asDefault: true + port: 8000 + # hostPort: 8000 +@@ -600,7 +614,7 @@ ports: + # trustedIPs: [] + # insecure: false + websecure: +- ## -- Enable this entrypoint as a default entrypoint. When a service doesn't explicity set an entrypoint it will only use this entrypoint. ++ ## -- Enable this entrypoint as a default entrypoint. When a service doesn't explicitly set an entrypoint it will only use this entrypoint. + # asDefault: true + port: 8443 + # hostPort: 8443 +@@ -666,7 +680,7 @@ ports: + # NodePort. + # + # -- You may not want to expose the metrics port on production deployments. +- # If you want to access it from outside of your cluster, ++ # If you want to access it from outside your cluster, + # use `kubectl port-forward` or create a secure ingress + expose: false + # -- The exposed port for this service +@@ -880,14 +894,15 @@ topologySpreadConstraints: [] + priorityClassName: "" + + # -- Set the container security context +-# -- To run the container with ports below 1024 this will need to be adjust to run as root ++# -- To run the container with ports below 1024 this will need to be adjusted to run as root + securityContext: + capabilities: + drop: [ALL] + readOnlyRootFilesystem: true ++ allowPrivilegeEscalation: false + + podSecurityContext: +- # /!\ When setting fsGroup, Kubernetes will recursively changes ownership and ++ # /!\ When setting fsGroup, Kubernetes will recursively change ownership and + # permissions for the contents of each volume to match the fsGroup. This can + # be an issue when storing sensitive content like TLS Certificates /!\ + # fsGroup: 65532 +``` + +## 23.1.0 ![AppVersion: v2.10.1](https://img.shields.io/static/v1?label=AppVersion&message=v2.10.1&color=success&logo=) ![Kubernetes: >=1.16.0-0](https://img.shields.io/static/v1?label=Kubernetes&message=%3E%3D1.16.0-0&color=informational&logo=kubernetes) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2023-06-06 + +* release: 🚀 publish v23.1.0 +* fix: 🐛 use k8s version for hpa api version +* fix: 🐛 http3 support on traefik v3 +* fix: use `targetPort` instead of `port` on ServiceMonitor +* feat: ➖ remove Traefik Hub v1 integration +* feat: ✨ add a warning when labelSelector don't match +* feat: common labels for all resources +* feat: allow specifying service loadBalancerClass +* feat: add optional `appProtocol` field on Service ports +* doc: added values README via helm-docs cli + +### Default value changes + +```diff +diff --git a/traefik/values.yaml b/traefik/values.yaml +index 71273cc..345bbd8 100644 +--- a/traefik/values.yaml ++++ b/traefik/values.yaml +@@ -1,70 +1,56 @@ + # Default values for Traefik + image: ++ # -- Traefik image host registry + registry: docker.io ++ # -- Traefik image repository + repository: traefik +- # defaults to appVersion ++ # -- defaults to appVersion + tag: "" ++ # -- Traefik image pull policy + pullPolicy: IfNotPresent + +-# +-# Configure integration with Traefik Hub +-# +-hub: +- ## Enabling Hub will: +- # * enable Traefik Hub integration on Traefik +- # * add `traefikhub-tunl` endpoint +- # * enable Prometheus metrics with addRoutersLabels +- # * enable allowExternalNameServices on KubernetesIngress provider +- # * enable allowCrossNamespace on KubernetesCRD provider +- # * add an internal (ClusterIP) Service, dedicated for Traefik Hub +- enabled: false +- ## Default port can be changed +- # tunnelPort: 9901 +- ## TLS is optional. Insecure is mutually exclusive with any other options +- # tls: +- # insecure: false +- # ca: "/path/to/ca.pem" +- # cert: "/path/to/cert.pem" +- # key: "/path/to/key.pem" ++# -- Add additional label to all resources ++commonLabels: {} + + # + # Configure the deployment + # + deployment: ++ # -- Enable deployment + enabled: true +- # Can be either Deployment or DaemonSet ++ # -- Deployment or DaemonSet + kind: Deployment +- # Number of pods of the deployment (only applies when kind == Deployment) ++ # -- Number of pods of the deployment (only applies when kind == Deployment) + replicas: 1 +- # Number of old history to retain to allow rollback (If not set, default Kubernetes value is set to 10) ++ # -- Number of old history to retain to allow rollback (If not set, default Kubernetes value is set to 10) + # revisionHistoryLimit: 1 +- # Amount of time (in seconds) before Kubernetes will send the SIGKILL signal if Traefik does not shut down ++ # -- Amount of time (in seconds) before Kubernetes will send the SIGKILL signal if Traefik does not shut down + terminationGracePeriodSeconds: 60 +- # The minimum number of seconds Traefik needs to be up and running before the DaemonSet/Deployment controller considers it available ++ # -- The minimum number of seconds Traefik needs to be up and running before the DaemonSet/Deployment controller considers it available + minReadySeconds: 0 +- # Additional deployment annotations (e.g. for jaeger-operator sidecar injection) ++ # -- Additional deployment annotations (e.g. for jaeger-operator sidecar injection) + annotations: {} +- # Additional deployment labels (e.g. for filtering deployment by custom labels) ++ # -- Additional deployment labels (e.g. for filtering deployment by custom labels) + labels: {} +- # Additional pod annotations (e.g. for mesh injection or prometheus scraping) ++ # -- Additional pod annotations (e.g. for mesh injection or prometheus scraping) + podAnnotations: {} +- # Additional Pod labels (e.g. for filtering Pod by custom labels) ++ # -- Additional Pod labels (e.g. for filtering Pod by custom labels) + podLabels: {} +- # Additional containers (e.g. for metric offloading sidecars) ++ # -- Additional containers (e.g. for metric offloading sidecars) + additionalContainers: [] + # https://docs.datadoghq.com/developers/dogstatsd/unix_socket/?tab=host + # - name: socat-proxy +- # image: alpine/socat:1.0.5 +- # args: ["-s", "-u", "udp-recv:8125", "unix-sendto:/socket/socket"] +- # volumeMounts: +- # - name: dsdsocket +- # mountPath: /socket +- # Additional volumes available for use with initContainers and additionalContainers ++ # image: alpine/socat:1.0.5 ++ # args: ["-s", "-u", "udp-recv:8125", "unix-sendto:/socket/socket"] ++ # volumeMounts: ++ # - name: dsdsocket ++ # mountPath: /socket ++ # -- Additional volumes available for use with initContainers and additionalContainers + additionalVolumes: [] + # - name: dsdsocket + # hostPath: + # path: /var/run/statsd-exporter +- # Additional initContainers (e.g. for setting file permission as shown below) ++ # -- Additional initContainers (e.g. for setting file permission as shown below) + initContainers: [] + # The "volume-permissions" init container is required if you run into permission issues. + # Related issue: https://github.com/traefik/traefik-helm-chart/issues/396 +@@ -78,9 +64,9 @@ deployment: + # volumeMounts: + # - name: data + # mountPath: /data +- # Use process namespace sharing ++ # -- Use process namespace sharing + shareProcessNamespace: false +- # Custom pod DNS policy. Apply if `hostNetwork: true` ++ # -- Custom pod DNS policy. Apply if `hostNetwork: true` + # dnsPolicy: ClusterFirstWithHostNet + dnsConfig: {} + # nameservers: +@@ -92,10 +78,10 @@ deployment: + # - name: ndots + # value: "2" + # - name: edns0 +- # Additional imagePullSecrets ++ # -- Additional imagePullSecrets + imagePullSecrets: [] + # - name: myRegistryKeySecretName +- # Pod lifecycle actions ++ # -- Pod lifecycle actions + lifecycle: {} + # preStop: + # exec: +@@ -107,7 +93,7 @@ deployment: + # host: localhost + # scheme: HTTP + +-# Pod disruption budget ++# -- Pod disruption budget + podDisruptionBudget: + enabled: false + # maxUnavailable: 1 +@@ -115,93 +101,112 @@ podDisruptionBudget: + # minAvailable: 0 + # minAvailable: 25% + +-# Create a default IngressClass for Traefik ++# -- Create a default IngressClass for Traefik + ingressClass: + enabled: true + isDefaultClass: true + +-# Enable experimental features ++# Traefik experimental features + experimental: + v3: ++ # -- Enable traefik version 3 + enabled: false + plugins: ++ # -- Enable traefik experimental plugins + enabled: false + kubernetesGateway: ++ # -- Enable traefik experimental GatewayClass CRD + enabled: false + gateway: ++ # -- Enable traefik regular kubernetes gateway + enabled: true + # certificate: + # group: "core" + # kind: "Secret" + # name: "mysecret" +- # By default, Gateway would be created to the Namespace you are deploying Traefik to. ++ # -- By default, Gateway would be created to the Namespace you are deploying Traefik to. + # You may create that Gateway in another namespace, setting its name below: + # namespace: default + # Additional gateway annotations (e.g. for cert-manager.io/issuer) + # annotations: + # cert-manager.io/issuer: letsencrypt + +-# Create an IngressRoute for the dashboard ++## Create an IngressRoute for the dashboard + ingressRoute: + dashboard: ++ # -- Create an IngressRoute for the dashboard + enabled: true +- # Additional ingressRoute annotations (e.g. for kubernetes.io/ingress.class) ++ # -- Additional ingressRoute annotations (e.g. for kubernetes.io/ingress.class) + annotations: {} +- # Additional ingressRoute labels (e.g. for filtering IngressRoute by custom labels) ++ # -- Additional ingressRoute labels (e.g. for filtering IngressRoute by custom labels) + labels: {} +- # The router match rule used for the dashboard ingressRoute ++ # -- The router match rule used for the dashboard ingressRoute + matchRule: PathPrefix(`/dashboard`) || PathPrefix(`/api`) +- # Specify the allowed entrypoints to use for the dashboard ingress route, (e.g. traefik, web, websecure). ++ # -- Specify the allowed entrypoints to use for the dashboard ingress route, (e.g. traefik, web, websecure). + # By default, it's using traefik entrypoint, which is not exposed. + # /!\ Do not expose your dashboard without any protection over the internet /!\ + entryPoints: ["traefik"] +- # Additional ingressRoute middlewares (e.g. for authentication) ++ # -- Additional ingressRoute middlewares (e.g. for authentication) + middlewares: [] +- # TLS options (e.g. secret containing certificate) ++ # -- TLS options (e.g. secret containing certificate) + tls: {} + +-# Customize updateStrategy of traefik pods + updateStrategy: ++ # -- Customize updateStrategy: RollingUpdate or OnDelete + type: RollingUpdate + rollingUpdate: + maxUnavailable: 0 + maxSurge: 1 + +-# Customize liveness and readiness probe values. + readinessProbe: ++ # -- The number of consecutive failures allowed before considering the probe as failed. + failureThreshold: 1 ++ # -- The number of seconds to wait before starting the first probe. + initialDelaySeconds: 2 ++ # -- The number of seconds to wait between consecutive probes. + periodSeconds: 10 ++ # -- The minimum consecutive successes required to consider the probe successful. + successThreshold: 1 ++ # -- The number of seconds to wait for a probe response before considering it as failed. + timeoutSeconds: 2 +- + livenessProbe: ++ # -- The number of consecutive failures allowed before considering the probe as failed. + failureThreshold: 3 ++ # -- The number of seconds to wait before starting the first probe. + initialDelaySeconds: 2 ++ # -- The number of seconds to wait between consecutive probes. + periodSeconds: 10 ++ # -- The minimum consecutive successes required to consider the probe successful. + successThreshold: 1 ++ # -- The number of seconds to wait for a probe response before considering it as failed. + timeoutSeconds: 2 + +-# +-# Configure providers +-# + providers: + kubernetesCRD: ++ # -- Load Kubernetes IngressRoute provider + enabled: true ++ # -- Allows IngressRoute to reference resources in namespace other than theirs + allowCrossNamespace: false ++ # -- Allows to reference ExternalName services in IngressRoute + allowExternalNameServices: false ++ # -- Allows to return 503 when there is no endpoints available + allowEmptyServices: false + # ingressClass: traefik-internal + # labelSelector: environment=production,method=traefik ++ # -- Array of namespaces to watch. If left empty, Traefik watches all namespaces. + namespaces: [] + # - "default" + + kubernetesIngress: ++ # -- Load Kubernetes IngressRoute provider + enabled: true ++ # -- Allows to reference ExternalName services in Ingress + allowExternalNameServices: false ++ # -- Allows to return 503 when there is no endpoints available + allowEmptyServices: false + # ingressClass: traefik-internal + # labelSelector: environment=production,method=traefik ++ # -- Array of namespaces to watch. If left empty, Traefik watches all namespaces. + namespaces: [] + # - "default" + # IP used for Kubernetes Ingress endpoints +@@ -212,13 +217,13 @@ providers: + # pathOverride: "" + + # +-# Add volumes to the traefik pod. The volume name will be passed to tpl. ++# -- Add volumes to the traefik pod. The volume name will be passed to tpl. + # This can be used to mount a cert pair or a configmap that holds a config.toml file. + # After the volume has been mounted, add the configs into traefik by using the `additionalArguments` list below, eg: +-# additionalArguments: ++# `additionalArguments: + # - "--providers.file.filename=/config/dynamic.toml" + # - "--ping" +-# - "--ping.entrypoint=web" ++# - "--ping.entrypoint=web"` + volumes: [] + # - name: public-cert + # mountPath: "/certs" +@@ -227,25 +232,22 @@ volumes: [] + # mountPath: "/config" + # type: configMap + +-# Additional volumeMounts to add to the Traefik container ++# -- Additional volumeMounts to add to the Traefik container + additionalVolumeMounts: [] +- # For instance when using a logshipper for access logs ++ # -- For instance when using a logshipper for access logs + # - name: traefik-logs + # mountPath: /var/log/traefik + +-## Logs +-## https://docs.traefik.io/observability/logs/ + logs: +- ## Traefik logs concern everything that happens to Traefik itself (startup, configuration, events, shutdown, and so on). + general: +- # By default, the logs use a text format (common), but you can ++ # -- By default, the logs use a text format (common), but you can + # also ask for the json format in the format option + # format: json + # By default, the level is set to ERROR. +- # Alternative logging levels are DEBUG, PANIC, FATAL, ERROR, WARN, and INFO. ++ # -- Alternative logging levels are DEBUG, PANIC, FATAL, ERROR, WARN, and INFO. + level: ERROR + access: +- # To enable access logs ++ # -- To enable access logs + enabled: false + ## By default, logs are written using the Common Log Format (CLF) on stdout. + ## To write logs in JSON, use json in the format option. +@@ -256,21 +258,24 @@ logs: + ## This option represents the number of log lines Traefik will keep in memory before writing + ## them to the selected output. In some cases, this option can greatly help performances. + # bufferingSize: 100 +- ## Filtering https://docs.traefik.io/observability/access-logs/#filtering ++ ## Filtering ++ # -- https://docs.traefik.io/observability/access-logs/#filtering + filters: {} + # statuscodes: "200,300-302" + # retryattempts: true + # minduration: 10ms +- ## Fields +- ## https://docs.traefik.io/observability/access-logs/#limiting-the-fieldsincluding-headers + fields: + general: ++ # -- Available modes: keep, drop, redact. + defaultmode: keep ++ # -- Names of the fields to limit. + names: {} + ## Examples: + # ClientUsername: drop + headers: ++ # -- Available modes: keep, drop, redact. + defaultmode: drop ++ # -- Names of the headers to limit. + names: {} + ## Examples: + # User-Agent: redact +@@ -278,10 +283,10 @@ logs: + # Content-Type: keep + + metrics: +- ## Prometheus is enabled by default. +- ## It can be disabled by setting "prometheus: null" ++ ## -- Prometheus is enabled by default. ++ ## -- It can be disabled by setting "prometheus: null" + prometheus: +- ## Entry point used to expose metrics. ++ # -- Entry point used to expose metrics. + entryPoint: metrics + ## Enable metrics on entry points. Default=true + # addEntryPointsLabels: false +@@ -404,11 +409,9 @@ metrics: + # ## This instructs the reporter to send metrics to the OpenTelemetry Collector using gRPC. + # grpc: true + +-## +-## enable optional CRDs for Prometheus Operator ++## -- enable optional CRDs for Prometheus Operator + ## + ## Create a dedicated metrics service for use with ServiceMonitor +- ## When hub.enabled is set to true, it's not needed: it will use hub service. + # service: + # enabled: false + # labels: {} +@@ -455,6 +458,8 @@ metrics: + # summary: "Traefik Down" + # description: "{{ $labels.pod }} on {{ $labels.nodename }} is down" + ++## Tracing ++# -- https://doc.traefik.io/traefik/observability/tracing/overview/ + tracing: {} + # instana: + # localAgentHost: 127.0.0.1 +@@ -497,20 +502,21 @@ tracing: {} + # secretToken: "" + # serviceEnvironment: "" + ++# -- Global command arguments to be passed to all traefik's pods + globalArguments: + - "--global.checknewversion" + - "--global.sendanonymoususage" + + # + # Configure Traefik static configuration +-# Additional arguments to be passed at Traefik's binary ++# -- Additional arguments to be passed at Traefik's binary + # All available options available on https://docs.traefik.io/reference/static-configuration/cli/ + ## Use curly braces to pass values: `helm install --set="additionalArguments={--providers.kubernetesingress.ingressclass=traefik-internal,--log.level=DEBUG}"` + additionalArguments: [] + # - "--providers.kubernetesingress.ingressclass=traefik-internal" + # - "--log.level=DEBUG" + +-# Environment variables to be passed to Traefik's binary ++# -- Environment variables to be passed to Traefik's binary + env: [] + # - name: SOME_VAR + # value: some-var-value +@@ -525,22 +531,20 @@ env: [] + # name: secret-name + # key: secret-key + ++# -- Environment variables to be passed to Traefik's binary from configMaps or secrets + envFrom: [] + # - configMapRef: + # name: config-map-name + # - secretRef: + # name: secret-name + +-# Configure ports + ports: +- # The name of this one can't be changed as it is used for the readiness and +- # liveness probes, but you can adjust its config to your liking + traefik: + port: 9000 +- # Use hostPort if set. ++ # -- Use hostPort if set. + # hostPort: 9000 + # +- # Use hostIP if set. If not set, Kubernetes will default to 0.0.0.0, which ++ # -- Use hostIP if set. If not set, Kubernetes will default to 0.0.0.0, which + # means it's listening on all your interfaces and all your IPs. You may want + # to set this value if you need traefik to listen on specific interface + # only. +@@ -558,27 +562,27 @@ ports: + # Defines whether the port is exposed if service.type is LoadBalancer or + # NodePort. + # +- # You SHOULD NOT expose the traefik port on production deployments. ++ # -- You SHOULD NOT expose the traefik port on production deployments. + # If you want to access it from outside of your cluster, + # use `kubectl port-forward` or create a secure ingress + expose: false +- # The exposed port for this service ++ # -- The exposed port for this service + exposedPort: 9000 +- # The port protocol (TCP/UDP) ++ # -- The port protocol (TCP/UDP) + protocol: TCP + web: +- ## Enable this entrypoint as a default entrypoint. When a service doesn't explicity set an entrypoint it will only use this entrypoint. ++ ## -- Enable this entrypoint as a default entrypoint. When a service doesn't explicity set an entrypoint it will only use this entrypoint. + # asDefault: true + port: 8000 + # hostPort: 8000 + # containerPort: 8000 + expose: true + exposedPort: 80 +- ## Different target traefik port on the cluster, useful for IP type LB ++ ## -- Different target traefik port on the cluster, useful for IP type LB + # targetPort: 80 + # The port protocol (TCP/UDP) + protocol: TCP +- # Use nodeport if set. This is useful if you have configured Traefik in a ++ # -- Use nodeport if set. This is useful if you have configured Traefik in a + # LoadBalancer. + # nodePort: 32080 + # Port Redirections +@@ -596,20 +600,22 @@ ports: + # trustedIPs: [] + # insecure: false + websecure: +- ## Enable this entrypoint as a default entrypoint. When a service doesn't explicity set an entrypoint it will only use this entrypoint. ++ ## -- Enable this entrypoint as a default entrypoint. When a service doesn't explicity set an entrypoint it will only use this entrypoint. + # asDefault: true + port: 8443 + # hostPort: 8443 + # containerPort: 8443 + expose: true + exposedPort: 443 +- ## Different target traefik port on the cluster, useful for IP type LB ++ ## -- Different target traefik port on the cluster, useful for IP type LB + # targetPort: 80 +- ## The port protocol (TCP/UDP) ++ ## -- The port protocol (TCP/UDP) + protocol: TCP + # nodePort: 32443 ++ ## -- Specify an application protocol. This may be used as a hint for a Layer 7 load balancer. ++ # appProtocol: https + # +- ## Enable HTTP/3 on the entrypoint ++ ## -- Enable HTTP/3 on the entrypoint + ## Enabling it will also enable http3 experimental feature + ## https://doc.traefik.io/traefik/routing/entrypoints/#http3 + ## There are known limitations when trying to listen on same ports for +@@ -619,12 +625,12 @@ ports: + enabled: false + # advertisedPort: 4443 + # +- ## Trust forwarded headers information (X-Forwarded-*). ++ ## -- Trust forwarded headers information (X-Forwarded-*). + #forwardedHeaders: + # trustedIPs: [] + # insecure: false + # +- ## Enable the Proxy Protocol header parsing for the entry point ++ ## -- Enable the Proxy Protocol header parsing for the entry point + #proxyProtocol: + # trustedIPs: [] + # insecure: false +@@ -642,33 +648,33 @@ ports: + # - foo.example.com + # - bar.example.com + # +- # One can apply Middlewares on an entrypoint ++ # -- One can apply Middlewares on an entrypoint + # https://doc.traefik.io/traefik/middlewares/overview/ + # https://doc.traefik.io/traefik/routing/entrypoints/#middlewares +- # /!\ It introduces here a link between your static configuration and your dynamic configuration /!\ ++ # -- /!\ It introduces here a link between your static configuration and your dynamic configuration /!\ + # It follows the provider naming convention: https://doc.traefik.io/traefik/providers/overview/#provider-namespace + # middlewares: + # - namespace-name1@kubernetescrd + # - namespace-name2@kubernetescrd + middlewares: [] + metrics: +- # When using hostNetwork, use another port to avoid conflict with node exporter: ++ # -- When using hostNetwork, use another port to avoid conflict with node exporter: + # https://github.com/prometheus/prometheus/wiki/Default-port-allocations + port: 9100 + # hostPort: 9100 + # Defines whether the port is exposed if service.type is LoadBalancer or + # NodePort. + # +- # You may not want to expose the metrics port on production deployments. ++ # -- You may not want to expose the metrics port on production deployments. + # If you want to access it from outside of your cluster, + # use `kubectl port-forward` or create a secure ingress + expose: false +- # The exposed port for this service ++ # -- The exposed port for this service + exposedPort: 9100 +- # The port protocol (TCP/UDP) ++ # -- The port protocol (TCP/UDP) + protocol: TCP + +-# TLS Options are created as TLSOption CRDs ++# -- TLS Options are created as TLSOption CRDs + # https://doc.traefik.io/traefik/https/tls/#tls-options + # When using `labelSelector`, you'll need to set labels on tlsOption accordingly. + # Example: +@@ -684,7 +690,7 @@ ports: + # - CurveP384 + tlsOptions: {} + +-# TLS Store are created as TLSStore CRDs. This is useful if you want to set a default certificate ++# -- TLS Store are created as TLSStore CRDs. This is useful if you want to set a default certificate + # https://doc.traefik.io/traefik/https/tls/#default-certificate + # Example: + # tlsStore: +@@ -693,24 +699,22 @@ tlsOptions: {} + # secretName: tls-cert + tlsStore: {} + +-# Options for the main traefik service, where the entrypoints traffic comes +-# from. + service: + enabled: true +- ## Single service is using `MixedProtocolLBService` feature gate. +- ## When set to false, it will create two Service, one for TCP and one for UDP. ++ ## -- Single service is using `MixedProtocolLBService` feature gate. ++ ## -- When set to false, it will create two Service, one for TCP and one for UDP. + single: true + type: LoadBalancer +- # Additional annotations applied to both TCP and UDP services (e.g. for cloud provider specific config) ++ # -- Additional annotations applied to both TCP and UDP services (e.g. for cloud provider specific config) + annotations: {} +- # Additional annotations for TCP service only ++ # -- Additional annotations for TCP service only + annotationsTCP: {} +- # Additional annotations for UDP service only ++ # -- Additional annotations for UDP service only + annotationsUDP: {} +- # Additional service labels (e.g. for filtering Service by custom labels) ++ # -- Additional service labels (e.g. for filtering Service by custom labels) + labels: {} +- # Additional entries here will be added to the service spec. +- # Cannot contain type, selector or ports entries. ++ # -- Additional entries here will be added to the service spec. ++ # -- Cannot contain type, selector or ports entries. + spec: {} + # externalTrafficPolicy: Cluster + # loadBalancerIP: "1.2.3.4" +@@ -718,6 +722,8 @@ service: + loadBalancerSourceRanges: [] + # - 192.168.0.1/32 + # - 172.16.0.0/16 ++ ## -- Class of the load balancer implementation ++ # loadBalancerClass: service.k8s.aws/nlb + externalIPs: [] + # - 1.2.3.4 + ## One of SingleStack, PreferDualStack, or RequireDualStack. +@@ -728,7 +734,7 @@ service: + # - IPv4 + # - IPv6 + ## +- ## An additionnal and optional internal Service. ++ ## -- An additionnal and optional internal Service. + ## Same parameters as external Service + # internal: + # type: ClusterIP +@@ -739,9 +745,8 @@ service: + # # externalIPs: [] + # # ipFamilies: [ "IPv4","IPv6" ] + +-## Create HorizontalPodAutoscaler object. +-## + autoscaling: ++ # -- Create HorizontalPodAutoscaler object. + enabled: false + # minReplicas: 1 + # maxReplicas: 10 +@@ -766,10 +771,10 @@ autoscaling: + # value: 1 + # periodSeconds: 60 + +-# Enable persistence using Persistent Volume Claims +-# ref: http://kubernetes.io/docs/user-guide/persistent-volumes/ +-# It can be used to store TLS certificates, see `storage` in certResolvers + persistence: ++ # -- Enable persistence using Persistent Volume Claims ++ # ref: http://kubernetes.io/docs/user-guide/persistent-volumes/ ++ # It can be used to store TLS certificates, see `storage` in certResolvers + enabled: false + name: data + # existingClaim: "" +@@ -779,8 +784,10 @@ persistence: + # volumeName: "" + path: /data + annotations: {} +- # subPath: "" # only mount a subpath of the Volume into the pod ++ # -- Only mount a subpath of the Volume into the pod ++ # subPath: "" + ++# -- Certificates resolvers configuration + certResolvers: {} + # letsencrypt: + # # for challenge options cf. https://doc.traefik.io/traefik/https/acme/ +@@ -802,13 +809,13 @@ certResolvers: {} + # # It has to match the path with a persistent volume + # storage: /data/acme.json + +-# If hostNetwork is true, runs traefik in the host network namespace ++# -- If hostNetwork is true, runs traefik in the host network namespace + # To prevent unschedulabel pods due to port collisions, if hostNetwork=true + # and replicas>1, a pod anti-affinity is recommended and will be set if the + # affinity is left as default. + hostNetwork: false + +-# Whether Role Based Access Control objects like roles and rolebindings should be created ++# -- Whether Role Based Access Control objects like roles and rolebindings should be created + rbac: + enabled: true + # If set to false, installs ClusterRole and ClusterRoleBinding so Traefik can be used across namespaces. +@@ -818,19 +825,20 @@ rbac: + # https://kubernetes.io/docs/reference/access-authn-authz/rbac/#user-facing-roles + # aggregateTo: [ "admin" ] + +-# Enable to create a PodSecurityPolicy and assign it to the Service Account via RoleBinding or ClusterRoleBinding ++# -- Enable to create a PodSecurityPolicy and assign it to the Service Account via RoleBinding or ClusterRoleBinding + podSecurityPolicy: + enabled: false + +-# The service account the pods will use to interact with the Kubernetes API ++# -- The service account the pods will use to interact with the Kubernetes API + serviceAccount: + # If set, an existing service account is used + # If not set, a service account is created automatically using the fullname template + name: "" + +-# Additional serviceAccount annotations (e.g. for oidc authentication) ++# -- Additional serviceAccount annotations (e.g. for oidc authentication) + serviceAccountAnnotations: {} + ++# -- The resources parameter defines CPU and memory requirements and limits for Traefik's containers. + resources: {} + # requests: + # cpu: "100m" +@@ -839,8 +847,8 @@ resources: {} + # cpu: "300m" + # memory: "150Mi" + +-# This example pod anti-affinity forces the scheduler to put traefik pods +-# on nodes where no other traefik pods are scheduled. ++# -- This example pod anti-affinity forces the scheduler to put traefik pods ++# -- on nodes where no other traefik pods are scheduled. + # It should be used when hostNetwork: true to prevent port conflicts + affinity: {} + # podAntiAffinity: +@@ -851,11 +859,15 @@ affinity: {} + # app.kubernetes.io/instance: '{{ .Release.Name }}-{{ .Release.Namespace }}' + # topologyKey: kubernetes.io/hostname + ++# -- nodeSelector is the simplest recommended form of node selection constraint. + nodeSelector: {} ++# -- Tolerations allow the scheduler to schedule pods with matching taints. + tolerations: [] ++# -- You can use topology spread constraints to control ++# how Pods are spread across your cluster among failure-domains. + topologySpreadConstraints: [] +-# # This example topologySpreadConstraints forces the scheduler to put traefik pods +-# # on nodes where no other traefik pods are scheduled. ++# This example topologySpreadConstraints forces the scheduler to put traefik pods ++# on nodes where no other traefik pods are scheduled. + # - labelSelector: + # matchLabels: + # app: '{{ template "traefik.name" . }}' +@@ -863,29 +875,33 @@ topologySpreadConstraints: [] + # topologyKey: kubernetes.io/hostname + # whenUnsatisfiable: DoNotSchedule + +-# Pods can have priority. +-# Priority indicates the importance of a Pod relative to other Pods. ++# -- Pods can have priority. ++# -- Priority indicates the importance of a Pod relative to other Pods. + priorityClassName: "" + +-# Set the container security context +-# To run the container with ports below 1024 this will need to be adjust to run as root ++# -- Set the container security context ++# -- To run the container with ports below 1024 this will need to be adjust to run as root + securityContext: + capabilities: + drop: [ALL] + readOnlyRootFilesystem: true + + podSecurityContext: +-# # /!\ When setting fsGroup, Kubernetes will recursively changes ownership and +-# # permissions for the contents of each volume to match the fsGroup. This can +-# # be an issue when storing sensitive content like TLS Certificates /!\ +-# fsGroup: 65532 ++ # /!\ When setting fsGroup, Kubernetes will recursively changes ownership and ++ # permissions for the contents of each volume to match the fsGroup. This can ++ # be an issue when storing sensitive content like TLS Certificates /!\ ++ # fsGroup: 65532 ++ # -- Specifies the policy for changing ownership and permissions of volume contents to match the fsGroup. + fsGroupChangePolicy: "OnRootMismatch" ++ # -- The ID of the group for all containers in the pod to run as. + runAsGroup: 65532 ++ # -- Specifies whether the containers should run as a non-root user. + runAsNonRoot: true ++ # -- The ID of the user for all containers in the pod to run as. + runAsUser: 65532 + + # +-# Extra objects to deploy (value evaluated as a template) ++# -- Extra objects to deploy (value evaluated as a template) + # + # In some cases, it can avoid the need for additional, extended or adhoc deployments. + # See #595 for more details and traefik/tests/values/extra.yaml for example. +@@ -895,5 +911,5 @@ extraObjects: [] + # It will not affect optional CRDs such as `ServiceMonitor` and `PrometheusRules` + # namespaceOverride: traefik + # +-## This will override the default app.kubernetes.io/instance label for all Objects. ++## -- This will override the default app.kubernetes.io/instance label for all Objects. + # instanceLabelOverride: traefik +``` + +## 23.0.1 ![AppVersion: v2.10.1](https://img.shields.io/static/v1?label=AppVersion&message=v2.10.1&color=success&logo=) ![Kubernetes: >=1.16.0-0](https://img.shields.io/static/v1?label=Kubernetes&message=%3E%3D1.16.0-0&color=informational&logo=kubernetes) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2023-04-28 + +* fix: ⬆️ Upgrade traefik Docker tag to v2.10.1 + + +## 23.0.0 ![AppVersion: v2.10.0](https://img.shields.io/static/v1?label=AppVersion&message=v2.10.0&color=success&logo=) ![Kubernetes: >=1.16.0-0](https://img.shields.io/static/v1?label=Kubernetes&message=%3E%3D1.16.0-0&color=informational&logo=kubernetes) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2023-04-26 + +* BREAKING CHANGE: Traefik 2.10 comes with CRDs update on API Group + + +## 22.3.0 ![AppVersion: v2.10.0](https://img.shields.io/static/v1?label=AppVersion&message=v2.10.0&color=success&logo=) ![Kubernetes: >=1.16.0-0](https://img.shields.io/static/v1?label=Kubernetes&message=%3E%3D1.16.0-0&color=informational&logo=kubernetes) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2023-04-25 + +* ⬆️ Upgrade traefik Docker tag to v2.10.0 +* fix: 🐛 update rbac for both traefik.io and containo.us apigroups (#836) +* breaking: 💥 update CRDs needed for Traefik v2.10 + + +## 22.2.0 ![AppVersion: v2.9.10](https://img.shields.io/static/v1?label=AppVersion&message=v2.9.10&color=success&logo=) ![Kubernetes: >=1.16.0-0](https://img.shields.io/static/v1?label=Kubernetes&message=%3E%3D1.16.0-0&color=informational&logo=kubernetes) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2023-04-24 + +* test: 👷 Update unit tests tooling +* fix: 🐛 annotations leaking between aliased subcharts +* fix: indentation on `TLSOption` +* feat: override container port +* feat: allow to set dnsConfig on pod template +* chore: 🔧 new release +* added targetPort support + +### Default value changes + +```diff +diff --git a/traefik/values.yaml b/traefik/values.yaml +index 9ece303..71273cc 100644 +--- a/traefik/values.yaml ++++ b/traefik/values.yaml +@@ -82,6 +82,16 @@ deployment: + shareProcessNamespace: false + # Custom pod DNS policy. Apply if `hostNetwork: true` + # dnsPolicy: ClusterFirstWithHostNet ++ dnsConfig: {} ++ # nameservers: ++ # - 192.0.2.1 # this is an example ++ # searches: ++ # - ns1.svc.cluster-domain.example ++ # - my.dns.search.suffix ++ # options: ++ # - name: ndots ++ # value: "2" ++ # - name: edns0 + # Additional imagePullSecrets + imagePullSecrets: [] + # - name: myRegistryKeySecretName +@@ -561,8 +571,11 @@ ports: + # asDefault: true + port: 8000 + # hostPort: 8000 ++ # containerPort: 8000 + expose: true + exposedPort: 80 ++ ## Different target traefik port on the cluster, useful for IP type LB ++ # targetPort: 80 + # The port protocol (TCP/UDP) + protocol: TCP + # Use nodeport if set. This is useful if you have configured Traefik in a +@@ -587,8 +600,11 @@ ports: + # asDefault: true + port: 8443 + # hostPort: 8443 ++ # containerPort: 8443 + expose: true + exposedPort: 443 ++ ## Different target traefik port on the cluster, useful for IP type LB ++ # targetPort: 80 + ## The port protocol (TCP/UDP) + protocol: TCP + # nodePort: 32443 +``` + +## 22.1.0 ![AppVersion: v2.9.10](https://img.shields.io/static/v1?label=AppVersion&message=v2.9.10&color=success&logo=) ![Kubernetes: >=1.16.0-0](https://img.shields.io/static/v1?label=Kubernetes&message=%3E%3D1.16.0-0&color=informational&logo=kubernetes) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2023-04-07 + +* ⬆️ Upgrade traefik Docker tag to v2.9.10 +* feat: add additional labels to tlsoption + +### Default value changes + +```diff +diff --git a/traefik/values.yaml b/traefik/values.yaml +index 4762b77..9ece303 100644 +--- a/traefik/values.yaml ++++ b/traefik/values.yaml +@@ -654,12 +654,15 @@ ports: + + # TLS Options are created as TLSOption CRDs + # https://doc.traefik.io/traefik/https/tls/#tls-options ++# When using `labelSelector`, you'll need to set labels on tlsOption accordingly. + # Example: + # tlsOptions: + # default: ++# labels: {} + # sniStrict: true + # preferServerCipherSuites: true +-# foobar: ++# customOptions: ++# labels: {} + # curvePreferences: + # - CurveP521 + # - CurveP384 +``` + +## 22.0.0 ![AppVersion: v2.9.9](https://img.shields.io/static/v1?label=AppVersion&message=v2.9.9&color=success&logo=) ![Kubernetes: >=1.16.0-0](https://img.shields.io/static/v1?label=Kubernetes&message=%3E%3D1.16.0-0&color=informational&logo=kubernetes) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2023-03-29 + +* BREAKING CHANGE: `image.repository` introduction may break during the upgrade. See PR #802. + + +## 21.2.1 ![AppVersion: v2.9.9](https://img.shields.io/static/v1?label=AppVersion&message=v2.9.9&color=success&logo=) ![Kubernetes: >=1.16.0-0](https://img.shields.io/static/v1?label=Kubernetes&message=%3E%3D1.16.0-0&color=informational&logo=kubernetes) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2023-03-28 + +* 🎨 Introduce `image.registry` and add explicit default (it may impact custom `image.repository`) +* ⬆️ Upgrade traefik Docker tag to v2.9.9 +* :memo: Clarify the need of an initContainer when enabling persistence for TLS Certificates + +### Default value changes + +```diff +diff --git a/traefik/values.yaml b/traefik/values.yaml +index cadc7a6..4762b77 100644 +--- a/traefik/values.yaml ++++ b/traefik/values.yaml +@@ -1,5 +1,6 @@ + # Default values for Traefik + image: ++ registry: docker.io + repository: traefik + # defaults to appVersion + tag: "" +@@ -66,10 +67,14 @@ deployment: + # Additional initContainers (e.g. for setting file permission as shown below) + initContainers: [] + # The "volume-permissions" init container is required if you run into permission issues. +- # Related issue: https://github.com/traefik/traefik/issues/6825 ++ # Related issue: https://github.com/traefik/traefik-helm-chart/issues/396 + # - name: volume-permissions +- # image: busybox:1.35 +- # command: ["sh", "-c", "touch /data/acme.json && chmod -Rv 600 /data/* && chown 65532:65532 /data/acme.json"] ++ # image: busybox:latest ++ # command: ["sh", "-c", "touch /data/acme.json; chmod -v 600 /data/acme.json"] ++ # securityContext: ++ # runAsNonRoot: true ++ # runAsGroup: 65532 ++ # runAsUser: 65532 + # volumeMounts: + # - name: data + # mountPath: /data +@@ -849,13 +854,17 @@ securityContext: + capabilities: + drop: [ALL] + readOnlyRootFilesystem: true ++ ++podSecurityContext: ++# # /!\ When setting fsGroup, Kubernetes will recursively changes ownership and ++# # permissions for the contents of each volume to match the fsGroup. This can ++# # be an issue when storing sensitive content like TLS Certificates /!\ ++# fsGroup: 65532 ++ fsGroupChangePolicy: "OnRootMismatch" + runAsGroup: 65532 + runAsNonRoot: true + runAsUser: 65532 + +-podSecurityContext: +- fsGroup: 65532 +- + # + # Extra objects to deploy (value evaluated as a template) + # +``` + +## 21.2.0 ![AppVersion: v2.9.8](https://img.shields.io/static/v1?label=AppVersion&message=v2.9.8&color=success&logo=) ![Kubernetes: >=1.16.0-0](https://img.shields.io/static/v1?label=Kubernetes&message=%3E%3D1.16.0-0&color=informational&logo=kubernetes) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2023-03-08 + +* 🚨 Fail when enabling PSP on Kubernetes v1.25+ (#801) +* ⬆️ Upgrade traefik Docker tag to v2.9.8 +* Separate UDP hostPort for HTTP/3 +* :sparkles: release 21.2.0 (#805) + + +## 21.1.0 ![AppVersion: v2.9.7](https://img.shields.io/static/v1?label=AppVersion&message=v2.9.7&color=success&logo=) ![Kubernetes: >=1.16.0-0](https://img.shields.io/static/v1?label=Kubernetes&message=%3E%3D1.16.0-0&color=informational&logo=kubernetes) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2023-02-15 + +* ⬆️ Upgrade traefik Docker tag to v2.9.7 +* ✨ release 21.1.0 +* fix: traefik image name for renovate +* feat: Add volumeName to PersistentVolumeClaim (#792) +* Allow setting TLS options on dashboard IngressRoute + +### Default value changes + +```diff +diff --git a/traefik/values.yaml b/traefik/values.yaml +index 780b04b..cadc7a6 100644 +--- a/traefik/values.yaml ++++ b/traefik/values.yaml +@@ -142,6 +142,8 @@ ingressRoute: + entryPoints: ["traefik"] + # Additional ingressRoute middlewares (e.g. for authentication) + middlewares: [] ++ # TLS options (e.g. secret containing certificate) ++ tls: {} + + # Customize updateStrategy of traefik pods + updateStrategy: +@@ -750,6 +752,7 @@ persistence: + accessMode: ReadWriteOnce + size: 128Mi + # storageClass: "" ++ # volumeName: "" + path: /data + annotations: {} + # subPath: "" # only mount a subpath of the Volume into the pod +``` + +## 21.0.0 ![AppVersion: v2.9.6](https://img.shields.io/static/v1?label=AppVersion&message=v2.9.6&color=success&logo=) ![Kubernetes: >=1.16.0-0](https://img.shields.io/static/v1?label=Kubernetes&message=%3E%3D1.16.0-0&color=informational&logo=kubernetes) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2023-02-10 + +* 🙈 Add a setting disable API check on Prometheus Operator (#769) +* 📝 Improve documentation on entrypoint options +* 💥 New release with BREAKING changes (#786) +* ✨ Chart.yaml - add kubeVersion: ">=1.16.0-0" +* fix: allowExternalNameServices for kubernetes ingress when hub enabled (#772) +* fix(service-metrics): invert prometheus svc & fullname length checking +* Configure Renovate (#783) +* :necktie: Improve labels settings behavior on metrics providers (#774) +* :bug: Disabling dashboard ingressroute should delete it (#785) +* :boom: Rename image.name => image.repository (#784) + +### Default value changes + +```diff +diff --git a/traefik/values.yaml b/traefik/values.yaml +index 42a27f9..780b04b 100644 +--- a/traefik/values.yaml ++++ b/traefik/values.yaml +@@ -1,6 +1,6 @@ + # Default values for Traefik + image: +- name: traefik ++ repository: traefik + # defaults to appVersion + tag: "" + pullPolicy: IfNotPresent +@@ -396,6 +396,8 @@ metrics: + # enabled: false + # labels: {} + # annotations: {} ++ ## When set to true, it won't check if Prometheus Operator CRDs are deployed ++ # disableAPICheck: false + # serviceMonitor: + # metricRelabelings: [] + # - sourceLabels: [__name__] +@@ -580,7 +582,7 @@ ports: + # hostPort: 8443 + expose: true + exposedPort: 443 +- # The port protocol (TCP/UDP) ++ ## The port protocol (TCP/UDP) + protocol: TCP + # nodePort: 32443 + # +@@ -594,6 +596,16 @@ ports: + enabled: false + # advertisedPort: 4443 + # ++ ## Trust forwarded headers information (X-Forwarded-*). ++ #forwardedHeaders: ++ # trustedIPs: [] ++ # insecure: false ++ # ++ ## Enable the Proxy Protocol header parsing for the entry point ++ #proxyProtocol: ++ # trustedIPs: [] ++ # insecure: false ++ # + ## Set TLS at the entrypoint + ## https://doc.traefik.io/traefik/routing/entrypoints/#tls + tls: +@@ -607,16 +619,6 @@ ports: + # - foo.example.com + # - bar.example.com + # +- # Trust forwarded headers information (X-Forwarded-*). +- # forwardedHeaders: +- # trustedIPs: [] +- # insecure: false +- # +- # Enable the Proxy Protocol header parsing for the entry point +- # proxyProtocol: +- # trustedIPs: [] +- # insecure: false +- # + # One can apply Middlewares on an entrypoint + # https://doc.traefik.io/traefik/middlewares/overview/ + # https://doc.traefik.io/traefik/routing/entrypoints/#middlewares +``` + +## 20.8.0 ![AppVersion: v2.9.6](https://img.shields.io/static/v1?label=AppVersion&message=v2.9.6&color=success&logo=) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2022-12-09 + +* ✨ update chart to version 20.8.0 +* ✨ add support for default entrypoints +* ✨ add support for OpenTelemetry and Traefik v3 + +### Default value changes + +```diff +diff --git a/traefik/values.yaml b/traefik/values.yaml +index b77539d..42a27f9 100644 +--- a/traefik/values.yaml ++++ b/traefik/values.yaml +@@ -107,6 +107,8 @@ ingressClass: + + # Enable experimental features + experimental: ++ v3: ++ enabled: false + plugins: + enabled: false + kubernetesGateway: +@@ -347,7 +349,43 @@ metrics: + # # addRoutersLabels: true + # ## Enable metrics on services. Default=true + # # addServicesLabels: false +- ++# openTelemetry: ++# ## Address of the OpenTelemetry Collector to send metrics to. ++# address: "localhost:4318" ++# ## Enable metrics on entry points. ++# addEntryPointsLabels: true ++# ## Enable metrics on routers. ++# addRoutersLabels: true ++# ## Enable metrics on services. ++# addServicesLabels: true ++# ## Explicit boundaries for Histogram data points. ++# explicitBoundaries: ++# - "0.1" ++# - "0.3" ++# - "1.2" ++# - "5.0" ++# ## Additional headers sent with metrics by the reporter to the OpenTelemetry Collector. ++# headers: ++# foo: bar ++# test: test ++# ## Allows reporter to send metrics to the OpenTelemetry Collector without using a secured protocol. ++# insecure: true ++# ## Interval at which metrics are sent to the OpenTelemetry Collector. ++# pushInterval: 10s ++# ## Allows to override the default URL path used for sending metrics. This option has no effect when using gRPC transport. ++# path: /foo/v1/traces ++# ## Defines the TLS configuration used by the reporter to send metrics to the OpenTelemetry Collector. ++# tls: ++# ## The path to the certificate authority, it defaults to the system bundle. ++# ca: path/to/ca.crt ++# ## The path to the public certificate. When using this option, setting the key option is required. ++# cert: path/to/foo.cert ++# ## The path to the private key. When using this option, setting the cert option is required. ++# key: path/to/key.key ++# ## If set to true, the TLS connection accepts any certificate presented by the server regardless of the hostnames it covers. ++# insecureSkipVerify: true ++# ## This instructs the reporter to send metrics to the OpenTelemetry Collector using gRPC. ++# grpc: true + + ## + ## enable optional CRDs for Prometheus Operator +@@ -510,6 +548,8 @@ ports: + # The port protocol (TCP/UDP) + protocol: TCP + web: ++ ## Enable this entrypoint as a default entrypoint. When a service doesn't explicity set an entrypoint it will only use this entrypoint. ++ # asDefault: true + port: 8000 + # hostPort: 8000 + expose: true +@@ -534,6 +574,8 @@ ports: + # trustedIPs: [] + # insecure: false + websecure: ++ ## Enable this entrypoint as a default entrypoint. When a service doesn't explicity set an entrypoint it will only use this entrypoint. ++ # asDefault: true + port: 8443 + # hostPort: 8443 + expose: true +``` + +## 20.7.0 ![AppVersion: v2.9.6](https://img.shields.io/static/v1?label=AppVersion&message=v2.9.6&color=success&logo=) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2022-12-08 + +* 🐛 Don't fail when prometheus is disabled (#756) +* ⬆️ Update default Traefik release to v2.9.6 (#758) +* ✨ support for Gateway annotations +* add keywords [networking], for artifacthub category quering +* :bug: Fix typo on bufferingSize for access logs (#753) +* :adhesive_bandage: Add quotes for artifacthub changelog parsing (#748) + +### Default value changes + +```diff +diff --git a/traefik/values.yaml b/traefik/values.yaml +index 4f2fb2a..b77539d 100644 +--- a/traefik/values.yaml ++++ b/traefik/values.yaml +@@ -120,6 +120,9 @@ experimental: + # By default, Gateway would be created to the Namespace you are deploying Traefik to. + # You may create that Gateway in another namespace, setting its name below: + # namespace: default ++ # Additional gateway annotations (e.g. for cert-manager.io/issuer) ++ # annotations: ++ # cert-manager.io/issuer: letsencrypt + + # Create an IngressRoute for the dashboard + ingressRoute: +@@ -219,7 +222,8 @@ logs: + # By default, the logs use a text format (common), but you can + # also ask for the json format in the format option + # format: json +- # By default, the level is set to ERROR. Alternative logging levels are DEBUG, PANIC, FATAL, ERROR, WARN, and INFO. ++ # By default, the level is set to ERROR. ++ # Alternative logging levels are DEBUG, PANIC, FATAL, ERROR, WARN, and INFO. + level: ERROR + access: + # To enable access logs +``` + +## 20.6.0 ![AppVersion: v2.9.5](https://img.shields.io/static/v1?label=AppVersion&message=v2.9.5&color=success&logo=) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2022-11-30 + +* 🔍️ Add filePath support on access logs (#747) +* :memo: Improve documentation on using PVC with TLS certificates +* :bug: Add missing scheme in help on Traefik Hub integration (#746) + +### Default value changes + +```diff +diff --git a/traefik/values.yaml b/traefik/values.yaml +index 15f1682..4f2fb2a 100644 +--- a/traefik/values.yaml ++++ b/traefik/values.yaml +@@ -211,10 +211,10 @@ additionalVolumeMounts: [] + # - name: traefik-logs + # mountPath: /var/log/traefik + +-# Logs +-# https://docs.traefik.io/observability/logs/ ++## Logs ++## https://docs.traefik.io/observability/logs/ + logs: +- # Traefik logs concern everything that happens to Traefik itself (startup, configuration, events, shutdown, and so on). ++ ## Traefik logs concern everything that happens to Traefik itself (startup, configuration, events, shutdown, and so on). + general: + # By default, the logs use a text format (common), but you can + # also ask for the json format in the format option +@@ -224,31 +224,32 @@ logs: + access: + # To enable access logs + enabled: false +- # By default, logs are written using the Common Log Format (CLF). +- # To write logs in JSON, use json in the format option. +- # If the given format is unsupported, the default (CLF) is used instead. ++ ## By default, logs are written using the Common Log Format (CLF) on stdout. ++ ## To write logs in JSON, use json in the format option. ++ ## If the given format is unsupported, the default (CLF) is used instead. + # format: json +- # To write the logs in an asynchronous fashion, specify a bufferingSize option. +- # This option represents the number of log lines Traefik will keep in memory before writing +- # them to the selected output. In some cases, this option can greatly help performances. ++ # filePath: "/var/log/traefik/access.log ++ ## To write the logs in an asynchronous fashion, specify a bufferingSize option. ++ ## This option represents the number of log lines Traefik will keep in memory before writing ++ ## them to the selected output. In some cases, this option can greatly help performances. + # bufferingSize: 100 +- # Filtering https://docs.traefik.io/observability/access-logs/#filtering ++ ## Filtering https://docs.traefik.io/observability/access-logs/#filtering + filters: {} + # statuscodes: "200,300-302" + # retryattempts: true + # minduration: 10ms +- # Fields +- # https://docs.traefik.io/observability/access-logs/#limiting-the-fieldsincluding-headers ++ ## Fields ++ ## https://docs.traefik.io/observability/access-logs/#limiting-the-fieldsincluding-headers + fields: + general: + defaultmode: keep + names: {} +- # Examples: ++ ## Examples: + # ClientUsername: drop + headers: + defaultmode: drop + names: {} +- # Examples: ++ ## Examples: + # User-Agent: redact + # Authorization: drop + # Content-Type: keep +@@ -693,10 +694,7 @@ autoscaling: + + # Enable persistence using Persistent Volume Claims + # ref: http://kubernetes.io/docs/user-guide/persistent-volumes/ +-# After the pvc has been mounted, add the configs into traefik by using the `additionalArguments` list below, eg: +-# additionalArguments: +-# - "--certificatesresolvers.le.acme.storage=/data/acme.json" +-# It will persist TLS certificates. ++# It can be used to store TLS certificates, see `storage` in certResolvers + persistence: + enabled: false + name: data +@@ -726,7 +724,7 @@ certResolvers: {} + # tlsChallenge: true + # httpChallenge: + # entryPoint: "web" +-# # match the path to persistence ++# # It has to match the path with a persistent volume + # storage: /data/acme.json + + # If hostNetwork is true, runs traefik in the host network namespace +``` + +## 20.5.3 ![AppVersion: v2.9.5](https://img.shields.io/static/v1?label=AppVersion&message=v2.9.5&color=success&logo=) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2022-11-25 + +* 🐛 Fix template issue with obsolete helm version + add helm version requirement (#743) + + +## 20.5.2 ![AppVersion: v2.9.5](https://img.shields.io/static/v1?label=AppVersion&message=v2.9.5&color=success&logo=) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2022-11-24 + +* ⬆️Update Traefik to v2.9.5 (#740) + + +## 20.5.1 ![AppVersion: v2.9.4](https://img.shields.io/static/v1?label=AppVersion&message=v2.9.4&color=success&logo=) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2022-11-23 + +* 🐛 Fix namespaceSelector on ServiceMonitor (#737) + + +## 20.5.0 ![AppVersion: v2.9.4](https://img.shields.io/static/v1?label=AppVersion&message=v2.9.4&color=success&logo=) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2022-11-23 + +* 🚀 Add complete support on metrics options (#735) +* 🐛 make tests use fixed version + +### Default value changes + +```diff +diff --git a/traefik/values.yaml b/traefik/values.yaml +index e49d02d..15f1682 100644 +--- a/traefik/values.yaml ++++ b/traefik/values.yaml +@@ -12,7 +12,7 @@ hub: + ## Enabling Hub will: + # * enable Traefik Hub integration on Traefik + # * add `traefikhub-tunl` endpoint +- # * enable addRoutersLabels on prometheus metrics ++ # * enable Prometheus metrics with addRoutersLabels + # * enable allowExternalNameServices on KubernetesIngress provider + # * enable allowCrossNamespace on KubernetesCRD provider + # * add an internal (ClusterIP) Service, dedicated for Traefik Hub +@@ -254,16 +254,96 @@ logs: + # Content-Type: keep + + metrics: +- # datadog: +- # address: 127.0.0.1:8125 +- # influxdb: +- # address: localhost:8089 +- # protocol: udp ++ ## Prometheus is enabled by default. ++ ## It can be disabled by setting "prometheus: null" + prometheus: ++ ## Entry point used to expose metrics. + entryPoint: metrics +- # addRoutersLabels: true +- # statsd: +- # address: localhost:8125 ++ ## Enable metrics on entry points. Default=true ++ # addEntryPointsLabels: false ++ ## Enable metrics on routers. Default=false ++ # addRoutersLabels: true ++ ## Enable metrics on services. Default=true ++ # addServicesLabels: false ++ ## Buckets for latency metrics. Default="0.1,0.3,1.2,5.0" ++ # buckets: "0.5,1.0,2.5" ++ ## When manualRouting is true, it disables the default internal router in ++ ## order to allow creating a custom router for prometheus@internal service. ++ # manualRouting: true ++# datadog: ++# ## Address instructs exporter to send metrics to datadog-agent at this address. ++# address: "127.0.0.1:8125" ++# ## The interval used by the exporter to push metrics to datadog-agent. Default=10s ++# # pushInterval: 30s ++# ## The prefix to use for metrics collection. Default="traefik" ++# # prefix: traefik ++# ## Enable metrics on entry points. Default=true ++# # addEntryPointsLabels: false ++# ## Enable metrics on routers. Default=false ++# # addRoutersLabels: true ++# ## Enable metrics on services. Default=true ++# # addServicesLabels: false ++# influxdb: ++# ## Address instructs exporter to send metrics to influxdb at this address. ++# address: localhost:8089 ++# ## InfluxDB's address protocol (udp or http). Default="udp" ++# protocol: udp ++# ## InfluxDB database used when protocol is http. Default="" ++# # database: "" ++# ## InfluxDB retention policy used when protocol is http. Default="" ++# # retentionPolicy: "" ++# ## InfluxDB username (only with http). Default="" ++# # username: "" ++# ## InfluxDB password (only with http). Default="" ++# # password: "" ++# ## The interval used by the exporter to push metrics to influxdb. Default=10s ++# # pushInterval: 30s ++# ## Additional labels (influxdb tags) on all metrics. ++# # additionalLabels: ++# # env: production ++# # foo: bar ++# ## Enable metrics on entry points. Default=true ++# # addEntryPointsLabels: false ++# ## Enable metrics on routers. Default=false ++# # addRoutersLabels: true ++# ## Enable metrics on services. Default=true ++# # addServicesLabels: false ++# influxdb2: ++# ## Address instructs exporter to send metrics to influxdb v2 at this address. ++# address: localhost:8086 ++# ## Token with which to connect to InfluxDB v2. ++# token: xxx ++# ## Organisation where metrics will be stored. ++# org: "" ++# ## Bucket where metrics will be stored. ++# bucket: "" ++# ## The interval used by the exporter to push metrics to influxdb. Default=10s ++# # pushInterval: 30s ++# ## Additional labels (influxdb tags) on all metrics. ++# # additionalLabels: ++# # env: production ++# # foo: bar ++# ## Enable metrics on entry points. Default=true ++# # addEntryPointsLabels: false ++# ## Enable metrics on routers. Default=false ++# # addRoutersLabels: true ++# ## Enable metrics on services. Default=true ++# # addServicesLabels: false ++# statsd: ++# ## Address instructs exporter to send metrics to statsd at this address. ++# address: localhost:8125 ++# ## The interval used by the exporter to push metrics to influxdb. Default=10s ++# # pushInterval: 30s ++# ## The prefix to use for metrics collection. Default="traefik" ++# # prefix: traefik ++# ## Enable metrics on entry points. Default=true ++# # addEntryPointsLabels: false ++# ## Enable metrics on routers. Default=false ++# # addRoutersLabels: true ++# ## Enable metrics on services. Default=true ++# # addServicesLabels: false ++ ++ + ## + ## enable optional CRDs for Prometheus Operator + ## +``` + +## 20.4.1 ![AppVersion: v2.9.4](https://img.shields.io/static/v1?label=AppVersion&message=v2.9.4&color=success&logo=) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2022-11-21 + +* 🐛 fix namespace references to support namespaceOverride + + +## 20.4.0 ![AppVersion: v2.9.4](https://img.shields.io/static/v1?label=AppVersion&message=v2.9.4&color=success&logo=) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2022-11-21 + +* Add (optional) dedicated metrics service (#727) + +### Default value changes + +```diff +diff --git a/traefik/values.yaml b/traefik/values.yaml +index ca15f6a..e49d02d 100644 +--- a/traefik/values.yaml ++++ b/traefik/values.yaml +@@ -267,6 +267,12 @@ metrics: + ## + ## enable optional CRDs for Prometheus Operator + ## ++ ## Create a dedicated metrics service for use with ServiceMonitor ++ ## When hub.enabled is set to true, it's not needed: it will use hub service. ++ # service: ++ # enabled: false ++ # labels: {} ++ # annotations: {} + # serviceMonitor: + # metricRelabelings: [] + # - sourceLabels: [__name__] +``` + +## 20.3.1 ![AppVersion: v2.9.4](https://img.shields.io/static/v1?label=AppVersion&message=v2.9.4&color=success&logo=) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2022-11-21 + +* 🐛 Fix namespace override which was missing on `ServiceAccount` (#731) + + +## 20.3.0 ![AppVersion: v2.9.4](https://img.shields.io/static/v1?label=AppVersion&message=v2.9.4&color=success&logo=) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2022-11-17 + +* Add overwrite option for instance label value (#725) + +### Default value changes + +```diff +diff --git a/traefik/values.yaml b/traefik/values.yaml +index c7f84a7..ca15f6a 100644 +--- a/traefik/values.yaml ++++ b/traefik/values.yaml +@@ -731,3 +731,6 @@ extraObjects: [] + # This will override the default Release Namespace for Helm. + # It will not affect optional CRDs such as `ServiceMonitor` and `PrometheusRules` + # namespaceOverride: traefik ++# ++## This will override the default app.kubernetes.io/instance label for all Objects. ++# instanceLabelOverride: traefik +``` + +## 20.2.1 ![AppVersion: v2.9.4](https://img.shields.io/static/v1?label=AppVersion&message=v2.9.4&color=success&logo=) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2022-11-17 + +* 🙈 do not namespace ingress class (#723) +* ✨ copy LICENSE and README.md on release + + +## 20.2.0 ![AppVersion: v2.9.4](https://img.shields.io/static/v1?label=AppVersion&message=v2.9.4&color=success&logo=) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2022-11-15 + +* ✨ add support for namespace overrides (#718) +* Document recent changes in the README (#717) + +### Default value changes + +```diff +diff --git a/traefik/values.yaml b/traefik/values.yaml +index 97a1b71..c7f84a7 100644 +--- a/traefik/values.yaml ++++ b/traefik/values.yaml +@@ -725,5 +725,9 @@ podSecurityContext: + # Extra objects to deploy (value evaluated as a template) + # + # In some cases, it can avoid the need for additional, extended or adhoc deployments. +-# See #595 for more details and traefik/tests/extra.yaml for example. ++# See #595 for more details and traefik/tests/values/extra.yaml for example. + extraObjects: [] ++ ++# This will override the default Release Namespace for Helm. ++# It will not affect optional CRDs such as `ServiceMonitor` and `PrometheusRules` ++# namespaceOverride: traefik +``` + +## 20.1.1 ![AppVersion: v2.9.4](https://img.shields.io/static/v1?label=AppVersion&message=v2.9.4&color=success&logo=) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2022-11-10 + +* fix: use consistent appVersion with Traefik Proxy + + +## 20.1.0 ![AppVersion: 2.9.4](https://img.shields.io/static/v1?label=AppVersion&message=2.9.4&color=success&logo=) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2022-11-09 + +* 🔧 Adds more settings for dashboard ingressRoute (#710) +* 🐛 fix chart releases + +### Default value changes + +```diff +diff --git a/traefik/values.yaml b/traefik/values.yaml +index 2ec3736..97a1b71 100644 +--- a/traefik/values.yaml ++++ b/traefik/values.yaml +@@ -129,10 +129,14 @@ ingressRoute: + annotations: {} + # Additional ingressRoute labels (e.g. for filtering IngressRoute by custom labels) + labels: {} ++ # The router match rule used for the dashboard ingressRoute ++ matchRule: PathPrefix(`/dashboard`) || PathPrefix(`/api`) + # Specify the allowed entrypoints to use for the dashboard ingress route, (e.g. traefik, web, websecure). + # By default, it's using traefik entrypoint, which is not exposed. + # /!\ Do not expose your dashboard without any protection over the internet /!\ + entryPoints: ["traefik"] ++ # Additional ingressRoute middlewares (e.g. for authentication) ++ middlewares: [] + + # Customize updateStrategy of traefik pods + updateStrategy: +``` + +## 20.0.0 ![AppVersion: 2.9.4](https://img.shields.io/static/v1?label=AppVersion&message=2.9.4&color=success&logo=) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2022-11-08 + +* 🐛 remove old deployment workflow +* ✨ migrate to centralised helm repository +* Allow updateStrategy to be configurable + +### Default value changes + +```diff +diff --git a/traefik/values.yaml b/traefik/values.yaml +index 413aa88..2ec3736 100644 +--- a/traefik/values.yaml ++++ b/traefik/values.yaml +@@ -134,9 +134,12 @@ ingressRoute: + # /!\ Do not expose your dashboard without any protection over the internet /!\ + entryPoints: ["traefik"] + +-rollingUpdate: +- maxUnavailable: 0 +- maxSurge: 1 ++# Customize updateStrategy of traefik pods ++updateStrategy: ++ type: RollingUpdate ++ rollingUpdate: ++ maxUnavailable: 0 ++ maxSurge: 1 + + # Customize liveness and readiness probe values. + readinessProbe: +``` + +## 19.0.4 ![AppVersion: 2.9.4](https://img.shields.io/static/v1?label=AppVersion&message=2.9.4&color=success&logo=) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2022-11-08 + +* 🔧 Adds more settings & rename (wrong) scrapeInterval to (valid) interval on ServiceMonitor (#703) + +### Default value changes + +```diff +diff --git a/traefik/values.yaml b/traefik/values.yaml +index b24c1cb..413aa88 100644 +--- a/traefik/values.yaml ++++ b/traefik/values.yaml +@@ -261,10 +261,6 @@ metrics: + ## enable optional CRDs for Prometheus Operator + ## + # serviceMonitor: +- # additionalLabels: +- # foo: bar +- # namespace: "another-namespace" +- # namespaceSelector: {} + # metricRelabelings: [] + # - sourceLabels: [__name__] + # separator: ; +@@ -279,9 +275,17 @@ metrics: + # replacement: $1 + # action: replace + # jobLabel: traefik +- # scrapeInterval: 30s +- # scrapeTimeout: 5s ++ # interval: 30s + # honorLabels: true ++ # # (Optional) ++ # # scrapeTimeout: 5s ++ # # honorTimestamps: true ++ # # enableHttp2: true ++ # # followRedirects: true ++ # # additionalLabels: ++ # # foo: bar ++ # # namespace: "another-namespace" ++ # # namespaceSelector: {} + # prometheusRule: + # additionalLabels: {} + # namespace: "another-namespace" +``` + +## 19.0.3 ![AppVersion: 2.9.4](https://img.shields.io/static/v1?label=AppVersion&message=2.9.4&color=success&logo=) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2022-11-03 + +* 🎨 Don't require exposed Ports when enabling Hub (#700) + + +## 19.0.2 ![AppVersion: 2.9.4](https://img.shields.io/static/v1?label=AppVersion&message=2.9.4&color=success&logo=) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2022-11-03 + +* :speech_balloon: Support volume secrets with '.' in name (#695) + + +## 19.0.1 ![AppVersion: 2.9.4](https://img.shields.io/static/v1?label=AppVersion&message=2.9.4&color=success&logo=) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2022-11-03 + +* 🐛 Fix IngressClass install on EKS (#699) + + +## 19.0.0 ![AppVersion: 2.9.4](https://img.shields.io/static/v1?label=AppVersion&message=2.9.4&color=success&logo=) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2022-11-02 + +* ✨ Provides Default IngressClass for Traefik by default (#693) + +### Default value changes + +```diff +diff --git a/traefik/values.yaml b/traefik/values.yaml +index 69190f1..b24c1cb 100644 +--- a/traefik/values.yaml ++++ b/traefik/values.yaml +@@ -100,11 +100,10 @@ podDisruptionBudget: + # minAvailable: 0 + # minAvailable: 25% + +-# Use ingressClass. Ignored if Traefik version < 2.3 / kubernetes < 1.18.x ++# Create a default IngressClass for Traefik + ingressClass: +- # true is not unit-testable yet, pending https://github.com/rancher/helm-unittest/pull/12 +- enabled: false +- isDefaultClass: false ++ enabled: true ++ isDefaultClass: true + + # Enable experimental features + experimental: +``` + +## 18.3.0 ![AppVersion: 2.9.4](https://img.shields.io/static/v1?label=AppVersion&message=2.9.4&color=success&logo=) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2022-10-31 + +* ⬆️ Update Traefik appVersion to 2.9.4 (#696) + + +## 18.2.0 ![AppVersion: 2.9.1](https://img.shields.io/static/v1?label=AppVersion&message=2.9.1&color=success&logo=) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2022-10-31 + +* 🚩 Add an optional "internal" service (#683) + +### Default value changes + +```diff +diff --git a/traefik/values.yaml b/traefik/values.yaml +index 8033a87..69190f1 100644 +--- a/traefik/values.yaml ++++ b/traefik/values.yaml +@@ -416,7 +416,7 @@ ports: + # The port protocol (TCP/UDP) + protocol: TCP + # Use nodeport if set. This is useful if you have configured Traefik in a +- # LoadBalancer ++ # LoadBalancer. + # nodePort: 32080 + # Port Redirections + # Added in 2.2, you can make permanent redirects via entrypoints. +@@ -549,13 +549,24 @@ service: + # - 172.16.0.0/16 + externalIPs: [] + # - 1.2.3.4 +- # One of SingleStack, PreferDualStack, or RequireDualStack. ++ ## One of SingleStack, PreferDualStack, or RequireDualStack. + # ipFamilyPolicy: SingleStack +- # List of IP families (e.g. IPv4 and/or IPv6). +- # ref: https://kubernetes.io/docs/concepts/services-networking/dual-stack/#services ++ ## List of IP families (e.g. IPv4 and/or IPv6). ++ ## ref: https://kubernetes.io/docs/concepts/services-networking/dual-stack/#services + # ipFamilies: + # - IPv4 + # - IPv6 ++ ## ++ ## An additionnal and optional internal Service. ++ ## Same parameters as external Service ++ # internal: ++ # type: ClusterIP ++ # # labels: {} ++ # # annotations: {} ++ # # spec: {} ++ # # loadBalancerSourceRanges: [] ++ # # externalIPs: [] ++ # # ipFamilies: [ "IPv4","IPv6" ] + + ## Create HorizontalPodAutoscaler object. + ## +``` + +## 18.1.0 ![AppVersion: 2.9.1](https://img.shields.io/static/v1?label=AppVersion&message=2.9.1&color=success&logo=) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2022-10-27 + +* 🚀 Add native support for Traefik Hub (#676) + +### Default value changes + +```diff +diff --git a/traefik/values.yaml b/traefik/values.yaml +index acce704..8033a87 100644 +--- a/traefik/values.yaml ++++ b/traefik/values.yaml +@@ -5,6 +5,27 @@ image: + tag: "" + pullPolicy: IfNotPresent + ++# ++# Configure integration with Traefik Hub ++# ++hub: ++ ## Enabling Hub will: ++ # * enable Traefik Hub integration on Traefik ++ # * add `traefikhub-tunl` endpoint ++ # * enable addRoutersLabels on prometheus metrics ++ # * enable allowExternalNameServices on KubernetesIngress provider ++ # * enable allowCrossNamespace on KubernetesCRD provider ++ # * add an internal (ClusterIP) Service, dedicated for Traefik Hub ++ enabled: false ++ ## Default port can be changed ++ # tunnelPort: 9901 ++ ## TLS is optional. Insecure is mutually exclusive with any other options ++ # tls: ++ # insecure: false ++ # ca: "/path/to/ca.pem" ++ # cert: "/path/to/cert.pem" ++ # key: "/path/to/key.pem" ++ + # + # Configure the deployment + # +@@ -505,6 +526,8 @@ tlsStore: {} + # from. + service: + enabled: true ++ ## Single service is using `MixedProtocolLBService` feature gate. ++ ## When set to false, it will create two Service, one for TCP and one for UDP. + single: true + type: LoadBalancer + # Additional annotations applied to both TCP and UDP services (e.g. for cloud provider specific config) +``` + +## 18.0.0 ![AppVersion: 2.9.1](https://img.shields.io/static/v1?label=AppVersion&message=2.9.1&color=success&logo=) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2022-10-26 + +* Refactor http3 and merge TCP with UDP ports into a single service (#656) + +### Default value changes + +```diff +diff --git a/traefik/values.yaml b/traefik/values.yaml +index 807bd09..acce704 100644 +--- a/traefik/values.yaml ++++ b/traefik/values.yaml +@@ -87,8 +87,6 @@ ingressClass: + + # Enable experimental features + experimental: +- http3: +- enabled: false + plugins: + enabled: false + kubernetesGateway: +@@ -421,12 +419,19 @@ ports: + # The port protocol (TCP/UDP) + protocol: TCP + # nodePort: 32443 +- # Enable HTTP/3. +- # Requires enabling experimental http3 feature and tls. +- # Note that you cannot have a UDP entrypoint with the same port. +- # http3: true +- # Set TLS at the entrypoint +- # https://doc.traefik.io/traefik/routing/entrypoints/#tls ++ # ++ ## Enable HTTP/3 on the entrypoint ++ ## Enabling it will also enable http3 experimental feature ++ ## https://doc.traefik.io/traefik/routing/entrypoints/#http3 ++ ## There are known limitations when trying to listen on same ports for ++ ## TCP & UDP (Http3). There is a workaround in this chart using dual Service. ++ ## https://github.com/kubernetes/kubernetes/issues/47249#issuecomment-587960741 ++ http3: ++ enabled: false ++ # advertisedPort: 4443 ++ # ++ ## Set TLS at the entrypoint ++ ## https://doc.traefik.io/traefik/routing/entrypoints/#tls + tls: + enabled: true + # this is the name of a TLSOption definition +@@ -500,6 +505,7 @@ tlsStore: {} + # from. + service: + enabled: true ++ single: true + type: LoadBalancer + # Additional annotations applied to both TCP and UDP services (e.g. for cloud provider specific config) + annotations: {} +``` + +## 17.0.5 ![AppVersion: 2.9.1](https://img.shields.io/static/v1?label=AppVersion&message=2.9.1&color=success&logo=) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2022-10-21 + +* 📝 Add annotations changelog for artifacthub.io & update Maintainers + + +## 17.0.4 ![AppVersion: 2.9.1](https://img.shields.io/static/v1?label=AppVersion&message=2.9.1&color=success&logo=) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2022-10-21 + +* :art: Add helper function for label selector + + +## 17.0.3 ![AppVersion: 2.9.1](https://img.shields.io/static/v1?label=AppVersion&message=2.9.1&color=success&logo=) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2022-10-20 + +* 🐛 fix changing label selectors + + +## 17.0.2 ![AppVersion: 2.9.1](https://img.shields.io/static/v1?label=AppVersion&message=2.9.1&color=success&logo=) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2022-10-20 + +* fix: setting ports.web.proxyProtocol.insecure=true + + +## 17.0.1 ![AppVersion: 2.9.1](https://img.shields.io/static/v1?label=AppVersion&message=2.9.1&color=success&logo=) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2022-10-20 + +* :bug: Unify all labels selector with traefik chart labels (#681) + +### Default value changes + +```diff +diff --git a/traefik/values.yaml b/traefik/values.yaml +index 6a90bc6..807bd09 100644 +--- a/traefik/values.yaml ++++ b/traefik/values.yaml +@@ -639,7 +639,7 @@ affinity: {} + # - labelSelector: + # matchLabels: + # app.kubernetes.io/name: '{{ template "traefik.name" . }}' +-# app.kubernetes.io/instance: '{{ .Release.Name }}' ++# app.kubernetes.io/instance: '{{ .Release.Name }}-{{ .Release.Namespace }}' + # topologyKey: kubernetes.io/hostname + + nodeSelector: {} +``` + +## 17.0.0 ![AppVersion: 2.9.1](https://img.shields.io/static/v1?label=AppVersion&message=2.9.1&color=success&logo=) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2022-10-20 + +* :bug: Fix `ClusterRole`, `ClusterRoleBinding` names and `app.kubernetes.io/instance` label (#662) + + +## 16.2.0 ![AppVersion: 2.9.1](https://img.shields.io/static/v1?label=AppVersion&message=2.9.1&color=success&logo=) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2022-10-20 + +* Add forwardedHeaders and proxyProtocol config (#673) + +### Default value changes + +```diff +diff --git a/traefik/values.yaml b/traefik/values.yaml +index 9b5afc4..6a90bc6 100644 +--- a/traefik/values.yaml ++++ b/traefik/values.yaml +@@ -403,6 +403,16 @@ ports: + # Added in 2.2, you can make permanent redirects via entrypoints. + # https://docs.traefik.io/routing/entrypoints/#redirection + # redirectTo: websecure ++ # ++ # Trust forwarded headers information (X-Forwarded-*). ++ # forwardedHeaders: ++ # trustedIPs: [] ++ # insecure: false ++ # ++ # Enable the Proxy Protocol header parsing for the entry point ++ # proxyProtocol: ++ # trustedIPs: [] ++ # insecure: false + websecure: + port: 8443 + # hostPort: 8443 +@@ -428,6 +438,16 @@ ports: + # - foo.example.com + # - bar.example.com + # ++ # Trust forwarded headers information (X-Forwarded-*). ++ # forwardedHeaders: ++ # trustedIPs: [] ++ # insecure: false ++ # ++ # Enable the Proxy Protocol header parsing for the entry point ++ # proxyProtocol: ++ # trustedIPs: [] ++ # insecure: false ++ # + # One can apply Middlewares on an entrypoint + # https://doc.traefik.io/traefik/middlewares/overview/ + # https://doc.traefik.io/traefik/routing/entrypoints/#middlewares +``` + +## 16.1.0 ![AppVersion: 2.9.1](https://img.shields.io/static/v1?label=AppVersion&message=2.9.1&color=success&logo=) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2022-10-19 + +* ✨ add optional ServiceMonitor & PrometheusRules CRDs (#425) + +### Default value changes + +```diff +diff --git a/traefik/values.yaml b/traefik/values.yaml +index 7e335b5..9b5afc4 100644 +--- a/traefik/values.yaml ++++ b/traefik/values.yaml +@@ -237,8 +237,46 @@ metrics: + prometheus: + entryPoint: metrics + # addRoutersLabels: true +- # statsd: +- # address: localhost:8125 ++ # statsd: ++ # address: localhost:8125 ++## ++## enable optional CRDs for Prometheus Operator ++## ++ # serviceMonitor: ++ # additionalLabels: ++ # foo: bar ++ # namespace: "another-namespace" ++ # namespaceSelector: {} ++ # metricRelabelings: [] ++ # - sourceLabels: [__name__] ++ # separator: ; ++ # regex: ^fluentd_output_status_buffer_(oldest|newest)_.+ ++ # replacement: $1 ++ # action: drop ++ # relabelings: [] ++ # - sourceLabels: [__meta_kubernetes_pod_node_name] ++ # separator: ; ++ # regex: ^(.*)$ ++ # targetLabel: nodename ++ # replacement: $1 ++ # action: replace ++ # jobLabel: traefik ++ # scrapeInterval: 30s ++ # scrapeTimeout: 5s ++ # honorLabels: true ++ # prometheusRule: ++ # additionalLabels: {} ++ # namespace: "another-namespace" ++ # rules: ++ # - alert: TraefikDown ++ # expr: up{job="traefik"} == 0 ++ # for: 5m ++ # labels: ++ # context: traefik ++ # severity: warning ++ # annotations: ++ # summary: "Traefik Down" ++ # description: "{{ $labels.pod }} on {{ $labels.nodename }} is down" + + tracing: {} + # instana: +``` + +## 16.0.0 ![AppVersion: 2.9.1](https://img.shields.io/static/v1?label=AppVersion&message=2.9.1&color=success&logo=) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2022-10-19 + +* :fire: Remove `Pilot` and `fallbackApiVersion` (#665) + +### Default value changes + +```diff +diff --git a/traefik/values.yaml b/traefik/values.yaml +index 03fdaed..7e335b5 100644 +--- a/traefik/values.yaml ++++ b/traefik/values.yaml +@@ -84,15 +84,6 @@ ingressClass: + # true is not unit-testable yet, pending https://github.com/rancher/helm-unittest/pull/12 + enabled: false + isDefaultClass: false +- # Use to force a networking.k8s.io API Version for certain CI/CD applications. E.g. "v1beta1" +- fallbackApiVersion: "" +- +-# Activate Pilot integration +-pilot: +- enabled: false +- token: "" +- # Toggle Pilot Dashboard +- # dashboard: false + + # Enable experimental features + experimental: +``` + +## 15.3.1 ![AppVersion: 2.9.1](https://img.shields.io/static/v1?label=AppVersion&message=2.9.1&color=success&logo=) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2022-10-18 + +* :art: Improve `IngressRoute` structure (#674) + + +## 15.3.0 ![AppVersion: 2.9.1](https://img.shields.io/static/v1?label=AppVersion&message=2.9.1&color=success&logo=) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2022-10-18 + +* 📌 Add capacity to enable User-facing role + +### Default value changes + +```diff +diff --git a/traefik/values.yaml b/traefik/values.yaml +index 76aac93..03fdaed 100644 +--- a/traefik/values.yaml ++++ b/traefik/values.yaml +@@ -553,10 +553,12 @@ hostNetwork: false + # Whether Role Based Access Control objects like roles and rolebindings should be created + rbac: + enabled: true +- + # If set to false, installs ClusterRole and ClusterRoleBinding so Traefik can be used across namespaces. + # If set to true, installs Role and RoleBinding. Providers will only watch target namespace. + namespaced: false ++ # Enable user-facing roles ++ # https://kubernetes.io/docs/reference/access-authn-authz/rbac/#user-facing-roles ++ # aggregateTo: [ "admin" ] + + # Enable to create a PodSecurityPolicy and assign it to the Service Account via RoleBinding or ClusterRoleBinding + podSecurityPolicy: +``` + +## 15.2.2 ![AppVersion: 2.9.1](https://img.shields.io/static/v1?label=AppVersion&message=2.9.1&color=success&logo=) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2022-10-17 + +* Fix provider namespace changes + + +## 15.2.1 ![AppVersion: 2.9.1](https://img.shields.io/static/v1?label=AppVersion&message=2.9.1&color=success&logo=) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2022-10-17 + +* 🐛 fix provider namespace changes + + +## 15.2.0 ![AppVersion: 2.9.1](https://img.shields.io/static/v1?label=AppVersion&message=2.9.1&color=success&logo=) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2022-10-17 + +* :bug: Allow to watch on specific namespaces without using rbac.namespaced (#666) + +### Default value changes + +```diff +diff --git a/traefik/values.yaml b/traefik/values.yaml +index 781ac15..76aac93 100644 +--- a/traefik/values.yaml ++++ b/traefik/values.yaml +@@ -555,7 +555,7 @@ rbac: + enabled: true + + # If set to false, installs ClusterRole and ClusterRoleBinding so Traefik can be used across namespaces. +- # If set to true, installs namespace-specific Role and RoleBinding and requires provider configuration be set to that same namespace ++ # If set to true, installs Role and RoleBinding. Providers will only watch target namespace. + namespaced: false + + # Enable to create a PodSecurityPolicy and assign it to the Service Account via RoleBinding or ClusterRoleBinding +``` + +## 15.1.1 ![AppVersion: 2.9.1](https://img.shields.io/static/v1?label=AppVersion&message=2.9.1&color=success&logo=) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2022-10-17 + +* :goal_net: Fail gracefully when http3 is not enabled correctly (#667) + + +## 15.1.0 ![AppVersion: 2.9.1](https://img.shields.io/static/v1?label=AppVersion&message=2.9.1&color=success&logo=) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2022-10-14 + +* :sparkles: add optional topologySpreadConstraints (#663) + +### Default value changes + +```diff +diff --git a/traefik/values.yaml b/traefik/values.yaml +index fc2c371..781ac15 100644 +--- a/traefik/values.yaml ++++ b/traefik/values.yaml +@@ -593,6 +593,15 @@ affinity: {} + + nodeSelector: {} + tolerations: [] ++topologySpreadConstraints: [] ++# # This example topologySpreadConstraints forces the scheduler to put traefik pods ++# # on nodes where no other traefik pods are scheduled. ++# - labelSelector: ++# matchLabels: ++# app: '{{ template "traefik.name" . }}' ++# maxSkew: 1 ++# topologyKey: kubernetes.io/hostname ++# whenUnsatisfiable: DoNotSchedule + + # Pods can have priority. + # Priority indicates the importance of a Pod relative to other Pods. +``` + +## 15.0.0 ![AppVersion: 2.9.1](https://img.shields.io/static/v1?label=AppVersion&message=2.9.1&color=success&logo=) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2022-10-13 + +* :rocket: Enable TLS by default on `websecure` port (#657) + +### Default value changes + +```diff +diff --git a/traefik/values.yaml b/traefik/values.yaml +index 400a29a..fc2c371 100644 +--- a/traefik/values.yaml ++++ b/traefik/values.yaml +@@ -389,7 +389,7 @@ ports: + # Set TLS at the entrypoint + # https://doc.traefik.io/traefik/routing/entrypoints/#tls + tls: +- enabled: false ++ enabled: true + # this is the name of a TLSOption definition + options: "" + certResolver: "" +``` + +## 14.0.2 ![AppVersion: 2.9.1](https://img.shields.io/static/v1?label=AppVersion&message=2.9.1&color=success&logo=) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2022-10-13 + +* :memo: Add Changelog (#661) + + +## 14.0.1 ![AppVersion: 2.9.1](https://img.shields.io/static/v1?label=AppVersion&message=2.9.1&color=success&logo=) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2022-10-11 + +* :memo: Update workaround for permissions 660 on acme.json + +### Default value changes + +```diff +diff --git a/traefik/values.yaml b/traefik/values.yaml +index a4e4ff2..400a29a 100644 +--- a/traefik/values.yaml ++++ b/traefik/values.yaml +@@ -45,10 +45,10 @@ deployment: + # Additional initContainers (e.g. for setting file permission as shown below) + initContainers: [] + # The "volume-permissions" init container is required if you run into permission issues. +- # Related issue: https://github.com/traefik/traefik/issues/6972 ++ # Related issue: https://github.com/traefik/traefik/issues/6825 + # - name: volume-permissions +- # image: busybox:1.31.1 +- # command: ["sh", "-c", "chmod -Rv 600 /data/*"] ++ # image: busybox:1.35 ++ # command: ["sh", "-c", "touch /data/acme.json && chmod -Rv 600 /data/* && chown 65532:65532 /data/acme.json"] + # volumeMounts: + # - name: data + # mountPath: /data +``` + +## 14.0.0 ![AppVersion: 2.9.1](https://img.shields.io/static/v1?label=AppVersion&message=2.9.1&color=success&logo=) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2022-10-11 + +* Limit rbac to only required resources for Ingress and CRD providers + + +## 13.0.1 ![AppVersion: 2.9.1](https://img.shields.io/static/v1?label=AppVersion&message=2.9.1&color=success&logo=) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2022-10-11 + +* Add helper function for common labels + + +## 13.0.0 ![AppVersion: 2.9.1](https://img.shields.io/static/v1?label=AppVersion&message=2.9.1&color=success&logo=) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2022-10-11 + +* Moved list object to individual objects + + +## 12.0.7 ![AppVersion: 2.9.1](https://img.shields.io/static/v1?label=AppVersion&message=2.9.1&color=success&logo=) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2022-10-10 + +* :lipstick: Affinity templating and example (#557) + +### Default value changes + +```diff +diff --git a/traefik/values.yaml b/traefik/values.yaml +index 4431c36..a4e4ff2 100644 +--- a/traefik/values.yaml ++++ b/traefik/values.yaml +@@ -578,19 +578,19 @@ resources: {} + # limits: + # cpu: "300m" + # memory: "150Mi" ++ ++# This example pod anti-affinity forces the scheduler to put traefik pods ++# on nodes where no other traefik pods are scheduled. ++# It should be used when hostNetwork: true to prevent port conflicts + affinity: {} +-# # This example pod anti-affinity forces the scheduler to put traefik pods +-# # on nodes where no other traefik pods are scheduled. +-# # It should be used when hostNetwork: true to prevent port conflicts +-# podAntiAffinity: +-# requiredDuringSchedulingIgnoredDuringExecution: +-# - labelSelector: +-# matchExpressions: +-# - key: app.kubernetes.io/name +-# operator: In +-# values: +-# - {{ template "traefik.name" . }} +-# topologyKey: kubernetes.io/hostname ++# podAntiAffinity: ++# requiredDuringSchedulingIgnoredDuringExecution: ++# - labelSelector: ++# matchLabels: ++# app.kubernetes.io/name: '{{ template "traefik.name" . }}' ++# app.kubernetes.io/instance: '{{ .Release.Name }}' ++# topologyKey: kubernetes.io/hostname ++ + nodeSelector: {} + tolerations: [] + +``` + +## 12.0.6 ![AppVersion: 2.9.1](https://img.shields.io/static/v1?label=AppVersion&message=2.9.1&color=success&logo=) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2022-10-10 + +* :bug: Ignore kustomization file used for CRDs update (#653) + + +## 12.0.5 ![AppVersion: 2.9.1](https://img.shields.io/static/v1?label=AppVersion&message=2.9.1&color=success&logo=) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2022-10-10 + +* :memo: Establish Traefik & CRD update process + +### Default value changes + +```diff +diff --git a/traefik/values.yaml b/traefik/values.yaml +index 3526729..4431c36 100644 +--- a/traefik/values.yaml ++++ b/traefik/values.yaml +@@ -342,6 +342,7 @@ ports: + + # Override the liveness/readiness port. This is useful to integrate traefik + # with an external Load Balancer that performs healthchecks. ++ # Default: ports.traefik.port + # healthchecksPort: 9000 + + # Override the liveness/readiness scheme. Useful for getting ping to +``` + +## 12.0.4 ![AppVersion: 2.9.1](https://img.shields.io/static/v1?label=AppVersion&message=2.9.1&color=success&logo=) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2022-10-10 + +* Allows ingressClass to be used without semver-compatible image tag + + +## 12.0.3 ![AppVersion: 2.9.1](https://img.shields.io/static/v1?label=AppVersion&message=2.9.1&color=success&logo=) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2022-10-10 + +* :bug: Should check hostNetwork when hostPort != containerPort + + +## 12.0.2 ![AppVersion: 2.9.1](https://img.shields.io/static/v1?label=AppVersion&message=2.9.1&color=success&logo=) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2022-10-07 + +* :goal_net: Fail gracefully when hostNetwork is enabled and hostPort != containerPort + + +## 12.0.1 ![AppVersion: 2.9.1](https://img.shields.io/static/v1?label=AppVersion&message=2.9.1&color=success&logo=) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2022-10-07 + +* :bug: Fix a typo on `behavior` for HPA v2 + + +## 12.0.0 ![AppVersion: 2.9.1](https://img.shields.io/static/v1?label=AppVersion&message=2.9.1&color=success&logo=) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2022-10-06 + +* Update default HPA API Version to `v2` and add support for behavior (#518) + +### Default value changes + +```diff +diff --git a/traefik/values.yaml b/traefik/values.yaml +index 2bd51f8..3526729 100644 +--- a/traefik/values.yaml ++++ b/traefik/values.yaml +@@ -488,11 +488,22 @@ autoscaling: + # - type: Resource + # resource: + # name: cpu +-# targetAverageUtilization: 60 ++# target: ++# type: Utilization ++# averageUtilization: 60 + # - type: Resource + # resource: + # name: memory +-# targetAverageUtilization: 60 ++# target: ++# type: Utilization ++# averageUtilization: 60 ++# behavior: ++# scaleDown: ++# stabilizationWindowSeconds: 300 ++# policies: ++# - type: Pods ++# value: 1 ++# periodSeconds: 60 + + # Enable persistence using Persistent Volume Claims + # ref: http://kubernetes.io/docs/user-guide/persistent-volumes/ +``` + +## 11.1.1 ![AppVersion: 2.9.1](https://img.shields.io/static/v1?label=AppVersion&message=2.9.1&color=success&logo=) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2022-10-05 + +* 🔊 add failure message when using maxUnavailable 0 and hostNetwork + + +## 11.1.0 ![AppVersion: 2.9.1](https://img.shields.io/static/v1?label=AppVersion&message=2.9.1&color=success&logo=) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2022-10-04 + +* Update Traefik to v2.9.1 + + +## 11.0.0 ![AppVersion: 2.8.7](https://img.shields.io/static/v1?label=AppVersion&message=2.8.7&color=success&logo=) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2022-10-04 + +* tweak default values to avoid downtime when updating + +### Default value changes + +```diff +diff --git a/traefik/values.yaml b/traefik/values.yaml +index 844cadc..2bd51f8 100644 +--- a/traefik/values.yaml ++++ b/traefik/values.yaml +@@ -126,20 +126,20 @@ ingressRoute: + entryPoints: ["traefik"] + + rollingUpdate: +- maxUnavailable: 1 ++ maxUnavailable: 0 + maxSurge: 1 + + # Customize liveness and readiness probe values. + readinessProbe: + failureThreshold: 1 +- initialDelaySeconds: 10 ++ initialDelaySeconds: 2 + periodSeconds: 10 + successThreshold: 1 + timeoutSeconds: 2 + + livenessProbe: + failureThreshold: 3 +- initialDelaySeconds: 10 ++ initialDelaySeconds: 2 + periodSeconds: 10 + successThreshold: 1 + timeoutSeconds: 2 +``` + +## 10.33.0 ![AppVersion: 2.8.7](https://img.shields.io/static/v1?label=AppVersion&message=2.8.7&color=success&logo=) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2022-10-04 + +* :rocket: Add `extraObjects` value that allows creating adhoc resources + +### Default value changes + +```diff +diff --git a/traefik/values.yaml b/traefik/values.yaml +index c926bd9..844cadc 100644 +--- a/traefik/values.yaml ++++ b/traefik/values.yaml +@@ -598,3 +598,10 @@ securityContext: + + podSecurityContext: + fsGroup: 65532 ++ ++# ++# Extra objects to deploy (value evaluated as a template) ++# ++# In some cases, it can avoid the need for additional, extended or adhoc deployments. ++# See #595 for more details and traefik/tests/extra.yaml for example. ++extraObjects: [] +``` + +## 10.32.0 ![AppVersion: 2.8.7](https://img.shields.io/static/v1?label=AppVersion&message=2.8.7&color=success&logo=) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2022-10-03 + +* Add support setting middleware on entrypoint + +### Default value changes + +```diff +diff --git a/traefik/values.yaml b/traefik/values.yaml +index 3957448..c926bd9 100644 +--- a/traefik/values.yaml ++++ b/traefik/values.yaml +@@ -397,6 +397,16 @@ ports: + # sans: + # - foo.example.com + # - bar.example.com ++ # ++ # One can apply Middlewares on an entrypoint ++ # https://doc.traefik.io/traefik/middlewares/overview/ ++ # https://doc.traefik.io/traefik/routing/entrypoints/#middlewares ++ # /!\ It introduces here a link between your static configuration and your dynamic configuration /!\ ++ # It follows the provider naming convention: https://doc.traefik.io/traefik/providers/overview/#provider-namespace ++ # middlewares: ++ # - namespace-name1@kubernetescrd ++ # - namespace-name2@kubernetescrd ++ middlewares: [] + metrics: + # When using hostNetwork, use another port to avoid conflict with node exporter: + # https://github.com/prometheus/prometheus/wiki/Default-port-allocations +``` + +## 10.31.0 ![AppVersion: 2.8.7](https://img.shields.io/static/v1?label=AppVersion&message=2.8.7&color=success&logo=) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2022-10-03 + +* Support setting dashboard entryPoints for ingressRoute resource + +### Default value changes + +```diff +diff --git a/traefik/values.yaml b/traefik/values.yaml +index c9feb76..3957448 100644 +--- a/traefik/values.yaml ++++ b/traefik/values.yaml +@@ -120,6 +120,10 @@ ingressRoute: + annotations: {} + # Additional ingressRoute labels (e.g. for filtering IngressRoute by custom labels) + labels: {} ++ # Specify the allowed entrypoints to use for the dashboard ingress route, (e.g. traefik, web, websecure). ++ # By default, it's using traefik entrypoint, which is not exposed. ++ # /!\ Do not expose your dashboard without any protection over the internet /!\ ++ entryPoints: ["traefik"] + + rollingUpdate: + maxUnavailable: 1 +``` + +## 10.30.2 ![AppVersion: 2.8.7](https://img.shields.io/static/v1?label=AppVersion&message=2.8.7&color=success&logo=) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2022-10-03 + +* :test_tube: Fail gracefully when asked to provide a service without ports + + +## 10.30.1 ![AppVersion: 2.8.7](https://img.shields.io/static/v1?label=AppVersion&message=2.8.7&color=success&logo=) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2022-09-30 + +* :arrow_up: Upgrade helm, ct & unittest (#638) + + +## 10.30.0 ![AppVersion: 2.8.7](https://img.shields.io/static/v1?label=AppVersion&message=2.8.7&color=success&logo=) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2022-09-30 + +* Add support HTTPS scheme for healthcheks + +### Default value changes + +```diff +diff --git a/traefik/values.yaml b/traefik/values.yaml +index fed4a8a..c9feb76 100644 +--- a/traefik/values.yaml ++++ b/traefik/values.yaml +@@ -340,6 +340,10 @@ ports: + # with an external Load Balancer that performs healthchecks. + # healthchecksPort: 9000 + ++ # Override the liveness/readiness scheme. Useful for getting ping to ++ # respond on websecure entryPoint. ++ # healthchecksScheme: HTTPS ++ + # Defines whether the port is exposed if service.type is LoadBalancer or + # NodePort. + # +``` + +## 10.29.0 ![AppVersion: 2.8.7](https://img.shields.io/static/v1?label=AppVersion&message=2.8.7&color=success&logo=) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2022-09-29 + +* Add missing tracing options + +### Default value changes + +```diff +diff --git a/traefik/values.yaml b/traefik/values.yaml +index d1708cc..fed4a8a 100644 +--- a/traefik/values.yaml ++++ b/traefik/values.yaml +@@ -247,12 +247,45 @@ metrics: + + tracing: {} + # instana: +- # enabled: true ++ # localAgentHost: 127.0.0.1 ++ # localAgentPort: 42699 ++ # logLevel: info ++ # enableAutoProfile: true + # datadog: + # localAgentHostPort: 127.0.0.1:8126 + # debug: false + # globalTag: "" + # prioritySampling: false ++ # jaeger: ++ # samplingServerURL: http://localhost:5778/sampling ++ # samplingType: const ++ # samplingParam: 1.0 ++ # localAgentHostPort: 127.0.0.1:6831 ++ # gen128Bit: false ++ # propagation: jaeger ++ # traceContextHeaderName: uber-trace-id ++ # disableAttemptReconnecting: true ++ # collector: ++ # endpoint: "" ++ # user: "" ++ # password: "" ++ # zipkin: ++ # httpEndpoint: http://localhost:9411/api/v2/spans ++ # sameSpan: false ++ # id128Bit: true ++ # sampleRate: 1.0 ++ # haystack: ++ # localAgentHost: 127.0.0.1 ++ # localAgentPort: 35000 ++ # globalTag: "" ++ # traceIDHeaderName: "" ++ # parentIDHeaderName: "" ++ # spanIDHeaderName: "" ++ # baggagePrefixHeaderName: "" ++ # elastic: ++ # serverURL: http://localhost:8200 ++ # secretToken: "" ++ # serviceEnvironment: "" + + globalArguments: + - "--global.checknewversion" +``` + +## 10.28.0 ![AppVersion: 2.8.7](https://img.shields.io/static/v1?label=AppVersion&message=2.8.7&color=success&logo=) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2022-09-29 + +* feat: add lifecycle for prestop and poststart + +### Default value changes + +```diff +diff --git a/traefik/values.yaml b/traefik/values.yaml +index 19a133c..d1708cc 100644 +--- a/traefik/values.yaml ++++ b/traefik/values.yaml +@@ -59,6 +59,17 @@ deployment: + # Additional imagePullSecrets + imagePullSecrets: [] + # - name: myRegistryKeySecretName ++ # Pod lifecycle actions ++ lifecycle: {} ++ # preStop: ++ # exec: ++ # command: ["/bin/sh", "-c", "sleep 40"] ++ # postStart: ++ # httpGet: ++ # path: /ping ++ # port: 9000 ++ # host: localhost ++ # scheme: HTTP + + # Pod disruption budget + podDisruptionBudget: +``` + +## 10.27.0 ![AppVersion: 2.8.7](https://img.shields.io/static/v1?label=AppVersion&message=2.8.7&color=success&logo=) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2022-09-29 + +* feat: add create gateway option + +### Default value changes + +```diff +diff --git a/traefik/values.yaml b/traefik/values.yaml +index d9c745e..19a133c 100644 +--- a/traefik/values.yaml ++++ b/traefik/values.yaml +@@ -91,6 +91,8 @@ experimental: + enabled: false + kubernetesGateway: + enabled: false ++ gateway: ++ enabled: true + # certificate: + # group: "core" + # kind: "Secret" +``` + +## 10.26.1 ![AppVersion: 2.8.7](https://img.shields.io/static/v1?label=AppVersion&message=2.8.7&color=success&logo=) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2022-09-28 + +* 🐛 fix rbac templating (#636) + + +## 10.26.0 ![AppVersion: 2.8.7](https://img.shields.io/static/v1?label=AppVersion&message=2.8.7&color=success&logo=) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2022-09-28 + +* :bug: Fix ingressClass support when rbac.namespaced=true (#499) + + +## 10.25.1 ![AppVersion: 2.8.7](https://img.shields.io/static/v1?label=AppVersion&message=2.8.7&color=success&logo=) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2022-09-28 + +* Add ingressclasses to traefik role + + +## 10.25.0 ![AppVersion: 2.8.7](https://img.shields.io/static/v1?label=AppVersion&message=2.8.7&color=success&logo=) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2022-09-27 + +* Add TLSStore resource to chart + +### Default value changes + +```diff +diff --git a/traefik/values.yaml b/traefik/values.yaml +index d4011c3..d9c745e 100644 +--- a/traefik/values.yaml ++++ b/traefik/values.yaml +@@ -373,6 +373,15 @@ ports: + # - CurveP384 + tlsOptions: {} + ++# TLS Store are created as TLSStore CRDs. This is useful if you want to set a default certificate ++# https://doc.traefik.io/traefik/https/tls/#default-certificate ++# Example: ++# tlsStore: ++# default: ++# defaultCertificate: ++# secretName: tls-cert ++tlsStore: {} ++ + # Options for the main traefik service, where the entrypoints traffic comes + # from. + service: +``` + +## 10.24.5 ![AppVersion: 2.8.7](https://img.shields.io/static/v1?label=AppVersion&message=2.8.7&color=success&logo=) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2022-09-27 + +* Suggest an alternative port for metrics + +### Default value changes + +```diff +diff --git a/traefik/values.yaml b/traefik/values.yaml +index 81f2e85..d4011c3 100644 +--- a/traefik/values.yaml ++++ b/traefik/values.yaml +@@ -344,6 +344,8 @@ ports: + # - foo.example.com + # - bar.example.com + metrics: ++ # When using hostNetwork, use another port to avoid conflict with node exporter: ++ # https://github.com/prometheus/prometheus/wiki/Default-port-allocations + port: 9100 + # hostPort: 9100 + # Defines whether the port is exposed if service.type is LoadBalancer or +``` + +## 10.24.4 ![AppVersion: 2.8.7](https://img.shields.io/static/v1?label=AppVersion&message=2.8.7&color=success&logo=) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2022-09-26 + +* Update Traefik to v2.8.7 + + +## 10.24.3 ![AppVersion: 2.8.5](https://img.shields.io/static/v1?label=AppVersion&message=2.8.5&color=success&logo=) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2022-09-14 + +* Update Traefik version to v2.8.5 + + +## 10.24.2 ![AppVersion: 2.8.4](https://img.shields.io/static/v1?label=AppVersion&message=2.8.4&color=success&logo=) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2022-09-05 + +* Update Traefik version to v2.8.4 + + +## 10.24.1 ![AppVersion: 2.8.0](https://img.shields.io/static/v1?label=AppVersion&message=2.8.0&color=success&logo=) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2022-08-29 + +* Update PodDisruptionBudget apiVersion to policy/v1 + + +## 10.24.0 ![AppVersion: 2.8.0](https://img.shields.io/static/v1?label=AppVersion&message=2.8.0&color=success&logo=) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2022-06-30 + +* Update Traefik version to v2.8.0 + + +## 10.23.0 ![AppVersion: 2.7.1](https://img.shields.io/static/v1?label=AppVersion&message=2.7.1&color=success&logo=) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2022-06-27 + +* Support environment variable usage for Datadog + + +## 10.22.0 ![AppVersion: 2.7.1](https://img.shields.io/static/v1?label=AppVersion&message=2.7.1&color=success&logo=) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2022-06-22 + +* Allow setting revisionHistoryLimit for Deployment and DaemonSet + +### Default value changes + +```diff +diff --git a/traefik/values.yaml b/traefik/values.yaml +index d5785ab..81f2e85 100644 +--- a/traefik/values.yaml ++++ b/traefik/values.yaml +@@ -14,6 +14,8 @@ deployment: + kind: Deployment + # Number of pods of the deployment (only applies when kind == Deployment) + replicas: 1 ++ # Number of old history to retain to allow rollback (If not set, default Kubernetes value is set to 10) ++ # revisionHistoryLimit: 1 + # Amount of time (in seconds) before Kubernetes will send the SIGKILL signal if Traefik does not shut down + terminationGracePeriodSeconds: 60 + # The minimum number of seconds Traefik needs to be up and running before the DaemonSet/Deployment controller considers it available +``` + +## 10.21.1 ![AppVersion: 2.7.1](https://img.shields.io/static/v1?label=AppVersion&message=2.7.1&color=success&logo=) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2022-06-15 + +* Update Traefik version to 2.7.1 + + +## 10.21.0 ![AppVersion: 2.7.0](https://img.shields.io/static/v1?label=AppVersion&message=2.7.0&color=success&logo=) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2022-06-15 + +* Support allowEmptyServices config for KubernetesCRD + +### Default value changes + +```diff +diff --git a/traefik/values.yaml b/traefik/values.yaml +index e141e29..d5785ab 100644 +--- a/traefik/values.yaml ++++ b/traefik/values.yaml +@@ -133,6 +133,7 @@ providers: + enabled: true + allowCrossNamespace: false + allowExternalNameServices: false ++ allowEmptyServices: false + # ingressClass: traefik-internal + # labelSelector: environment=production,method=traefik + namespaces: [] +``` + +## 10.20.1 ![AppVersion: 2.7.0](https://img.shields.io/static/v1?label=AppVersion&message=2.7.0&color=success&logo=) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2022-06-01 + +* Add Acme certificate resolver configuration + +### Default value changes + +```diff +diff --git a/traefik/values.yaml b/traefik/values.yaml +index a16b107..e141e29 100644 +--- a/traefik/values.yaml ++++ b/traefik/values.yaml +@@ -433,6 +433,27 @@ persistence: + annotations: {} + # subPath: "" # only mount a subpath of the Volume into the pod + ++certResolvers: {} ++# letsencrypt: ++# # for challenge options cf. https://doc.traefik.io/traefik/https/acme/ ++# email: email@example.com ++# dnsChallenge: ++# # also add the provider's required configuration under env ++# # or expand then from secrets/configmaps with envfrom ++# # cf. https://doc.traefik.io/traefik/https/acme/#providers ++# provider: digitalocean ++# # add futher options for the dns challenge as needed ++# # cf. https://doc.traefik.io/traefik/https/acme/#dnschallenge ++# delayBeforeCheck: 30 ++# resolvers: ++# - 1.1.1.1 ++# - 8.8.8.8 ++# tlsChallenge: true ++# httpChallenge: ++# entryPoint: "web" ++# # match the path to persistence ++# storage: /data/acme.json ++ + # If hostNetwork is true, runs traefik in the host network namespace + # To prevent unschedulabel pods due to port collisions, if hostNetwork=true + # and replicas>1, a pod anti-affinity is recommended and will be set if the +``` + +## 10.20.0 ![AppVersion: 2.7.0](https://img.shields.io/static/v1?label=AppVersion&message=2.7.0&color=success&logo=) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2022-05-25 + +* Update Traefik Proxy to v2.7.0 + + +## 10.19.5 ![AppVersion: 2.6.6](https://img.shields.io/static/v1?label=AppVersion&message=2.6.6&color=success&logo=) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2022-05-04 + +* Upgrade Traefik to 2.6.6 + + +## 10.19.4 ![AppVersion: 2.6.3](https://img.shields.io/static/v1?label=AppVersion&message=2.6.3&color=success&logo=) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2022-03-31 + +* Update Traefik dependency version to 2.6.3 + + +## 10.19.3 ![AppVersion: 2.6.2](https://img.shields.io/static/v1?label=AppVersion&message=2.6.2&color=success&logo=) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2022-03-30 + +* Update CRDs to match the ones defined in the reference documentation + + +## 10.19.2 ![AppVersion: 2.6.2](https://img.shields.io/static/v1?label=AppVersion&message=2.6.2&color=success&logo=) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2022-03-30 + +* Revert Traefik version to 2.6.2 + + +## 10.19.1 ![AppVersion: 2.6.3](https://img.shields.io/static/v1?label=AppVersion&message=2.6.3&color=success&logo=) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2022-03-30 + +* Update Traefik version to 2.6.3 + + +## 10.19.0 ![AppVersion: 2.6.2](https://img.shields.io/static/v1?label=AppVersion&message=2.6.2&color=success&logo=) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2022-03-28 + +* Support ingressClass option for KubernetesIngress provider + +### Default value changes + +```diff +diff --git a/traefik/values.yaml b/traefik/values.yaml +index 02ab704..a16b107 100644 +--- a/traefik/values.yaml ++++ b/traefik/values.yaml +@@ -142,6 +142,7 @@ providers: + enabled: true + allowExternalNameServices: false + allowEmptyServices: false ++ # ingressClass: traefik-internal + # labelSelector: environment=production,method=traefik + namespaces: [] + # - "default" +``` + +## 10.18.0 ![AppVersion: 2.6.2](https://img.shields.io/static/v1?label=AppVersion&message=2.6.2&color=success&logo=) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2022-03-28 + +* Support liveness and readyness probes customization + +### Default value changes + +```diff +diff --git a/traefik/values.yaml b/traefik/values.yaml +index 15f1103..02ab704 100644 +--- a/traefik/values.yaml ++++ b/traefik/values.yaml +@@ -110,6 +110,20 @@ rollingUpdate: + maxUnavailable: 1 + maxSurge: 1 + ++# Customize liveness and readiness probe values. ++readinessProbe: ++ failureThreshold: 1 ++ initialDelaySeconds: 10 ++ periodSeconds: 10 ++ successThreshold: 1 ++ timeoutSeconds: 2 ++ ++livenessProbe: ++ failureThreshold: 3 ++ initialDelaySeconds: 10 ++ periodSeconds: 10 ++ successThreshold: 1 ++ timeoutSeconds: 2 + + # + # Configure providers +``` + +## 10.17.0 ![AppVersion: 2.6.2](https://img.shields.io/static/v1?label=AppVersion&message=2.6.2&color=success&logo=) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2022-03-28 + +* Support Datadog tracing + +### Default value changes + +```diff +diff --git a/traefik/values.yaml b/traefik/values.yaml +index 4dccd1a..15f1103 100644 +--- a/traefik/values.yaml ++++ b/traefik/values.yaml +@@ -217,6 +217,11 @@ metrics: + tracing: {} + # instana: + # enabled: true ++ # datadog: ++ # localAgentHostPort: 127.0.0.1:8126 ++ # debug: false ++ # globalTag: "" ++ # prioritySampling: false + + globalArguments: + - "--global.checknewversion" +``` + +## 10.16.1 ![AppVersion: 2.6.2](https://img.shields.io/static/v1?label=AppVersion&message=2.6.2&color=success&logo=) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2022-03-28 + +* Update Traefik version to 2.6.2 + + +## 10.16.0 ![AppVersion: 2.6.1](https://img.shields.io/static/v1?label=AppVersion&message=2.6.1&color=success&logo=) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2022-03-28 + +* Support allowEmptyServices for KubernetesIngress provider + +### Default value changes + +```diff +diff --git a/traefik/values.yaml b/traefik/values.yaml +index 1f9dbbe..4dccd1a 100644 +--- a/traefik/values.yaml ++++ b/traefik/values.yaml +@@ -127,6 +127,7 @@ providers: + kubernetesIngress: + enabled: true + allowExternalNameServices: false ++ allowEmptyServices: false + # labelSelector: environment=production,method=traefik + namespaces: [] + # - "default" +``` + +## 10.15.0 ![AppVersion: 2.6.1](https://img.shields.io/static/v1?label=AppVersion&message=2.6.1&color=success&logo=) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2022-03-08 + +* Add metrics.prometheus.addRoutersLabels option + +### Default value changes + +```diff +diff --git a/traefik/values.yaml b/traefik/values.yaml +index cd4d49b..1f9dbbe 100644 +--- a/traefik/values.yaml ++++ b/traefik/values.yaml +@@ -209,6 +209,7 @@ metrics: + # protocol: udp + prometheus: + entryPoint: metrics ++ # addRoutersLabels: true + # statsd: + # address: localhost:8125 + +``` + +## 10.14.2 ![AppVersion: 2.6.1](https://img.shields.io/static/v1?label=AppVersion&message=2.6.1&color=success&logo=) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2022-02-18 + +* Update Traefik to v2.6.1 + + +## 10.14.1 ![AppVersion: 2.6.0](https://img.shields.io/static/v1?label=AppVersion&message=2.6.0&color=success&logo=) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2022-02-09 + +* Add missing inFlightConn TCP middleware CRD + + +## 10.14.0 ![AppVersion: 2.6.0](https://img.shields.io/static/v1?label=AppVersion&message=2.6.0&color=success&logo=) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2022-02-03 + +* Add experimental HTTP/3 support + +### Default value changes + +```diff +diff --git a/traefik/values.yaml b/traefik/values.yaml +index d49122f..cd4d49b 100644 +--- a/traefik/values.yaml ++++ b/traefik/values.yaml +@@ -83,6 +83,8 @@ pilot: + + # Enable experimental features + experimental: ++ http3: ++ enabled: false + plugins: + enabled: false + kubernetesGateway: +@@ -300,6 +302,10 @@ ports: + # The port protocol (TCP/UDP) + protocol: TCP + # nodePort: 32443 ++ # Enable HTTP/3. ++ # Requires enabling experimental http3 feature and tls. ++ # Note that you cannot have a UDP entrypoint with the same port. ++ # http3: true + # Set TLS at the entrypoint + # https://doc.traefik.io/traefik/routing/entrypoints/#tls + tls: +``` + +## 10.13.0 ![AppVersion: 2.6.0](https://img.shields.io/static/v1?label=AppVersion&message=2.6.0&color=success&logo=) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2022-02-01 + +* Add support for ipFamilies + +### Default value changes + +```diff +diff --git a/traefik/values.yaml b/traefik/values.yaml +index 32fce6f..d49122f 100644 +--- a/traefik/values.yaml ++++ b/traefik/values.yaml +@@ -366,6 +366,11 @@ service: + # - 1.2.3.4 + # One of SingleStack, PreferDualStack, or RequireDualStack. + # ipFamilyPolicy: SingleStack ++ # List of IP families (e.g. IPv4 and/or IPv6). ++ # ref: https://kubernetes.io/docs/concepts/services-networking/dual-stack/#services ++ # ipFamilies: ++ # - IPv4 ++ # - IPv6 + + ## Create HorizontalPodAutoscaler object. + ## +``` + +## 10.12.0 ![AppVersion: 2.6.0](https://img.shields.io/static/v1?label=AppVersion&message=2.6.0&color=success&logo=) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2022-02-01 + +* Add shareProcessNamespace option to podtemplate + +### Default value changes + +```diff +diff --git a/traefik/values.yaml b/traefik/values.yaml +index ab25456..32fce6f 100644 +--- a/traefik/values.yaml ++++ b/traefik/values.yaml +@@ -50,6 +50,8 @@ deployment: + # volumeMounts: + # - name: data + # mountPath: /data ++ # Use process namespace sharing ++ shareProcessNamespace: false + # Custom pod DNS policy. Apply if `hostNetwork: true` + # dnsPolicy: ClusterFirstWithHostNet + # Additional imagePullSecrets +``` + +## 10.11.1 ![AppVersion: 2.6.0](https://img.shields.io/static/v1?label=AppVersion&message=2.6.0&color=success&logo=) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2022-01-31 + +* Fix anti-affinity example + +### Default value changes + +```diff +diff --git a/traefik/values.yaml b/traefik/values.yaml +index 8c72905..ab25456 100644 +--- a/traefik/values.yaml ++++ b/traefik/values.yaml +@@ -438,13 +438,13 @@ affinity: {} + # # It should be used when hostNetwork: true to prevent port conflicts + # podAntiAffinity: + # requiredDuringSchedulingIgnoredDuringExecution: +-# - labelSelector: +-# matchExpressions: +-# - key: app +-# operator: In +-# values: +-# - {{ template "traefik.name" . }} +-# topologyKey: failure-domain.beta.kubernetes.io/zone ++# - labelSelector: ++# matchExpressions: ++# - key: app.kubernetes.io/name ++# operator: In ++# values: ++# - {{ template "traefik.name" . }} ++# topologyKey: kubernetes.io/hostname + nodeSelector: {} + tolerations: [] + +``` + +## 10.11.0 ![AppVersion: 2.6.0](https://img.shields.io/static/v1?label=AppVersion&message=2.6.0&color=success&logo=) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2022-01-31 + +* Add setting to enable Instana tracing + +### Default value changes + +```diff +diff --git a/traefik/values.yaml b/traefik/values.yaml +index 7fe4a2c..8c72905 100644 +--- a/traefik/values.yaml ++++ b/traefik/values.yaml +@@ -208,6 +208,10 @@ metrics: + # statsd: + # address: localhost:8125 + ++tracing: {} ++ # instana: ++ # enabled: true ++ + globalArguments: + - "--global.checknewversion" + - "--global.sendanonymoususage" +``` + +## 10.10.0 ![AppVersion: 2.6.0](https://img.shields.io/static/v1?label=AppVersion&message=2.6.0&color=success&logo=) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2022-01-31 + +* Update Traefik to v2.6 + +### Default value changes + +```diff +diff --git a/traefik/values.yaml b/traefik/values.yaml +index 8ae4bd8..7fe4a2c 100644 +--- a/traefik/values.yaml ++++ b/traefik/values.yaml +@@ -85,9 +85,8 @@ experimental: + enabled: false + kubernetesGateway: + enabled: false +- appLabelSelector: "traefik" +- certificates: [] +- # - group: "core" ++ # certificate: ++ # group: "core" + # kind: "Secret" + # name: "mysecret" + # By default, Gateway would be created to the Namespace you are deploying Traefik to. +``` + +## 10.9.1 ![AppVersion: 2.5.6](https://img.shields.io/static/v1?label=AppVersion&message=2.5.6&color=success&logo=) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2021-12-24 + +* Bump traefik version to 2.5.6 + + +## 10.9.0 ![AppVersion: 2.5.4](https://img.shields.io/static/v1?label=AppVersion&message=2.5.4&color=success&logo=) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2021-12-20 + +* feat: add allowExternalNameServices to KubernetesIngress provider + +### Default value changes + +```diff +diff --git a/traefik/values.yaml b/traefik/values.yaml +index 79df205..8ae4bd8 100644 +--- a/traefik/values.yaml ++++ b/traefik/values.yaml +@@ -123,6 +123,7 @@ providers: + + kubernetesIngress: + enabled: true ++ allowExternalNameServices: false + # labelSelector: environment=production,method=traefik + namespaces: [] + # - "default" +``` + +## 10.8.0 ![AppVersion: 2.5.4](https://img.shields.io/static/v1?label=AppVersion&message=2.5.4&color=success&logo=) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2021-12-20 + +* Add support to specify minReadySeconds on Deployment/DaemonSet + +### Default value changes + +```diff +diff --git a/traefik/values.yaml b/traefik/values.yaml +index 7e9186b..79df205 100644 +--- a/traefik/values.yaml ++++ b/traefik/values.yaml +@@ -16,6 +16,8 @@ deployment: + replicas: 1 + # Amount of time (in seconds) before Kubernetes will send the SIGKILL signal if Traefik does not shut down + terminationGracePeriodSeconds: 60 ++ # The minimum number of seconds Traefik needs to be up and running before the DaemonSet/Deployment controller considers it available ++ minReadySeconds: 0 + # Additional deployment annotations (e.g. for jaeger-operator sidecar injection) + annotations: {} + # Additional deployment labels (e.g. for filtering deployment by custom labels) +``` + +## 10.7.1 ![AppVersion: 2.5.4](https://img.shields.io/static/v1?label=AppVersion&message=2.5.4&color=success&logo=) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2021-12-06 + +* Fix pod disruption when using percentages + +### Default value changes + +```diff +diff --git a/traefik/values.yaml b/traefik/values.yaml +index e0655c8..7e9186b 100644 +--- a/traefik/values.yaml ++++ b/traefik/values.yaml +@@ -52,13 +52,15 @@ deployment: + # dnsPolicy: ClusterFirstWithHostNet + # Additional imagePullSecrets + imagePullSecrets: [] +- # - name: myRegistryKeySecretName ++ # - name: myRegistryKeySecretName + + # Pod disruption budget + podDisruptionBudget: + enabled: false + # maxUnavailable: 1 ++ # maxUnavailable: 33% + # minAvailable: 0 ++ # minAvailable: 25% + + # Use ingressClass. Ignored if Traefik version < 2.3 / kubernetes < 1.18.x + ingressClass: +``` + +## 10.7.0 ![AppVersion: 2.5.4](https://img.shields.io/static/v1?label=AppVersion&message=2.5.4&color=success&logo=) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2021-12-06 + +* Add support for ipFamilyPolicy + +### Default value changes + +```diff +diff --git a/traefik/values.yaml b/traefik/values.yaml +index 3ec7105..e0655c8 100644 +--- a/traefik/values.yaml ++++ b/traefik/values.yaml +@@ -343,8 +343,8 @@ service: + annotationsUDP: {} + # Additional service labels (e.g. for filtering Service by custom labels) + labels: {} +- # Additional entries here will be added to the service spec. Cannot contains +- # type, selector or ports entries. ++ # Additional entries here will be added to the service spec. ++ # Cannot contain type, selector or ports entries. + spec: {} + # externalTrafficPolicy: Cluster + # loadBalancerIP: "1.2.3.4" +@@ -354,6 +354,8 @@ service: + # - 172.16.0.0/16 + externalIPs: [] + # - 1.2.3.4 ++ # One of SingleStack, PreferDualStack, or RequireDualStack. ++ # ipFamilyPolicy: SingleStack + + ## Create HorizontalPodAutoscaler object. + ## +``` + +## 10.6.2 ![AppVersion: 2.5.4](https://img.shields.io/static/v1?label=AppVersion&message=2.5.4&color=success&logo=) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2021-11-15 + +* Bump Traefik version to 2.5.4 + + +## 10.6.1 ![AppVersion: 2.5.3](https://img.shields.io/static/v1?label=AppVersion&message=2.5.3&color=success&logo=) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2021-11-05 + +* Add missing Gateway API resources to ClusterRole + + +## 10.6.0 ![AppVersion: 2.5.3](https://img.shields.io/static/v1?label=AppVersion&message=2.5.3&color=success&logo=) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2021-10-13 + +* feat: allow termination grace period to be configurable + +### Default value changes + +```diff +diff --git a/traefik/values.yaml b/traefik/values.yaml +index f06ebc6..3ec7105 100644 +--- a/traefik/values.yaml ++++ b/traefik/values.yaml +@@ -14,6 +14,8 @@ deployment: + kind: Deployment + # Number of pods of the deployment (only applies when kind == Deployment) + replicas: 1 ++ # Amount of time (in seconds) before Kubernetes will send the SIGKILL signal if Traefik does not shut down ++ terminationGracePeriodSeconds: 60 + # Additional deployment annotations (e.g. for jaeger-operator sidecar injection) + annotations: {} + # Additional deployment labels (e.g. for filtering deployment by custom labels) +``` + +## 10.5.0 ![AppVersion: 2.5.3](https://img.shields.io/static/v1?label=AppVersion&message=2.5.3&color=success&logo=) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2021-10-13 + +* feat: add allowExternalNameServices to Kubernetes CRD provider + +### Default value changes + +```diff +diff --git a/traefik/values.yaml b/traefik/values.yaml +index 3bcb350..f06ebc6 100644 +--- a/traefik/values.yaml ++++ b/traefik/values.yaml +@@ -109,6 +109,7 @@ providers: + kubernetesCRD: + enabled: true + allowCrossNamespace: false ++ allowExternalNameServices: false + # ingressClass: traefik-internal + # labelSelector: environment=production,method=traefik + namespaces: [] +``` + +## 10.4.2 ![AppVersion: 2.5.3](https://img.shields.io/static/v1?label=AppVersion&message=2.5.3&color=success&logo=) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2021-10-13 + +* fix(crd): add permissionsPolicy to headers middleware + + +## 10.4.1 ![AppVersion: 2.5.3](https://img.shields.io/static/v1?label=AppVersion&message=2.5.3&color=success&logo=) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2021-10-13 + +* fix(crd): add peerCertURI option to ServersTransport + + +## 10.4.0 ![AppVersion: 2.5.3](https://img.shields.io/static/v1?label=AppVersion&message=2.5.3&color=success&logo=) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2021-10-12 + +* Add Kubernetes CRD labelSelector and ingressClass options + +### Default value changes + +```diff +diff --git a/traefik/values.yaml b/traefik/values.yaml +index f54f5fe..3bcb350 100644 +--- a/traefik/values.yaml ++++ b/traefik/values.yaml +@@ -109,8 +109,11 @@ providers: + kubernetesCRD: + enabled: true + allowCrossNamespace: false ++ # ingressClass: traefik-internal ++ # labelSelector: environment=production,method=traefik + namespaces: [] + # - "default" ++ + kubernetesIngress: + enabled: true + # labelSelector: environment=production,method=traefik +``` + +## 10.3.6 ![AppVersion: 2.5.3](https://img.shields.io/static/v1?label=AppVersion&message=2.5.3&color=success&logo=) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2021-09-24 + +* Fix missing RequireAnyClientCert value to TLSOption CRD + + +## 10.3.5 ![AppVersion: 2.5.3](https://img.shields.io/static/v1?label=AppVersion&message=2.5.3&color=success&logo=) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2021-09-23 + +* Bump Traefik version to 2.5.3 + + +## 10.3.4 ![AppVersion: 2.5.1](https://img.shields.io/static/v1?label=AppVersion&message=2.5.1&color=success&logo=) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2021-09-17 + +* Add allowCrossNamespace option on kubernetesCRD provider + +### Default value changes + +```diff +diff --git a/traefik/values.yaml b/traefik/values.yaml +index 7e3a579..f54f5fe 100644 +--- a/traefik/values.yaml ++++ b/traefik/values.yaml +@@ -108,6 +108,7 @@ rollingUpdate: + providers: + kubernetesCRD: + enabled: true ++ allowCrossNamespace: false + namespaces: [] + # - "default" + kubernetesIngress: +``` + +## 10.3.3 ![AppVersion: 2.5.1](https://img.shields.io/static/v1?label=AppVersion&message=2.5.1&color=success&logo=) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2021-09-17 + +* fix(crd): missing alpnProtocols in TLSOption + + +## 10.3.2 ![AppVersion: 2.5.1](https://img.shields.io/static/v1?label=AppVersion&message=2.5.1&color=success&logo=) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2021-08-23 + +* Releasing 2.5.1 + + +## 10.3.1 ![AppVersion: 2.5.0](https://img.shields.io/static/v1?label=AppVersion&message=2.5.0&color=success&logo=) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2021-08-20 + +* Fix Ingress RBAC for namespaced scoped deployment + + +## 10.3.0 ![AppVersion: 2.5.0](https://img.shields.io/static/v1?label=AppVersion&message=2.5.0&color=success&logo=) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2021-08-18 + +* Releasing Traefik 2.5.0 + + +## 10.2.0 ![AppVersion: 2.4.13](https://img.shields.io/static/v1?label=AppVersion&message=2.4.13&color=success&logo=) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2021-08-18 + +* Allow setting TCP and UDP service annotations separately + +### Default value changes + +```diff +diff --git a/traefik/values.yaml b/traefik/values.yaml +index 72a01ea..7e3a579 100644 +--- a/traefik/values.yaml ++++ b/traefik/values.yaml +@@ -328,8 +328,12 @@ tlsOptions: {} + service: + enabled: true + type: LoadBalancer +- # Additional annotations (e.g. for cloud provider specific config) ++ # Additional annotations applied to both TCP and UDP services (e.g. for cloud provider specific config) + annotations: {} ++ # Additional annotations for TCP service only ++ annotationsTCP: {} ++ # Additional annotations for UDP service only ++ annotationsUDP: {} + # Additional service labels (e.g. for filtering Service by custom labels) + labels: {} + # Additional entries here will be added to the service spec. Cannot contains +``` + +## 10.1.6 ![AppVersion: 2.4.13](https://img.shields.io/static/v1?label=AppVersion&message=2.4.13&color=success&logo=) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2021-08-17 + +* fix: missing service labels + + +## 10.1.5 ![AppVersion: 2.4.13](https://img.shields.io/static/v1?label=AppVersion&message=2.4.13&color=success&logo=) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2021-08-17 + +* fix(pvc-annotaions): see traefik/traefik-helm-chart#471 + + +## 10.1.4 ![AppVersion: 2.4.13](https://img.shields.io/static/v1?label=AppVersion&message=2.4.13&color=success&logo=) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2021-08-17 + +* fix(ingressclass): fallbackApiVersion default shouldn't be `nil` + +### Default value changes + +```diff +diff --git a/traefik/values.yaml b/traefik/values.yaml +index 04d336c..72a01ea 100644 +--- a/traefik/values.yaml ++++ b/traefik/values.yaml +@@ -64,7 +64,7 @@ ingressClass: + enabled: false + isDefaultClass: false + # Use to force a networking.k8s.io API Version for certain CI/CD applications. E.g. "v1beta1" +- fallbackApiVersion: ++ fallbackApiVersion: "" + + # Activate Pilot integration + pilot: +``` + +## 10.1.3 ![AppVersion: 2.4.13](https://img.shields.io/static/v1?label=AppVersion&message=2.4.13&color=success&logo=) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2021-08-16 + +* Move Prometheus annotations to Pods + + +## 10.1.2 ![AppVersion: 2.4.13](https://img.shields.io/static/v1?label=AppVersion&message=2.4.13&color=success&logo=) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2021-08-10 + +* Version bumped 2.4.13 + + +## 10.1.1 ![AppVersion: 2.4.9](https://img.shields.io/static/v1?label=AppVersion&message=2.4.9&color=success&logo=) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2021-07-20 + +* Fixing Prometheus.io/port annotation + + +## 10.1.0 ![AppVersion: 2.4.9](https://img.shields.io/static/v1?label=AppVersion&message=2.4.9&color=success&logo=) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2021-07-20 + +* Add metrics framework, and prom annotations + +### Default value changes + +```diff +diff --git a/traefik/values.yaml b/traefik/values.yaml +index f6e370a..04d336c 100644 +--- a/traefik/values.yaml ++++ b/traefik/values.yaml +@@ -186,6 +186,17 @@ logs: + # Authorization: drop + # Content-Type: keep + ++metrics: ++ # datadog: ++ # address: 127.0.0.1:8125 ++ # influxdb: ++ # address: localhost:8089 ++ # protocol: udp ++ prometheus: ++ entryPoint: metrics ++ # statsd: ++ # address: localhost:8125 ++ + globalArguments: + - "--global.checknewversion" + - "--global.sendanonymoususage" +@@ -284,6 +295,20 @@ ports: + # sans: + # - foo.example.com + # - bar.example.com ++ metrics: ++ port: 9100 ++ # hostPort: 9100 ++ # Defines whether the port is exposed if service.type is LoadBalancer or ++ # NodePort. ++ # ++ # You may not want to expose the metrics port on production deployments. ++ # If you want to access it from outside of your cluster, ++ # use `kubectl port-forward` or create a secure ingress ++ expose: false ++ # The exposed port for this service ++ exposedPort: 9100 ++ # The port protocol (TCP/UDP) ++ protocol: TCP + + # TLS Options are created as TLSOption CRDs + # https://doc.traefik.io/traefik/https/tls/#tls-options +``` + +## 10.0.2 ![AppVersion: 2.4.9](https://img.shields.io/static/v1?label=AppVersion&message=2.4.9&color=success&logo=) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2021-07-14 + +* feat(gateway): introduces param / pick Namespace installing Gateway + +### Default value changes + +```diff +diff --git a/traefik/values.yaml b/traefik/values.yaml +index 9bf90ea..f6e370a 100644 +--- a/traefik/values.yaml ++++ b/traefik/values.yaml +@@ -84,6 +84,9 @@ experimental: + # - group: "core" + # kind: "Secret" + # name: "mysecret" ++ # By default, Gateway would be created to the Namespace you are deploying Traefik to. ++ # You may create that Gateway in another namespace, setting its name below: ++ # namespace: default + + # Create an IngressRoute for the dashboard + ingressRoute: +``` + +## 10.0.1 ![AppVersion: 2.4.9](https://img.shields.io/static/v1?label=AppVersion&message=2.4.9&color=success&logo=) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2021-07-14 + +* Add RBAC for middlewaretcps + + +## 10.0.0 ![AppVersion: 2.4.9](https://img.shields.io/static/v1?label=AppVersion&message=2.4.9&color=success&logo=) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2021-07-07 + +* Update CRD versions + + +## 9.20.1 ![AppVersion: 2.4.8](https://img.shields.io/static/v1?label=AppVersion&message=2.4.8&color=success&logo=) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2021-07-05 + +* Revert CRD templating + + +## 9.20.0 ![AppVersion: 2.4.8](https://img.shields.io/static/v1?label=AppVersion&message=2.4.8&color=success&logo=) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2021-07-05 + +* Add support for apiextensions v1 CRDs + + +## 9.19.2 ![AppVersion: 2.4.8](https://img.shields.io/static/v1?label=AppVersion&message=2.4.8&color=success&logo=) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2021-06-16 + +* Add name-metadata for service "List" object + + +## 9.19.1 ![AppVersion: 2.4.8](https://img.shields.io/static/v1?label=AppVersion&message=2.4.8&color=success&logo=) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2021-05-13 + +* fix simple typo + +### Default value changes + +```diff +diff --git a/traefik/values.yaml b/traefik/values.yaml +index b30afac..9bf90ea 100644 +--- a/traefik/values.yaml ++++ b/traefik/values.yaml +@@ -363,7 +363,7 @@ rbac: + # If set to true, installs namespace-specific Role and RoleBinding and requires provider configuration be set to that same namespace + namespaced: false + +-# Enable to create a PodSecurityPolicy and assign it to the Service Account via RoleBindin or ClusterRoleBinding ++# Enable to create a PodSecurityPolicy and assign it to the Service Account via RoleBinding or ClusterRoleBinding + podSecurityPolicy: + enabled: false + +``` + +## 9.19.0 ![AppVersion: 2.4.8](https://img.shields.io/static/v1?label=AppVersion&message=2.4.8&color=success&logo=) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2021-04-29 + +* Fix IngressClass api version + +### Default value changes + +```diff +diff --git a/traefik/values.yaml b/traefik/values.yaml +index 0aa2d6b..b30afac 100644 +--- a/traefik/values.yaml ++++ b/traefik/values.yaml +@@ -63,6 +63,8 @@ ingressClass: + # true is not unit-testable yet, pending https://github.com/rancher/helm-unittest/pull/12 + enabled: false + isDefaultClass: false ++ # Use to force a networking.k8s.io API Version for certain CI/CD applications. E.g. "v1beta1" ++ fallbackApiVersion: + + # Activate Pilot integration + pilot: +``` + +## 9.18.3 ![AppVersion: 2.4.8](https://img.shields.io/static/v1?label=AppVersion&message=2.4.8&color=success&logo=) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2021-04-26 + +* Fix: ignore provider namespace args on disabled + + +## 9.18.2 ![AppVersion: 2.4.8](https://img.shields.io/static/v1?label=AppVersion&message=2.4.8&color=success&logo=) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2021-04-02 + +* Fix pilot dashboard deactivation + + +## 9.18.1 ![AppVersion: 2.4.8](https://img.shields.io/static/v1?label=AppVersion&message=2.4.8&color=success&logo=) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2021-03-29 + +* Do not disable Traefik Pilot in the dashboard by default + + +## 9.18.0 ![AppVersion: 2.4.8](https://img.shields.io/static/v1?label=AppVersion&message=2.4.8&color=success&logo=) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2021-03-24 + +* Add an option to toggle the pilot dashboard + +### Default value changes + +```diff +diff --git a/traefik/values.yaml b/traefik/values.yaml +index 017f771..0aa2d6b 100644 +--- a/traefik/values.yaml ++++ b/traefik/values.yaml +@@ -68,6 +68,8 @@ ingressClass: + pilot: + enabled: false + token: "" ++ # Toggle Pilot Dashboard ++ # dashboard: false + + # Enable experimental features + experimental: +``` + +## 9.17.6 ![AppVersion: 2.4.8](https://img.shields.io/static/v1?label=AppVersion&message=2.4.8&color=success&logo=) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2021-03-24 + +* Bump Traefik to 2.4.8 + + +## 9.17.5 ![AppVersion: 2.4.7](https://img.shields.io/static/v1?label=AppVersion&message=2.4.7&color=success&logo=) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2021-03-17 + +* feat(labelSelector): option matching Ingresses based on labelSelectors + +### Default value changes + +```diff +diff --git a/traefik/values.yaml b/traefik/values.yaml +index 868a985..017f771 100644 +--- a/traefik/values.yaml ++++ b/traefik/values.yaml +@@ -105,6 +105,7 @@ providers: + # - "default" + kubernetesIngress: + enabled: true ++ # labelSelector: environment=production,method=traefik + namespaces: [] + # - "default" + # IP used for Kubernetes Ingress endpoints +``` + +## 9.17.4 ![AppVersion: 2.4.7](https://img.shields.io/static/v1?label=AppVersion&message=2.4.7&color=success&logo=) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2021-03-17 + +* Add helm resource-policy annotation on PVC + + +## 9.17.3 ![AppVersion: 2.4.7](https://img.shields.io/static/v1?label=AppVersion&message=2.4.7&color=success&logo=) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2021-03-17 + +* Throw error with explicit latest tag + + +## 9.17.2 ![AppVersion: 2.4.7](https://img.shields.io/static/v1?label=AppVersion&message=2.4.7&color=success&logo=) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2021-03-10 + +* fix(keywords): removed by mistake + + +## 9.17.1 ![AppVersion: 2.4.7](https://img.shields.io/static/v1?label=AppVersion&message=2.4.7&color=success&logo=) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2021-03-10 + +* feat(healthchecksPort): Support for overriding the liveness/readiness probes port + +### Default value changes + +```diff +diff --git a/traefik/values.yaml b/traefik/values.yaml +index 56abb93..868a985 100644 +--- a/traefik/values.yaml ++++ b/traefik/values.yaml +@@ -120,6 +120,8 @@ providers: + # After the volume has been mounted, add the configs into traefik by using the `additionalArguments` list below, eg: + # additionalArguments: + # - "--providers.file.filename=/config/dynamic.toml" ++# - "--ping" ++# - "--ping.entrypoint=web" + volumes: [] + # - name: public-cert + # mountPath: "/certs" +@@ -225,6 +227,10 @@ ports: + # only. + # hostIP: 192.168.100.10 + ++ # Override the liveness/readiness port. This is useful to integrate traefik ++ # with an external Load Balancer that performs healthchecks. ++ # healthchecksPort: 9000 ++ + # Defines whether the port is exposed if service.type is LoadBalancer or + # NodePort. + # +``` + +## 9.16.2 ![AppVersion: 2.4.7](https://img.shields.io/static/v1?label=AppVersion&message=2.4.7&color=success&logo=) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2021-03-09 + +* Bump Traefik to 2.4.7 + + +## 9.16.1 ![AppVersion: 2.4.6](https://img.shields.io/static/v1?label=AppVersion&message=2.4.6&color=success&logo=) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2021-03-09 + +* Adding custom labels to deployment + +### Default value changes + +```diff +diff --git a/traefik/values.yaml b/traefik/values.yaml +index ba24be7..56abb93 100644 +--- a/traefik/values.yaml ++++ b/traefik/values.yaml +@@ -16,6 +16,8 @@ deployment: + replicas: 1 + # Additional deployment annotations (e.g. for jaeger-operator sidecar injection) + annotations: {} ++ # Additional deployment labels (e.g. for filtering deployment by custom labels) ++ labels: {} + # Additional pod annotations (e.g. for mesh injection or prometheus scraping) + podAnnotations: {} + # Additional Pod labels (e.g. for filtering Pod by custom labels) +``` + +## 9.15.2 ![AppVersion: 2.4.6](https://img.shields.io/static/v1?label=AppVersion&message=2.4.6&color=success&logo=) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2021-03-02 + +* Upgrade Traefik to 2.4.6 + + +## 9.15.1 ![AppVersion: 2.4.5](https://img.shields.io/static/v1?label=AppVersion&message=2.4.5&color=success&logo=) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2021-03-02 + +* Configurable PVC name + +### Default value changes + +```diff +diff --git a/traefik/values.yaml b/traefik/values.yaml +index 1e0e5a9..ba24be7 100644 +--- a/traefik/values.yaml ++++ b/traefik/values.yaml +@@ -327,6 +327,7 @@ autoscaling: + # It will persist TLS certificates. + persistence: + enabled: false ++ name: data + # existingClaim: "" + accessMode: ReadWriteOnce + size: 128Mi +``` + +## 9.14.4 ![AppVersion: 2.4.5](https://img.shields.io/static/v1?label=AppVersion&message=2.4.5&color=success&logo=) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2021-03-02 + +* fix typo + + +## 9.14.3 ![AppVersion: 2.4.5](https://img.shields.io/static/v1?label=AppVersion&message=2.4.5&color=success&logo=) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2021-02-19 + +* Bump Traefik to 2.4.5 + + +## 9.14.2 ![AppVersion: 2.4.2](https://img.shields.io/static/v1?label=AppVersion&message=2.4.2&color=success&logo=) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2021-02-03 + +* docs: indent nit for dsdsocket example + +### Default value changes + +```diff +diff --git a/traefik/values.yaml b/traefik/values.yaml +index 56485ad..1e0e5a9 100644 +--- a/traefik/values.yaml ++++ b/traefik/values.yaml +@@ -33,7 +33,7 @@ deployment: + additionalVolumes: [] + # - name: dsdsocket + # hostPath: +- # path: /var/run/statsd-exporter ++ # path: /var/run/statsd-exporter + # Additional initContainers (e.g. for setting file permission as shown below) + initContainers: [] + # The "volume-permissions" init container is required if you run into permission issues. +``` + +## 9.14.1 ![AppVersion: 2.4.2](https://img.shields.io/static/v1?label=AppVersion&message=2.4.2&color=success&logo=) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2021-02-03 + +* Update Traefik to 2.4.2 + + +## 9.14.0 ![AppVersion: 2.4.0](https://img.shields.io/static/v1?label=AppVersion&message=2.4.0&color=success&logo=) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2021-02-01 + +* Enable Kubernetes Gateway provider with an experimental flag + +### Default value changes + +```diff +diff --git a/traefik/values.yaml b/traefik/values.yaml +index 50cab94..56485ad 100644 +--- a/traefik/values.yaml ++++ b/traefik/values.yaml +@@ -71,6 +71,13 @@ pilot: + experimental: + plugins: + enabled: false ++ kubernetesGateway: ++ enabled: false ++ appLabelSelector: "traefik" ++ certificates: [] ++ # - group: "core" ++ # kind: "Secret" ++ # name: "mysecret" + + # Create an IngressRoute for the dashboard + ingressRoute: +``` + +## 9.13.0 ![AppVersion: 2.4.0](https://img.shields.io/static/v1?label=AppVersion&message=2.4.0&color=success&logo=) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2021-01-22 + +* Update Traefik to 2.4 and add resources + + +## 9.12.3 ![AppVersion: 2.3.6](https://img.shields.io/static/v1?label=AppVersion&message=2.3.6&color=success&logo=) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2020-12-31 + +* Revert API Upgrade + + +## 9.12.2 ![AppVersion: 2.3.6](https://img.shields.io/static/v1?label=AppVersion&message=2.3.6&color=success&logo=) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2020-12-31 + +* Bump Traefik to 2.3.6 + + +## 9.12.1 ![AppVersion: 2.3.3](https://img.shields.io/static/v1?label=AppVersion&message=2.3.3&color=success&logo=) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2020-12-30 + +* Resolve #303, change CRD version from v1beta1 to v1 + + +## 9.12.0 ![AppVersion: 2.3.3](https://img.shields.io/static/v1?label=AppVersion&message=2.3.3&color=success&logo=) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2020-12-30 + +* Implement support for DaemonSet + +### Default value changes + +```diff +diff --git a/traefik/values.yaml b/traefik/values.yaml +index 60a721d..50cab94 100644 +--- a/traefik/values.yaml ++++ b/traefik/values.yaml +@@ -10,7 +10,9 @@ image: + # + deployment: + enabled: true +- # Number of pods of the deployment ++ # Can be either Deployment or DaemonSet ++ kind: Deployment ++ # Number of pods of the deployment (only applies when kind == Deployment) + replicas: 1 + # Additional deployment annotations (e.g. for jaeger-operator sidecar injection) + annotations: {} +``` + +## 9.11.0 ![AppVersion: 2.3.3](https://img.shields.io/static/v1?label=AppVersion&message=2.3.3&color=success&logo=) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2020-11-20 + +* add podLabels - custom labels + +### Default value changes + +```diff +diff --git a/traefik/values.yaml b/traefik/values.yaml +index a187df7..60a721d 100644 +--- a/traefik/values.yaml ++++ b/traefik/values.yaml +@@ -16,6 +16,8 @@ deployment: + annotations: {} + # Additional pod annotations (e.g. for mesh injection or prometheus scraping) + podAnnotations: {} ++ # Additional Pod labels (e.g. for filtering Pod by custom labels) ++ podLabels: {} + # Additional containers (e.g. for metric offloading sidecars) + additionalContainers: [] + # https://docs.datadoghq.com/developers/dogstatsd/unix_socket/?tab=host +``` + +## 9.10.2 ![AppVersion: 2.3.3](https://img.shields.io/static/v1?label=AppVersion&message=2.3.3&color=success&logo=) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2020-11-20 + +* Bump Traefik to 2.3.3 + + +## 9.10.1 ![AppVersion: 2.3.1](https://img.shields.io/static/v1?label=AppVersion&message=2.3.1&color=success&logo=) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2020-11-04 + +* Specify IngressClass resource when checking for cluster capability + + +## 9.10.0 ![AppVersion: 2.3.1](https://img.shields.io/static/v1?label=AppVersion&message=2.3.1&color=success&logo=) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2020-11-03 + +* Add list of watched provider namespaces + +### Default value changes + +```diff +diff --git a/traefik/values.yaml b/traefik/values.yaml +index e6b85ca..a187df7 100644 +--- a/traefik/values.yaml ++++ b/traefik/values.yaml +@@ -88,8 +88,12 @@ rollingUpdate: + providers: + kubernetesCRD: + enabled: true ++ namespaces: [] ++ # - "default" + kubernetesIngress: + enabled: true ++ namespaces: [] ++ # - "default" + # IP used for Kubernetes Ingress endpoints + publishedService: + enabled: false +``` + +## 9.9.0 ![AppVersion: 2.3.1](https://img.shields.io/static/v1?label=AppVersion&message=2.3.1&color=success&logo=) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2020-11-03 + +* Add additionalVolumeMounts for traefik container + +### Default value changes + +```diff +diff --git a/traefik/values.yaml b/traefik/values.yaml +index 37dd151..e6b85ca 100644 +--- a/traefik/values.yaml ++++ b/traefik/values.yaml +@@ -111,6 +111,12 @@ volumes: [] + # mountPath: "/config" + # type: configMap + ++# Additional volumeMounts to add to the Traefik container ++additionalVolumeMounts: [] ++ # For instance when using a logshipper for access logs ++ # - name: traefik-logs ++ # mountPath: /var/log/traefik ++ + # Logs + # https://docs.traefik.io/observability/logs/ + logs: +``` + +## 9.8.4 ![AppVersion: 2.3.1](https://img.shields.io/static/v1?label=AppVersion&message=2.3.1&color=success&logo=) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2020-11-03 + +* fix: multiple ImagePullSecrets + + +## 9.8.3 ![AppVersion: 2.3.1](https://img.shields.io/static/v1?label=AppVersion&message=2.3.1&color=success&logo=) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2020-10-30 + +* Add imagePullSecrets + +### Default value changes + +```diff +diff --git a/traefik/values.yaml b/traefik/values.yaml +index 87f60c0..37dd151 100644 +--- a/traefik/values.yaml ++++ b/traefik/values.yaml +@@ -42,6 +42,9 @@ deployment: + # mountPath: /data + # Custom pod DNS policy. Apply if `hostNetwork: true` + # dnsPolicy: ClusterFirstWithHostNet ++ # Additional imagePullSecrets ++ imagePullSecrets: [] ++ # - name: myRegistryKeySecretName + + # Pod disruption budget + podDisruptionBudget: +``` + +## 9.8.2 ![AppVersion: 2.3.1](https://img.shields.io/static/v1?label=AppVersion&message=2.3.1&color=success&logo=) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2020-10-28 + +* Add chart repo to source + + +## 9.8.1 ![AppVersion: 2.3.1](https://img.shields.io/static/v1?label=AppVersion&message=2.3.1&color=success&logo=) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2020-10-23 + +* fix semver compare + +### Default value changes + +```diff +diff --git a/traefik/values.yaml b/traefik/values.yaml +index 4ca1f8f..87f60c0 100644 +--- a/traefik/values.yaml ++++ b/traefik/values.yaml +@@ -1,7 +1,8 @@ + # Default values for Traefik + image: + name: traefik +- tag: 2.3.1 ++ # defaults to appVersion ++ tag: "" + pullPolicy: IfNotPresent + + # +``` + +## 9.8.0 ![AppVersion: 2.3.1](https://img.shields.io/static/v1?label=AppVersion&message=2.3.1&color=success&logo=) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2020-10-20 + +* feat: Enable entrypoint tls config + TLSOption + +### Default value changes + +```diff +diff --git a/traefik/values.yaml b/traefik/values.yaml +index eee3622..4ca1f8f 100644 +--- a/traefik/values.yaml ++++ b/traefik/values.yaml +@@ -231,6 +231,31 @@ ports: + # The port protocol (TCP/UDP) + protocol: TCP + # nodePort: 32443 ++ # Set TLS at the entrypoint ++ # https://doc.traefik.io/traefik/routing/entrypoints/#tls ++ tls: ++ enabled: false ++ # this is the name of a TLSOption definition ++ options: "" ++ certResolver: "" ++ domains: [] ++ # - main: example.com ++ # sans: ++ # - foo.example.com ++ # - bar.example.com ++ ++# TLS Options are created as TLSOption CRDs ++# https://doc.traefik.io/traefik/https/tls/#tls-options ++# Example: ++# tlsOptions: ++# default: ++# sniStrict: true ++# preferServerCipherSuites: true ++# foobar: ++# curvePreferences: ++# - CurveP521 ++# - CurveP384 ++tlsOptions: {} + + # Options for the main traefik service, where the entrypoints traffic comes + # from. +``` + +## 9.7.0 ![AppVersion: 2.3.1](https://img.shields.io/static/v1?label=AppVersion&message=2.3.1&color=success&logo=) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2020-10-15 + +* Add a configuration option for an emptyDir as plugin storage + +### Default value changes + +```diff +diff --git a/traefik/values.yaml b/traefik/values.yaml +index b7153a1..eee3622 100644 +--- a/traefik/values.yaml ++++ b/traefik/values.yaml +@@ -54,10 +54,16 @@ ingressClass: + enabled: false + isDefaultClass: false + ++# Activate Pilot integration + pilot: + enabled: false + token: "" + ++# Enable experimental features ++experimental: ++ plugins: ++ enabled: false ++ + # Create an IngressRoute for the dashboard + ingressRoute: + dashboard: +``` + +## 9.6.0 ![AppVersion: 2.3.1](https://img.shields.io/static/v1?label=AppVersion&message=2.3.1&color=success&logo=) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2020-10-15 + +* Add additional volumes for init and additional containers + +### Default value changes + +```diff +diff --git a/traefik/values.yaml b/traefik/values.yaml +index 9bac45e..b7153a1 100644 +--- a/traefik/values.yaml ++++ b/traefik/values.yaml +@@ -17,6 +17,18 @@ deployment: + podAnnotations: {} + # Additional containers (e.g. for metric offloading sidecars) + additionalContainers: [] ++ # https://docs.datadoghq.com/developers/dogstatsd/unix_socket/?tab=host ++ # - name: socat-proxy ++ # image: alpine/socat:1.0.5 ++ # args: ["-s", "-u", "udp-recv:8125", "unix-sendto:/socket/socket"] ++ # volumeMounts: ++ # - name: dsdsocket ++ # mountPath: /socket ++ # Additional volumes available for use with initContainers and additionalContainers ++ additionalVolumes: [] ++ # - name: dsdsocket ++ # hostPath: ++ # path: /var/run/statsd-exporter + # Additional initContainers (e.g. for setting file permission as shown below) + initContainers: [] + # The "volume-permissions" init container is required if you run into permission issues. +``` + +## 9.5.2 ![AppVersion: 2.3.1](https://img.shields.io/static/v1?label=AppVersion&message=2.3.1&color=success&logo=) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2020-10-15 + +* Replace extensions with policy because of deprecation + + +## 9.5.1 ![AppVersion: 2.3.1](https://img.shields.io/static/v1?label=AppVersion&message=2.3.1&color=success&logo=) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2020-10-14 + +* Template custom volume name + +### Default value changes + +```diff +diff --git a/traefik/values.yaml b/traefik/values.yaml +index 5a8d8ea..9bac45e 100644 +--- a/traefik/values.yaml ++++ b/traefik/values.yaml +@@ -76,7 +76,7 @@ providers: + # pathOverride: "" + + # +-# Add volumes to the traefik pod. ++# Add volumes to the traefik pod. The volume name will be passed to tpl. + # This can be used to mount a cert pair or a configmap that holds a config.toml file. + # After the volume has been mounted, add the configs into traefik by using the `additionalArguments` list below, eg: + # additionalArguments: +@@ -85,7 +85,7 @@ volumes: [] + # - name: public-cert + # mountPath: "/certs" + # type: secret +-# - name: configs ++# - name: '{{ printf "%s-configs" .Release.Name }}' + # mountPath: "/config" + # type: configMap + +``` + +## 9.5.0 ![AppVersion: 2.3.1](https://img.shields.io/static/v1?label=AppVersion&message=2.3.1&color=success&logo=) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2020-10-02 + +* Create PodSecurityPolicy and RBAC when needed. + +### Default value changes + +```diff +diff --git a/traefik/values.yaml b/traefik/values.yaml +index 8c4d866..5a8d8ea 100644 +--- a/traefik/values.yaml ++++ b/traefik/values.yaml +@@ -281,6 +281,10 @@ rbac: + # If set to true, installs namespace-specific Role and RoleBinding and requires provider configuration be set to that same namespace + namespaced: false + ++# Enable to create a PodSecurityPolicy and assign it to the Service Account via RoleBindin or ClusterRoleBinding ++podSecurityPolicy: ++ enabled: false ++ + # The service account the pods will use to interact with the Kubernetes API + serviceAccount: + # If set, an existing service account is used +``` + +## 9.4.3 ![AppVersion: 2.3.1](https://img.shields.io/static/v1?label=AppVersion&message=2.3.1&color=success&logo=) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2020-10-02 + +* Update traefik to v2.3.1 + +### Default value changes + +```diff +diff --git a/traefik/values.yaml b/traefik/values.yaml +index 3df75a4..8c4d866 100644 +--- a/traefik/values.yaml ++++ b/traefik/values.yaml +@@ -1,7 +1,7 @@ + # Default values for Traefik + image: + name: traefik +- tag: 2.3.0 ++ tag: 2.3.1 + pullPolicy: IfNotPresent + + # +``` + +## 9.4.2 ![AppVersion: 2.3.0](https://img.shields.io/static/v1?label=AppVersion&message=2.3.0&color=success&logo=) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2020-10-02 + +* Add Artifact Hub repository metadata file + + +## 9.4.1 ![AppVersion: 2.3.0](https://img.shields.io/static/v1?label=AppVersion&message=2.3.0&color=success&logo=) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2020-10-01 + +* Fix broken chart icon url + + +## 9.4.0 ![AppVersion: 2.3.0](https://img.shields.io/static/v1?label=AppVersion&message=2.3.0&color=success&logo=) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2020-10-01 + +* Allow to specify custom labels on Service + +### Default value changes + +```diff +diff --git a/traefik/values.yaml b/traefik/values.yaml +index a6175ff..3df75a4 100644 +--- a/traefik/values.yaml ++++ b/traefik/values.yaml +@@ -221,6 +221,8 @@ service: + type: LoadBalancer + # Additional annotations (e.g. for cloud provider specific config) + annotations: {} ++ # Additional service labels (e.g. for filtering Service by custom labels) ++ labels: {} + # Additional entries here will be added to the service spec. Cannot contains + # type, selector or ports entries. + spec: {} +``` + +## 9.3.0 ![AppVersion: 2.3.0](https://img.shields.io/static/v1?label=AppVersion&message=2.3.0&color=success&logo=) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2020-09-24 + +* Release Traefik 2.3 + +### Default value changes + +```diff +diff --git a/traefik/values.yaml b/traefik/values.yaml +index fba955d..a6175ff 100644 +--- a/traefik/values.yaml ++++ b/traefik/values.yaml +@@ -1,7 +1,7 @@ + # Default values for Traefik + image: + name: traefik +- tag: 2.2.8 ++ tag: 2.3.0 + pullPolicy: IfNotPresent + + # +@@ -36,6 +36,16 @@ podDisruptionBudget: + # maxUnavailable: 1 + # minAvailable: 0 + ++# Use ingressClass. Ignored if Traefik version < 2.3 / kubernetes < 1.18.x ++ingressClass: ++ # true is not unit-testable yet, pending https://github.com/rancher/helm-unittest/pull/12 ++ enabled: false ++ isDefaultClass: false ++ ++pilot: ++ enabled: false ++ token: "" ++ + # Create an IngressRoute for the dashboard + ingressRoute: + dashboard: +``` + +## 9.2.1 ![AppVersion: 2.2.8](https://img.shields.io/static/v1?label=AppVersion&message=2.2.8&color=success&logo=) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2020-09-18 + +* Add new helm url + + +## 9.2.0 ![AppVersion: 2.2.8](https://img.shields.io/static/v1?label=AppVersion&message=2.2.8&color=success&logo=) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2020-09-16 + +* chore: move to new organization. + +### Default value changes + +```diff +diff --git a/traefik/values.yaml b/traefik/values.yaml +index 9f52c39..fba955d 100644 +--- a/traefik/values.yaml ++++ b/traefik/values.yaml +@@ -20,7 +20,7 @@ deployment: + # Additional initContainers (e.g. for setting file permission as shown below) + initContainers: [] + # The "volume-permissions" init container is required if you run into permission issues. +- # Related issue: https://github.com/containous/traefik/issues/6972 ++ # Related issue: https://github.com/traefik/traefik/issues/6972 + # - name: volume-permissions + # image: busybox:1.31.1 + # command: ["sh", "-c", "chmod -Rv 600 /data/*"] +``` + +## 9.1.1 ![AppVersion: 2.2.8](https://img.shields.io/static/v1?label=AppVersion&message=2.2.8&color=success&logo=) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2020-09-04 + +* Update reference to using kubectl proxy to kubectl port-forward + +### Default value changes + +```diff +diff --git a/traefik/values.yaml b/traefik/values.yaml +index 7b74a39..9f52c39 100644 +--- a/traefik/values.yaml ++++ b/traefik/values.yaml +@@ -175,7 +175,7 @@ ports: + # + # You SHOULD NOT expose the traefik port on production deployments. + # If you want to access it from outside of your cluster, +- # use `kubectl proxy` or create a secure ingress ++ # use `kubectl port-forward` or create a secure ingress + expose: false + # The exposed port for this service + exposedPort: 9000 +``` + +## 9.1.0 ![AppVersion: 2.2.8](https://img.shields.io/static/v1?label=AppVersion&message=2.2.8&color=success&logo=) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2020-08-24 + +* PublishedService option + +### Default value changes + +```diff +diff --git a/traefik/values.yaml b/traefik/values.yaml +index e161a14..7b74a39 100644 +--- a/traefik/values.yaml ++++ b/traefik/values.yaml +@@ -58,6 +58,12 @@ providers: + enabled: true + kubernetesIngress: + enabled: true ++ # IP used for Kubernetes Ingress endpoints ++ publishedService: ++ enabled: false ++ # Published Kubernetes Service to copy status from. Format: namespace/servicename ++ # By default this Traefik service ++ # pathOverride: "" + + # + # Add volumes to the traefik pod. +``` + +## 9.0.0 ![AppVersion: 2.2.8](https://img.shields.io/static/v1?label=AppVersion&message=2.2.8&color=success&logo=) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2020-08-21 + +* feat: Move Chart apiVersion: v2 + + +## 8.13.3 ![AppVersion: 2.2.8](https://img.shields.io/static/v1?label=AppVersion&message=2.2.8&color=success&logo=) ![Helm: v2](https://img.shields.io/static/v1?label=Helm&message=v2&color=inactive&logo=helm) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2020-08-21 + +* bug: Check for port config + + +## 8.13.2 ![AppVersion: 2.2.8](https://img.shields.io/static/v1?label=AppVersion&message=2.2.8&color=success&logo=) ![Helm: v2](https://img.shields.io/static/v1?label=Helm&message=v2&color=inactive&logo=helm) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2020-08-19 + +* Fix log level configuration + + +## 8.13.1 ![AppVersion: 2.2.8](https://img.shields.io/static/v1?label=AppVersion&message=2.2.8&color=success&logo=) ![Helm: v2](https://img.shields.io/static/v1?label=Helm&message=v2&color=inactive&logo=helm) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2020-08-18 + +* Dont redirect to websecure by default + +### Default value changes + +```diff +diff --git a/traefik/values.yaml b/traefik/values.yaml +index 67276f7..e161a14 100644 +--- a/traefik/values.yaml ++++ b/traefik/values.yaml +@@ -188,7 +188,7 @@ ports: + # Port Redirections + # Added in 2.2, you can make permanent redirects via entrypoints. + # https://docs.traefik.io/routing/entrypoints/#redirection +- redirectTo: websecure ++ # redirectTo: websecure + websecure: + port: 8443 + # hostPort: 8443 +``` + +## 8.13.0 ![AppVersion: 2.2.8](https://img.shields.io/static/v1?label=AppVersion&message=2.2.8&color=success&logo=) ![Helm: v2](https://img.shields.io/static/v1?label=Helm&message=v2&color=inactive&logo=helm) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2020-08-18 + +* Add logging, and http redirect config + +### Default value changes + +```diff +diff --git a/traefik/values.yaml b/traefik/values.yaml +index 6f79580..67276f7 100644 +--- a/traefik/values.yaml ++++ b/traefik/values.yaml +@@ -73,6 +73,48 @@ volumes: [] + # mountPath: "/config" + # type: configMap + ++# Logs ++# https://docs.traefik.io/observability/logs/ ++logs: ++ # Traefik logs concern everything that happens to Traefik itself (startup, configuration, events, shutdown, and so on). ++ general: ++ # By default, the logs use a text format (common), but you can ++ # also ask for the json format in the format option ++ # format: json ++ # By default, the level is set to ERROR. Alternative logging levels are DEBUG, PANIC, FATAL, ERROR, WARN, and INFO. ++ level: ERROR ++ access: ++ # To enable access logs ++ enabled: false ++ # By default, logs are written using the Common Log Format (CLF). ++ # To write logs in JSON, use json in the format option. ++ # If the given format is unsupported, the default (CLF) is used instead. ++ # format: json ++ # To write the logs in an asynchronous fashion, specify a bufferingSize option. ++ # This option represents the number of log lines Traefik will keep in memory before writing ++ # them to the selected output. In some cases, this option can greatly help performances. ++ # bufferingSize: 100 ++ # Filtering https://docs.traefik.io/observability/access-logs/#filtering ++ filters: {} ++ # statuscodes: "200,300-302" ++ # retryattempts: true ++ # minduration: 10ms ++ # Fields ++ # https://docs.traefik.io/observability/access-logs/#limiting-the-fieldsincluding-headers ++ fields: ++ general: ++ defaultmode: keep ++ names: {} ++ # Examples: ++ # ClientUsername: drop ++ headers: ++ defaultmode: drop ++ names: {} ++ # Examples: ++ # User-Agent: redact ++ # Authorization: drop ++ # Content-Type: keep ++ + globalArguments: + - "--global.checknewversion" + - "--global.sendanonymoususage" +@@ -143,6 +185,10 @@ ports: + # Use nodeport if set. This is useful if you have configured Traefik in a + # LoadBalancer + # nodePort: 32080 ++ # Port Redirections ++ # Added in 2.2, you can make permanent redirects via entrypoints. ++ # https://docs.traefik.io/routing/entrypoints/#redirection ++ redirectTo: websecure + websecure: + port: 8443 + # hostPort: 8443 +``` + +## 8.12.0 ![AppVersion: 2.2.8](https://img.shields.io/static/v1?label=AppVersion&message=2.2.8&color=success&logo=) ![Helm: v2](https://img.shields.io/static/v1?label=Helm&message=v2&color=inactive&logo=helm) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2020-08-14 + +* Add image pull policy + +### Default value changes + +```diff +diff --git a/traefik/values.yaml b/traefik/values.yaml +index 10b3949..6f79580 100644 +--- a/traefik/values.yaml ++++ b/traefik/values.yaml +@@ -2,6 +2,7 @@ + image: + name: traefik + tag: 2.2.8 ++ pullPolicy: IfNotPresent + + # + # Configure the deployment +``` + +## 8.11.0 ![AppVersion: 2.2.8](https://img.shields.io/static/v1?label=AppVersion&message=2.2.8&color=success&logo=) ![Helm: v2](https://img.shields.io/static/v1?label=Helm&message=v2&color=inactive&logo=helm) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2020-08-12 + +* Add dns policy option + +### Default value changes + +```diff +diff --git a/traefik/values.yaml b/traefik/values.yaml +index 80ddaaa..10b3949 100644 +--- a/traefik/values.yaml ++++ b/traefik/values.yaml +@@ -26,6 +26,8 @@ deployment: + # volumeMounts: + # - name: data + # mountPath: /data ++ # Custom pod DNS policy. Apply if `hostNetwork: true` ++ # dnsPolicy: ClusterFirstWithHostNet + + # Pod disruption budget + podDisruptionBudget: +``` + +## 8.10.0 ![AppVersion: 2.2.8](https://img.shields.io/static/v1?label=AppVersion&message=2.2.8&color=success&logo=) ![Helm: v2](https://img.shields.io/static/v1?label=Helm&message=v2&color=inactive&logo=helm) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2020-08-11 + +* Add hostIp to port configuration + +### Default value changes + +```diff +diff --git a/traefik/values.yaml b/traefik/values.yaml +index 936ab92..80ddaaa 100644 +--- a/traefik/values.yaml ++++ b/traefik/values.yaml +@@ -112,6 +112,12 @@ ports: + port: 9000 + # Use hostPort if set. + # hostPort: 9000 ++ # ++ # Use hostIP if set. If not set, Kubernetes will default to 0.0.0.0, which ++ # means it's listening on all your interfaces and all your IPs. You may want ++ # to set this value if you need traefik to listen on specific interface ++ # only. ++ # hostIP: 192.168.100.10 + + # Defines whether the port is exposed if service.type is LoadBalancer or + # NodePort. +``` + +## 8.9.2 ![AppVersion: 2.2.8](https://img.shields.io/static/v1?label=AppVersion&message=2.2.8&color=success&logo=) ![Helm: v2](https://img.shields.io/static/v1?label=Helm&message=v2&color=inactive&logo=helm) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2020-08-10 + +* Bump Traefik to 2.2.8 + +### Default value changes + +```diff +diff --git a/traefik/values.yaml b/traefik/values.yaml +index 42ee893..936ab92 100644 +--- a/traefik/values.yaml ++++ b/traefik/values.yaml +@@ -1,7 +1,7 @@ + # Default values for Traefik + image: + name: traefik +- tag: 2.2.5 ++ tag: 2.2.8 + + # + # Configure the deployment +``` + +## 8.9.1 ![AppVersion: 2.2.5](https://img.shields.io/static/v1?label=AppVersion&message=2.2.5&color=success&logo=) ![Helm: v2](https://img.shields.io/static/v1?label=Helm&message=v2&color=inactive&logo=helm) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2020-07-15 + +* Upgrade traefik version + +### Default value changes + +```diff +diff --git a/traefik/values.yaml b/traefik/values.yaml +index a7fb668..42ee893 100644 +--- a/traefik/values.yaml ++++ b/traefik/values.yaml +@@ -1,7 +1,7 @@ + # Default values for Traefik + image: + name: traefik +- tag: 2.2.1 ++ tag: 2.2.5 + + # + # Configure the deployment +``` + +## 8.9.0 ![AppVersion: 2.2.1](https://img.shields.io/static/v1?label=AppVersion&message=2.2.1&color=success&logo=) ![Helm: v2](https://img.shields.io/static/v1?label=Helm&message=v2&color=inactive&logo=helm) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2020-07-08 + +* run init container to set proper permissions on volume + +### Default value changes + +```diff +diff --git a/traefik/values.yaml b/traefik/values.yaml +index 62e3a77..a7fb668 100644 +--- a/traefik/values.yaml ++++ b/traefik/values.yaml +@@ -16,6 +16,16 @@ deployment: + podAnnotations: {} + # Additional containers (e.g. for metric offloading sidecars) + additionalContainers: [] ++ # Additional initContainers (e.g. for setting file permission as shown below) ++ initContainers: [] ++ # The "volume-permissions" init container is required if you run into permission issues. ++ # Related issue: https://github.com/containous/traefik/issues/6972 ++ # - name: volume-permissions ++ # image: busybox:1.31.1 ++ # command: ["sh", "-c", "chmod -Rv 600 /data/*"] ++ # volumeMounts: ++ # - name: data ++ # mountPath: /data + + # Pod disruption budget + podDisruptionBudget: +``` + +## 8.8.1 ![AppVersion: 2.2.1](https://img.shields.io/static/v1?label=AppVersion&message=2.2.1&color=success&logo=) ![Helm: v2](https://img.shields.io/static/v1?label=Helm&message=v2&color=inactive&logo=helm) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2020-07-02 + +* Additional container fix + +### Default value changes + +```diff +diff --git a/traefik/values.yaml b/traefik/values.yaml +index 85df29c..62e3a77 100644 +--- a/traefik/values.yaml ++++ b/traefik/values.yaml +@@ -15,7 +15,7 @@ deployment: + # Additional pod annotations (e.g. for mesh injection or prometheus scraping) + podAnnotations: {} + # Additional containers (e.g. for metric offloading sidecars) +- additionalContainers: {} ++ additionalContainers: [] + + # Pod disruption budget + podDisruptionBudget: +``` + +## 8.8.0 ![AppVersion: 2.2.1](https://img.shields.io/static/v1?label=AppVersion&message=2.2.1&color=success&logo=) ![Helm: v2](https://img.shields.io/static/v1?label=Helm&message=v2&color=inactive&logo=helm) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2020-07-01 + +* added additionalContainers option to chart + +### Default value changes + +```diff +diff --git a/traefik/values.yaml b/traefik/values.yaml +index 6a9dfd8..85df29c 100644 +--- a/traefik/values.yaml ++++ b/traefik/values.yaml +@@ -14,6 +14,8 @@ deployment: + annotations: {} + # Additional pod annotations (e.g. for mesh injection or prometheus scraping) + podAnnotations: {} ++ # Additional containers (e.g. for metric offloading sidecars) ++ additionalContainers: {} + + # Pod disruption budget + podDisruptionBudget: +``` + +## 8.7.2 ![AppVersion: 2.2.1](https://img.shields.io/static/v1?label=AppVersion&message=2.2.1&color=success&logo=) ![Helm: v2](https://img.shields.io/static/v1?label=Helm&message=v2&color=inactive&logo=helm) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2020-06-30 + +* Update image + + +## 8.7.1 ![AppVersion: 2.2.1](https://img.shields.io/static/v1?label=AppVersion&message=2.2.1&color=success&logo=) ![Helm: v2](https://img.shields.io/static/v1?label=Helm&message=v2&color=inactive&logo=helm) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2020-06-26 + +* Update values.yaml + +### Default value changes + +```diff +diff --git a/traefik/values.yaml b/traefik/values.yaml +index 05f9eab..6a9dfd8 100644 +--- a/traefik/values.yaml ++++ b/traefik/values.yaml +@@ -196,7 +196,7 @@ rbac: + # If set to true, installs namespace-specific Role and RoleBinding and requires provider configuration be set to that same namespace + namespaced: false + +-# The service account the pods will use to interact with the Kubernates API ++# The service account the pods will use to interact with the Kubernetes API + serviceAccount: + # If set, an existing service account is used + # If not set, a service account is created automatically using the fullname template +``` + +## 8.7.0 ![AppVersion: 2.2.1](https://img.shields.io/static/v1?label=AppVersion&message=2.2.1&color=success&logo=) ![Helm: v2](https://img.shields.io/static/v1?label=Helm&message=v2&color=inactive&logo=helm) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2020-06-23 + +* Add option to disable providers + +### Default value changes + +```diff +diff --git a/traefik/values.yaml b/traefik/values.yaml +index 102ae00..05f9eab 100644 +--- a/traefik/values.yaml ++++ b/traefik/values.yaml +@@ -34,6 +34,16 @@ rollingUpdate: + maxUnavailable: 1 + maxSurge: 1 + ++ ++# ++# Configure providers ++# ++providers: ++ kubernetesCRD: ++ enabled: true ++ kubernetesIngress: ++ enabled: true ++ + # + # Add volumes to the traefik pod. + # This can be used to mount a cert pair or a configmap that holds a config.toml file. +``` + +## 8.6.1 ![AppVersion: 2.2.1](https://img.shields.io/static/v1?label=AppVersion&message=2.2.1&color=success&logo=) ![Helm: v2](https://img.shields.io/static/v1?label=Helm&message=v2&color=inactive&logo=helm) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2020-06-18 + +* Fix read-only /tmp + + +## 8.6.0 ![AppVersion: 2.2.1](https://img.shields.io/static/v1?label=AppVersion&message=2.2.1&color=success&logo=) ![Helm: v2](https://img.shields.io/static/v1?label=Helm&message=v2&color=inactive&logo=helm) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2020-06-17 + +* Add existing PVC support(#158) + +### Default value changes + +```diff +diff --git a/traefik/values.yaml b/traefik/values.yaml +index b2f4fc3..102ae00 100644 +--- a/traefik/values.yaml ++++ b/traefik/values.yaml +@@ -164,6 +164,7 @@ autoscaling: + # It will persist TLS certificates. + persistence: + enabled: false ++# existingClaim: "" + accessMode: ReadWriteOnce + size: 128Mi + # storageClass: "" +``` + +## 8.5.0 ![AppVersion: 2.2.1](https://img.shields.io/static/v1?label=AppVersion&message=2.2.1&color=success&logo=) ![Helm: v2](https://img.shields.io/static/v1?label=Helm&message=v2&color=inactive&logo=helm) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2020-06-16 + +* UDP support + +### Default value changes + +```diff +diff --git a/traefik/values.yaml b/traefik/values.yaml +index 9a9b668..b2f4fc3 100644 +--- a/traefik/values.yaml ++++ b/traefik/values.yaml +@@ -100,11 +100,15 @@ ports: + expose: false + # The exposed port for this service + exposedPort: 9000 ++ # The port protocol (TCP/UDP) ++ protocol: TCP + web: + port: 8000 + # hostPort: 8000 + expose: true + exposedPort: 80 ++ # The port protocol (TCP/UDP) ++ protocol: TCP + # Use nodeport if set. This is useful if you have configured Traefik in a + # LoadBalancer + # nodePort: 32080 +@@ -113,6 +117,8 @@ ports: + # hostPort: 8443 + expose: true + exposedPort: 443 ++ # The port protocol (TCP/UDP) ++ protocol: TCP + # nodePort: 32443 + + # Options for the main traefik service, where the entrypoints traffic comes +``` + +## 8.4.1 ![AppVersion: 2.2.1](https://img.shields.io/static/v1?label=AppVersion&message=2.2.1&color=success&logo=) ![Helm: v2](https://img.shields.io/static/v1?label=Helm&message=v2&color=inactive&logo=helm) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2020-06-10 + +* Fix PDB with minAvailable set + +### Default value changes + +```diff +diff --git a/traefik/values.yaml b/traefik/values.yaml +index e812b98..9a9b668 100644 +--- a/traefik/values.yaml ++++ b/traefik/values.yaml +@@ -18,7 +18,7 @@ deployment: + # Pod disruption budget + podDisruptionBudget: + enabled: false +- maxUnavailable: 1 ++ # maxUnavailable: 1 + # minAvailable: 0 + + # Create an IngressRoute for the dashboard +``` + +## 8.4.0 ![AppVersion: 2.2.1](https://img.shields.io/static/v1?label=AppVersion&message=2.2.1&color=success&logo=) ![Helm: v2](https://img.shields.io/static/v1?label=Helm&message=v2&color=inactive&logo=helm) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2020-06-09 + +* Add pod disruption budget (#192) + +### Default value changes + +```diff +diff --git a/traefik/values.yaml b/traefik/values.yaml +index 5f44e5c..e812b98 100644 +--- a/traefik/values.yaml ++++ b/traefik/values.yaml +@@ -15,6 +15,12 @@ deployment: + # Additional pod annotations (e.g. for mesh injection or prometheus scraping) + podAnnotations: {} + ++# Pod disruption budget ++podDisruptionBudget: ++ enabled: false ++ maxUnavailable: 1 ++ # minAvailable: 0 ++ + # Create an IngressRoute for the dashboard + ingressRoute: + dashboard: +``` + +## 8.3.0 ![AppVersion: 2.2.1](https://img.shields.io/static/v1?label=AppVersion&message=2.2.1&color=success&logo=) ![Helm: v2](https://img.shields.io/static/v1?label=Helm&message=v2&color=inactive&logo=helm) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2020-06-08 + +* Add option to disable RBAC and ServiceAccount + +### Default value changes + +```diff +diff --git a/traefik/values.yaml b/traefik/values.yaml +index 96bba18..5f44e5c 100644 +--- a/traefik/values.yaml ++++ b/traefik/values.yaml +@@ -165,6 +165,20 @@ persistence: + # affinity is left as default. + hostNetwork: false + ++# Whether Role Based Access Control objects like roles and rolebindings should be created ++rbac: ++ enabled: true ++ ++ # If set to false, installs ClusterRole and ClusterRoleBinding so Traefik can be used across namespaces. ++ # If set to true, installs namespace-specific Role and RoleBinding and requires provider configuration be set to that same namespace ++ namespaced: false ++ ++# The service account the pods will use to interact with the Kubernates API ++serviceAccount: ++ # If set, an existing service account is used ++ # If not set, a service account is created automatically using the fullname template ++ name: "" ++ + # Additional serviceAccount annotations (e.g. for oidc authentication) + serviceAccountAnnotations: {} + +``` + +## 8.2.1 ![AppVersion: 2.2.1](https://img.shields.io/static/v1?label=AppVersion&message=2.2.1&color=success&logo=) ![Helm: v2](https://img.shields.io/static/v1?label=Helm&message=v2&color=inactive&logo=helm) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2020-05-25 + +* Remove suggested providers.kubernetesingress value + +### Default value changes + +```diff +diff --git a/traefik/values.yaml b/traefik/values.yaml +index e35bdf9..96bba18 100644 +--- a/traefik/values.yaml ++++ b/traefik/values.yaml +@@ -50,9 +50,9 @@ globalArguments: + # Configure Traefik static configuration + # Additional arguments to be passed at Traefik's binary + # All available options available on https://docs.traefik.io/reference/static-configuration/cli/ +-## Use curly braces to pass values: `helm install --set="additionalArguments={--providers.kubernetesingress,--log.level=DEBUG}"` ++## Use curly braces to pass values: `helm install --set="additionalArguments={--providers.kubernetesingress.ingressclass=traefik-internal,--log.level=DEBUG}"` + additionalArguments: [] +-# - "--providers.kubernetesingress" ++# - "--providers.kubernetesingress.ingressclass=traefik-internal" + # - "--log.level=DEBUG" + + # Environment variables to be passed to Traefik's binary +``` + +## 8.2.0 ![AppVersion: 2.2.1](https://img.shields.io/static/v1?label=AppVersion&message=2.2.1&color=success&logo=) ![Helm: v2](https://img.shields.io/static/v1?label=Helm&message=v2&color=inactive&logo=helm) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2020-05-18 + +* Add kubernetes ingress by default + + +## 8.1.5 ![AppVersion: 2.2.1](https://img.shields.io/static/v1?label=AppVersion&message=2.2.1&color=success&logo=) ![Helm: v2](https://img.shields.io/static/v1?label=Helm&message=v2&color=inactive&logo=helm) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2020-05-18 + +* Fix example log params in values.yml + +### Default value changes + +```diff +diff --git a/traefik/values.yaml b/traefik/values.yaml +index abe2334..e35bdf9 100644 +--- a/traefik/values.yaml ++++ b/traefik/values.yaml +@@ -50,10 +50,10 @@ globalArguments: + # Configure Traefik static configuration + # Additional arguments to be passed at Traefik's binary + # All available options available on https://docs.traefik.io/reference/static-configuration/cli/ +-## Use curly braces to pass values: `helm install --set="additionalArguments={--providers.kubernetesingress,--logs.level=DEBUG}"` ++## Use curly braces to pass values: `helm install --set="additionalArguments={--providers.kubernetesingress,--log.level=DEBUG}"` + additionalArguments: [] + # - "--providers.kubernetesingress" +-# - "--logs.level=DEBUG" ++# - "--log.level=DEBUG" + + # Environment variables to be passed to Traefik's binary + env: [] +``` + +## 8.1.4 ![AppVersion: 2.2.1](https://img.shields.io/static/v1?label=AppVersion&message=2.2.1&color=success&logo=) ![Helm: v2](https://img.shields.io/static/v1?label=Helm&message=v2&color=inactive&logo=helm) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2020-04-30 + +* Update Traefik to v2.2.1 + +### Default value changes + +```diff +diff --git a/traefik/values.yaml b/traefik/values.yaml +index 57cc7e1..abe2334 100644 +--- a/traefik/values.yaml ++++ b/traefik/values.yaml +@@ -1,7 +1,7 @@ + # Default values for Traefik + image: + name: traefik +- tag: 2.2.0 ++ tag: 2.2.1 + + # + # Configure the deployment +``` + +## 8.1.3 ![AppVersion: 2.2.0](https://img.shields.io/static/v1?label=AppVersion&message=2.2.0&color=success&logo=) ![Helm: v2](https://img.shields.io/static/v1?label=Helm&message=v2&color=inactive&logo=helm) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2020-04-29 + +* Clarify additionnal arguments log + +### Default value changes + +```diff +diff --git a/traefik/values.yaml b/traefik/values.yaml +index d639f72..57cc7e1 100644 +--- a/traefik/values.yaml ++++ b/traefik/values.yaml +@@ -50,9 +50,10 @@ globalArguments: + # Configure Traefik static configuration + # Additional arguments to be passed at Traefik's binary + # All available options available on https://docs.traefik.io/reference/static-configuration/cli/ +-## Use curly braces to pass values: `helm install --set="additionalArguments={--providers.kubernetesingress,--global.checknewversion=true}"` ++## Use curly braces to pass values: `helm install --set="additionalArguments={--providers.kubernetesingress,--logs.level=DEBUG}"` + additionalArguments: [] + # - "--providers.kubernetesingress" ++# - "--logs.level=DEBUG" + + # Environment variables to be passed to Traefik's binary + env: [] +``` + +## 8.1.2 ![AppVersion: 2.2.0](https://img.shields.io/static/v1?label=AppVersion&message=2.2.0&color=success&logo=) ![Helm: v2](https://img.shields.io/static/v1?label=Helm&message=v2&color=inactive&logo=helm) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2020-04-23 + +* Remove invalid flags. (#161) + +### Default value changes + +```diff +diff --git a/traefik/values.yaml b/traefik/values.yaml +index 0e7aaef..d639f72 100644 +--- a/traefik/values.yaml ++++ b/traefik/values.yaml +@@ -34,8 +34,6 @@ rollingUpdate: + # After the volume has been mounted, add the configs into traefik by using the `additionalArguments` list below, eg: + # additionalArguments: + # - "--providers.file.filename=/config/dynamic.toml" +-# - "--tls.certificates.certFile=/certs/tls.crt" +-# - "--tls.certificates.keyFile=/certs/tls.key" + volumes: [] + # - name: public-cert + # mountPath: "/certs" +``` + +## 8.1.1 ![AppVersion: 2.2.0](https://img.shields.io/static/v1?label=AppVersion&message=2.2.0&color=success&logo=) ![Helm: v2](https://img.shields.io/static/v1?label=Helm&message=v2&color=inactive&logo=helm) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2020-04-23 + +* clarify project philosophy and guidelines + + +## 8.1.0 ![AppVersion: 2.2.0](https://img.shields.io/static/v1?label=AppVersion&message=2.2.0&color=success&logo=) ![Helm: v2](https://img.shields.io/static/v1?label=Helm&message=v2&color=inactive&logo=helm) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2020-04-22 + +* Add priorityClassName & securityContext + +### Default value changes + +```diff +diff --git a/traefik/values.yaml b/traefik/values.yaml +index d55a40a..0e7aaef 100644 +--- a/traefik/values.yaml ++++ b/traefik/values.yaml +@@ -191,3 +191,20 @@ affinity: {} + # topologyKey: failure-domain.beta.kubernetes.io/zone + nodeSelector: {} + tolerations: [] ++ ++# Pods can have priority. ++# Priority indicates the importance of a Pod relative to other Pods. ++priorityClassName: "" ++ ++# Set the container security context ++# To run the container with ports below 1024 this will need to be adjust to run as root ++securityContext: ++ capabilities: ++ drop: [ALL] ++ readOnlyRootFilesystem: true ++ runAsGroup: 65532 ++ runAsNonRoot: true ++ runAsUser: 65532 ++ ++podSecurityContext: ++ fsGroup: 65532 +``` + +## 8.0.4 ![AppVersion: 2.2.0](https://img.shields.io/static/v1?label=AppVersion&message=2.2.0&color=success&logo=) ![Helm: v2](https://img.shields.io/static/v1?label=Helm&message=v2&color=inactive&logo=helm) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2020-04-20 + +* Possibility to bind environment variables via envFrom + +### Default value changes + +```diff +diff --git a/traefik/values.yaml b/traefik/values.yaml +index 7f8092e..d55a40a 100644 +--- a/traefik/values.yaml ++++ b/traefik/values.yaml +@@ -71,6 +71,12 @@ env: [] + # name: secret-name + # key: secret-key + ++envFrom: [] ++# - configMapRef: ++# name: config-map-name ++# - secretRef: ++# name: secret-name ++ + # Configure ports + ports: + # The name of this one can't be changed as it is used for the readiness and +``` + +## 8.0.3 ![AppVersion: 2.2.0](https://img.shields.io/static/v1?label=AppVersion&message=2.2.0&color=success&logo=) ![Helm: v2](https://img.shields.io/static/v1?label=Helm&message=v2&color=inactive&logo=helm) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2020-04-15 + +* Add support for data volume subPath. (#147) + +### Default value changes + +```diff +diff --git a/traefik/values.yaml b/traefik/values.yaml +index 152339b..7f8092e 100644 +--- a/traefik/values.yaml ++++ b/traefik/values.yaml +@@ -152,6 +152,7 @@ persistence: + # storageClass: "" + path: /data + annotations: {} ++ # subPath: "" # only mount a subpath of the Volume into the pod + + # If hostNetwork is true, runs traefik in the host network namespace + # To prevent unschedulabel pods due to port collisions, if hostNetwork=true +``` + +## 8.0.2 ![AppVersion: 2.2.0](https://img.shields.io/static/v1?label=AppVersion&message=2.2.0&color=success&logo=) ![Helm: v2](https://img.shields.io/static/v1?label=Helm&message=v2&color=inactive&logo=helm) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2020-04-10 + +* Ability to add custom labels to dashboard's IngressRoute + +### Default value changes + +```diff +diff --git a/traefik/values.yaml b/traefik/values.yaml +index 5d294b7..152339b 100644 +--- a/traefik/values.yaml ++++ b/traefik/values.yaml +@@ -21,6 +21,8 @@ ingressRoute: + enabled: true + # Additional ingressRoute annotations (e.g. for kubernetes.io/ingress.class) + annotations: {} ++ # Additional ingressRoute labels (e.g. for filtering IngressRoute by custom labels) ++ labels: {} + + rollingUpdate: + maxUnavailable: 1 +``` + +## 8.0.1 ![AppVersion: 2.2.0](https://img.shields.io/static/v1?label=AppVersion&message=2.2.0&color=success&logo=) ![Helm: v2](https://img.shields.io/static/v1?label=Helm&message=v2&color=inactive&logo=helm) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2020-04-10 + +* rbac does not need "pods" per documentation + + +## 8.0.0 ![AppVersion: 2.2.0](https://img.shields.io/static/v1?label=AppVersion&message=2.2.0&color=success&logo=) ![Helm: v2](https://img.shields.io/static/v1?label=Helm&message=v2&color=inactive&logo=helm) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2020-04-07 + +* follow helm best practices + +### Default value changes + +```diff +diff --git a/traefik/values.yaml b/traefik/values.yaml +index e61a9fd..5d294b7 100644 +--- a/traefik/values.yaml ++++ b/traefik/values.yaml +@@ -10,7 +10,7 @@ deployment: + enabled: true + # Number of pods of the deployment + replicas: 1 +- # Addtional deployment annotations (e.g. for jaeger-operator sidecar injection) ++ # Additional deployment annotations (e.g. for jaeger-operator sidecar injection) + annotations: {} + # Additional pod annotations (e.g. for mesh injection or prometheus scraping) + podAnnotations: {} +@@ -19,7 +19,7 @@ deployment: + ingressRoute: + dashboard: + enabled: true +- # Addtional ingressRoute annotations (e.g. for kubernetes.io/ingress.class) ++ # Additional ingressRoute annotations (e.g. for kubernetes.io/ingress.class) + annotations: {} + + rollingUpdate: +``` + +## 7.2.1 ![AppVersion: 2.2.0](https://img.shields.io/static/v1?label=AppVersion&message=2.2.0&color=success&logo=) ![Helm: v2](https://img.shields.io/static/v1?label=Helm&message=v2&color=inactive&logo=helm) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2020-04-07 + +* add annotations to ingressRoute + +### Default value changes + +```diff +diff --git a/traefik/values.yaml b/traefik/values.yaml +index 15d1c25..e61a9fd 100644 +--- a/traefik/values.yaml ++++ b/traefik/values.yaml +@@ -19,6 +19,8 @@ deployment: + ingressRoute: + dashboard: + enabled: true ++ # Addtional ingressRoute annotations (e.g. for kubernetes.io/ingress.class) ++ annotations: {} + + rollingUpdate: + maxUnavailable: 1 +``` + +## 7.2.0 ![AppVersion: 2.2.0](https://img.shields.io/static/v1?label=AppVersion&message=2.2.0&color=success&logo=) ![Helm: v2](https://img.shields.io/static/v1?label=Helm&message=v2&color=inactive&logo=helm) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2020-04-03 + +* Add support for helm 2 + + +## 7.1.0 ![AppVersion: 2.2.0](https://img.shields.io/static/v1?label=AppVersion&message=2.2.0&color=success&logo=) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2020-03-31 + +* Add support for externalIPs + +### Default value changes + +```diff +diff --git a/traefik/values.yaml b/traefik/values.yaml +index 6d6d13f..15d1c25 100644 +--- a/traefik/values.yaml ++++ b/traefik/values.yaml +@@ -116,6 +116,8 @@ service: + loadBalancerSourceRanges: [] + # - 192.168.0.1/32 + # - 172.16.0.0/16 ++ externalIPs: [] ++ # - 1.2.3.4 + + ## Create HorizontalPodAutoscaler object. + ## +``` + +## 7.0.0 ![AppVersion: 2.2.0](https://img.shields.io/static/v1?label=AppVersion&message=2.2.0&color=success&logo=) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2020-03-27 + +* Remove secretsEnv value key + +### Default value changes + +```diff +diff --git a/traefik/values.yaml b/traefik/values.yaml +index 1ac720d..6d6d13f 100644 +--- a/traefik/values.yaml ++++ b/traefik/values.yaml +@@ -52,18 +52,20 @@ globalArguments: + additionalArguments: [] + # - "--providers.kubernetesingress" + +-# Secret to be set as environment variables to be passed to Traefik's binary +-secretEnv: [] +- # - name: SOME_VAR +- # secretName: my-secret-name +- # secretKey: my-secret-key +- + # Environment variables to be passed to Traefik's binary + env: [] +- # - name: SOME_VAR +- # value: some-var-value +- # - name: SOME_OTHER_VAR +- # value: some-other-var-value ++# - name: SOME_VAR ++# value: some-var-value ++# - name: SOME_VAR_FROM_CONFIG_MAP ++# valueFrom: ++# configMapRef: ++# name: configmap-name ++# key: config-key ++# - name: SOME_SECRET ++# valueFrom: ++# secretKeyRef: ++# name: secret-name ++# key: secret-key + + # Configure ports + ports: +``` + +## 6.4.0 ![AppVersion: 2.2.0](https://img.shields.io/static/v1?label=AppVersion&message=2.2.0&color=success&logo=) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2020-03-27 + +* Add ability to set serviceAccount annotations + +### Default value changes + +```diff +diff --git a/traefik/values.yaml b/traefik/values.yaml +index 85abe42..1ac720d 100644 +--- a/traefik/values.yaml ++++ b/traefik/values.yaml +@@ -151,6 +151,9 @@ persistence: + # affinity is left as default. + hostNetwork: false + ++# Additional serviceAccount annotations (e.g. for oidc authentication) ++serviceAccountAnnotations: {} ++ + resources: {} + # requests: + # cpu: "100m" +``` + +## 6.3.0 ![AppVersion: 2.2.0](https://img.shields.io/static/v1?label=AppVersion&message=2.2.0&color=success&logo=) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2020-03-27 + +* hpa + +### Default value changes + +```diff +diff --git a/traefik/values.yaml b/traefik/values.yaml +index 2f5d132..85abe42 100644 +--- a/traefik/values.yaml ++++ b/traefik/values.yaml +@@ -115,6 +115,22 @@ service: + # - 192.168.0.1/32 + # - 172.16.0.0/16 + ++## Create HorizontalPodAutoscaler object. ++## ++autoscaling: ++ enabled: false ++# minReplicas: 1 ++# maxReplicas: 10 ++# metrics: ++# - type: Resource ++# resource: ++# name: cpu ++# targetAverageUtilization: 60 ++# - type: Resource ++# resource: ++# name: memory ++# targetAverageUtilization: 60 ++ + # Enable persistence using Persistent Volume Claims + # ref: http://kubernetes.io/docs/user-guide/persistent-volumes/ + # After the pvc has been mounted, add the configs into traefik by using the `additionalArguments` list below, eg: +``` + +## 6.2.0 ![AppVersion: 2.2.0](https://img.shields.io/static/v1?label=AppVersion&message=2.2.0&color=success&logo=) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2020-03-26 + +* Update to v2.2 (#96) + +### Default value changes + +```diff +diff --git a/traefik/values.yaml b/traefik/values.yaml +index ebd2fde..2f5d132 100644 +--- a/traefik/values.yaml ++++ b/traefik/values.yaml +@@ -1,7 +1,7 @@ + # Default values for Traefik + image: + name: traefik +- tag: 2.1.8 ++ tag: 2.2.0 + + # + # Configure the deployment +``` + +## 6.1.2 ![AppVersion: 2.1.8](https://img.shields.io/static/v1?label=AppVersion&message=2.1.8&color=success&logo=) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2020-03-20 + +* Upgrade traefik version + +### Default value changes + +```diff +diff --git a/traefik/values.yaml b/traefik/values.yaml +index 65c7665..ebd2fde 100644 +--- a/traefik/values.yaml ++++ b/traefik/values.yaml +@@ -1,7 +1,7 @@ + # Default values for Traefik + image: + name: traefik +- tag: 2.1.4 ++ tag: 2.1.8 + + # + # Configure the deployment +``` + +## 6.1.1 ![AppVersion: 2.1.4](https://img.shields.io/static/v1?label=AppVersion&message=2.1.4&color=success&logo=) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2020-03-20 + +* Upgrade traefik version + +### Default value changes + +```diff +diff --git a/traefik/values.yaml b/traefik/values.yaml +index 89c7ac1..65c7665 100644 +--- a/traefik/values.yaml ++++ b/traefik/values.yaml +@@ -1,7 +1,7 @@ + # Default values for Traefik + image: + name: traefik +- tag: 2.1.3 ++ tag: 2.1.4 + + # + # Configure the deployment +``` + +## 6.1.0 ![AppVersion: 2.1.3](https://img.shields.io/static/v1?label=AppVersion&message=2.1.3&color=success&logo=) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2020-03-20 + +* Add ability to add annotations to deployment + +### Default value changes + +```diff +diff --git a/traefik/values.yaml b/traefik/values.yaml +index 8d66111..89c7ac1 100644 +--- a/traefik/values.yaml ++++ b/traefik/values.yaml +@@ -10,6 +10,8 @@ deployment: + enabled: true + # Number of pods of the deployment + replicas: 1 ++ # Addtional deployment annotations (e.g. for jaeger-operator sidecar injection) ++ annotations: {} + # Additional pod annotations (e.g. for mesh injection or prometheus scraping) + podAnnotations: {} + +``` + +## 6.0.2 ![AppVersion: 2.1.3](https://img.shields.io/static/v1?label=AppVersion&message=2.1.3&color=success&logo=) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2020-03-16 + +* Correct storage class key name + + +## 6.0.1 ![AppVersion: 2.1.3](https://img.shields.io/static/v1?label=AppVersion&message=2.1.3&color=success&logo=) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2020-03-16 + +* Change default values of arrays from objects to actual arrays + +### Default value changes + +```diff +diff --git a/traefik/values.yaml b/traefik/values.yaml +index 490b2b6..8d66111 100644 +--- a/traefik/values.yaml ++++ b/traefik/values.yaml +@@ -51,13 +51,13 @@ additionalArguments: [] + # - "--providers.kubernetesingress" + + # Secret to be set as environment variables to be passed to Traefik's binary +-secretEnv: {} ++secretEnv: [] + # - name: SOME_VAR + # secretName: my-secret-name + # secretKey: my-secret-key + + # Environment variables to be passed to Traefik's binary +-env: {} ++env: [] + # - name: SOME_VAR + # value: some-var-value + # - name: SOME_OTHER_VAR +@@ -109,7 +109,7 @@ service: + # externalTrafficPolicy: Cluster + # loadBalancerIP: "1.2.3.4" + # clusterIP: "2.3.4.5" +- loadBalancerSourceRanges: {} ++ loadBalancerSourceRanges: [] + # - 192.168.0.1/32 + # - 172.16.0.0/16 + +``` + +## 6.0.0 ![AppVersion: 2.1.3](https://img.shields.io/static/v1?label=AppVersion&message=2.1.3&color=success&logo=) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2020-03-15 + +* Cleanup + +### Default value changes + +```diff +diff --git a/traefik/values.yaml b/traefik/values.yaml +index 7aebefe..490b2b6 100644 +--- a/traefik/values.yaml ++++ b/traefik/values.yaml +@@ -18,15 +18,10 @@ ingressRoute: + dashboard: + enabled: true + +-additional: +- checkNewVersion: true +- sendAnonymousUsage: true +- + rollingUpdate: + maxUnavailable: 1 + maxSurge: 1 + +- + # + # Add volumes to the traefik pod. + # This can be used to mount a cert pair or a configmap that holds a config.toml file. +@@ -43,9 +38,14 @@ volumes: [] + # mountPath: "/config" + # type: configMap + ++globalArguments: ++ - "--global.checknewversion" ++ - "--global.sendanonymoususage" ++ + # +-# Configure Traefik entry points ++# Configure Traefik static configuration + # Additional arguments to be passed at Traefik's binary ++# All available options available on https://docs.traefik.io/reference/static-configuration/cli/ + ## Use curly braces to pass values: `helm install --set="additionalArguments={--providers.kubernetesingress,--global.checknewversion=true}"` + additionalArguments: [] + # - "--providers.kubernetesingress" +@@ -63,7 +63,7 @@ env: {} + # - name: SOME_OTHER_VAR + # value: some-other-var-value + +-# ++# Configure ports + ports: + # The name of this one can't be changed as it is used for the readiness and + # liveness probes, but you can adjust its config to your liking +@@ -94,7 +94,7 @@ ports: + # hostPort: 8443 + expose: true + exposedPort: 443 +- # nodePort: 32443 ++ # nodePort: 32443 + + # Options for the main traefik service, where the entrypoints traffic comes + # from. +@@ -113,9 +113,6 @@ service: + # - 192.168.0.1/32 + # - 172.16.0.0/16 + +-logs: +- loglevel: WARN +- + # Enable persistence using Persistent Volume Claims + # ref: http://kubernetes.io/docs/user-guide/persistent-volumes/ + # After the pvc has been mounted, add the configs into traefik by using the `additionalArguments` list below, eg: +``` + +## 5.6.0 ![AppVersion: 2.1.3](https://img.shields.io/static/v1?label=AppVersion&message=2.1.3&color=success&logo=) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2020-03-12 + +* Add field enabled for resources + +### Default value changes + +```diff +diff --git a/traefik/values.yaml b/traefik/values.yaml +index 38bb263..7aebefe 100644 +--- a/traefik/values.yaml ++++ b/traefik/values.yaml +@@ -7,11 +7,17 @@ image: + # Configure the deployment + # + deployment: ++ enabled: true + # Number of pods of the deployment + replicas: 1 + # Additional pod annotations (e.g. for mesh injection or prometheus scraping) + podAnnotations: {} + ++# Create an IngressRoute for the dashboard ++ingressRoute: ++ dashboard: ++ enabled: true ++ + additional: + checkNewVersion: true + sendAnonymousUsage: true +@@ -93,6 +99,7 @@ ports: + # Options for the main traefik service, where the entrypoints traffic comes + # from. + service: ++ enabled: true + type: LoadBalancer + # Additional annotations (e.g. for cloud provider specific config) + annotations: {} +``` + +## 5.5.0 ![AppVersion: 2.1.3](https://img.shields.io/static/v1?label=AppVersion&message=2.1.3&color=success&logo=) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2020-03-12 + +* expose hostnetwork option + +### Default value changes + +```diff +diff --git a/traefik/values.yaml b/traefik/values.yaml +index ecb2833..38bb263 100644 +--- a/traefik/values.yaml ++++ b/traefik/values.yaml +@@ -123,6 +123,12 @@ persistence: + path: /data + annotations: {} + ++# If hostNetwork is true, runs traefik in the host network namespace ++# To prevent unschedulabel pods due to port collisions, if hostNetwork=true ++# and replicas>1, a pod anti-affinity is recommended and will be set if the ++# affinity is left as default. ++hostNetwork: false ++ + resources: {} + # requests: + # cpu: "100m" +@@ -131,5 +137,17 @@ resources: {} + # cpu: "300m" + # memory: "150Mi" + affinity: {} ++# # This example pod anti-affinity forces the scheduler to put traefik pods ++# # on nodes where no other traefik pods are scheduled. ++# # It should be used when hostNetwork: true to prevent port conflicts ++# podAntiAffinity: ++# requiredDuringSchedulingIgnoredDuringExecution: ++# - labelSelector: ++# matchExpressions: ++# - key: app ++# operator: In ++# values: ++# - {{ template "traefik.name" . }} ++# topologyKey: failure-domain.beta.kubernetes.io/zone + nodeSelector: {} + tolerations: [] +``` + +## 5.4.0 ![AppVersion: 2.1.3](https://img.shields.io/static/v1?label=AppVersion&message=2.1.3&color=success&logo=) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2020-03-12 + +* Add support for hostport + +### Default value changes + +```diff +diff --git a/traefik/values.yaml b/traefik/values.yaml +index ec1d619..ecb2833 100644 +--- a/traefik/values.yaml ++++ b/traefik/values.yaml +@@ -63,6 +63,9 @@ ports: + # liveness probes, but you can adjust its config to your liking + traefik: + port: 9000 ++ # Use hostPort if set. ++ # hostPort: 9000 ++ + # Defines whether the port is exposed if service.type is LoadBalancer or + # NodePort. + # +@@ -74,6 +77,7 @@ ports: + exposedPort: 9000 + web: + port: 8000 ++ # hostPort: 8000 + expose: true + exposedPort: 80 + # Use nodeport if set. This is useful if you have configured Traefik in a +@@ -81,6 +85,7 @@ ports: + # nodePort: 32080 + websecure: + port: 8443 ++ # hostPort: 8443 + expose: true + exposedPort: 443 + # nodePort: 32443 +``` + +## 5.3.3 ![AppVersion: 2.1.3](https://img.shields.io/static/v1?label=AppVersion&message=2.1.3&color=success&logo=) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2020-03-12 + +* Fix replica check + +### Default value changes + +```diff +diff --git a/traefik/values.yaml b/traefik/values.yaml +index 7f31548..ec1d619 100644 +--- a/traefik/values.yaml ++++ b/traefik/values.yaml +@@ -40,7 +40,7 @@ volumes: [] + # + # Configure Traefik entry points + # Additional arguments to be passed at Traefik's binary +-## Use curly braces to pass values: `helm install --set="{--providers.kubernetesingress,--global.checknewversion=true}" ." ++## Use curly braces to pass values: `helm install --set="additionalArguments={--providers.kubernetesingress,--global.checknewversion=true}"` + additionalArguments: [] + # - "--providers.kubernetesingress" + +``` + +## 5.3.2 ![AppVersion: 2.1.3](https://img.shields.io/static/v1?label=AppVersion&message=2.1.3&color=success&logo=) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2020-03-11 + +* Fixed typo in README + + +## 5.3.1 ![AppVersion: 2.1.3](https://img.shields.io/static/v1?label=AppVersion&message=2.1.3&color=success&logo=) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2020-03-11 + +* Production ready + + +## 5.3.0 ![AppVersion: 2.1.3](https://img.shields.io/static/v1?label=AppVersion&message=2.1.3&color=success&logo=) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2020-03-11 + +* Not authorise acme if replica > 1 + + +## 5.2.1 ![AppVersion: 2.1.3](https://img.shields.io/static/v1?label=AppVersion&message=2.1.3&color=success&logo=) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2020-03-11 + +* Fix volume mount + + +## 5.2.0 ![AppVersion: 2.1.3](https://img.shields.io/static/v1?label=AppVersion&message=2.1.3&color=success&logo=) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2020-03-11 + +* Add secret as env var + +### Default value changes + +```diff +diff --git a/traefik/values.yaml b/traefik/values.yaml +index ccea845..7f31548 100644 +--- a/traefik/values.yaml ++++ b/traefik/values.yaml +@@ -44,12 +44,18 @@ volumes: [] + additionalArguments: [] + # - "--providers.kubernetesingress" + ++# Secret to be set as environment variables to be passed to Traefik's binary ++secretEnv: {} ++ # - name: SOME_VAR ++ # secretName: my-secret-name ++ # secretKey: my-secret-key ++ + # Environment variables to be passed to Traefik's binary + env: {} +-# - name: SOME_VAR +-# value: some-var-value +-# - name: SOME_OTHER_VAR +-# value: some-other-var-value ++ # - name: SOME_VAR ++ # value: some-var-value ++ # - name: SOME_OTHER_VAR ++ # value: some-other-var-value + + # + ports: +``` + +## 5.1.0 ![AppVersion: 2.1.3](https://img.shields.io/static/v1?label=AppVersion&message=2.1.3&color=success&logo=) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2020-03-10 + +* Enhance security by add loadBalancerSourceRanges to lockdown ip address. + +### Default value changes + +```diff +diff --git a/traefik/values.yaml b/traefik/values.yaml +index 78bbee0..ccea845 100644 +--- a/traefik/values.yaml ++++ b/traefik/values.yaml +@@ -91,6 +91,9 @@ service: + # externalTrafficPolicy: Cluster + # loadBalancerIP: "1.2.3.4" + # clusterIP: "2.3.4.5" ++ loadBalancerSourceRanges: {} ++ # - 192.168.0.1/32 ++ # - 172.16.0.0/16 + + logs: + loglevel: WARN +``` + +## 5.0.0 ![AppVersion: 2.1.3](https://img.shields.io/static/v1?label=AppVersion&message=2.1.3&color=success&logo=) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2020-03-10 + +* Expose dashboard by default but only on traefik entrypoint + +### Default value changes + +```diff +diff --git a/traefik/values.yaml b/traefik/values.yaml +index a442fca..78bbee0 100644 +--- a/traefik/values.yaml ++++ b/traefik/values.yaml +@@ -92,15 +92,6 @@ service: + # loadBalancerIP: "1.2.3.4" + # clusterIP: "2.3.4.5" + +-dashboard: +- # Enable the dashboard on Traefik +- enable: true +- +- # Expose the dashboard and api through an ingress route at /dashboard +- # and /api This is not secure and SHOULD NOT be enabled on production +- # deployments +- ingressRoute: false +- + logs: + loglevel: WARN + +``` + +## 4.1.3 ![AppVersion: 2.1.3](https://img.shields.io/static/v1?label=AppVersion&message=2.1.3&color=success&logo=) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2020-03-10 + +* Add annotations for PVC (#98) + +### Default value changes + +```diff +diff --git a/traefik/values.yaml b/traefik/values.yaml +index 8b2f4db..a442fca 100644 +--- a/traefik/values.yaml ++++ b/traefik/values.yaml +@@ -116,6 +116,7 @@ persistence: + size: 128Mi + # storageClass: "" + path: /data ++ annotations: {} + + resources: {} + # requests: +``` + +## 4.1.2 ![AppVersion: 2.1.3](https://img.shields.io/static/v1?label=AppVersion&message=2.1.3&color=success&logo=) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2020-03-10 + +* Added persistent volume support. (#86) + +### Default value changes + +```diff +diff --git a/traefik/values.yaml b/traefik/values.yaml +index 2a2554f..8b2f4db 100644 +--- a/traefik/values.yaml ++++ b/traefik/values.yaml +@@ -103,7 +103,20 @@ dashboard: + + logs: + loglevel: WARN +-# ++ ++# Enable persistence using Persistent Volume Claims ++# ref: http://kubernetes.io/docs/user-guide/persistent-volumes/ ++# After the pvc has been mounted, add the configs into traefik by using the `additionalArguments` list below, eg: ++# additionalArguments: ++# - "--certificatesresolvers.le.acme.storage=/data/acme.json" ++# It will persist TLS certificates. ++persistence: ++ enabled: false ++ accessMode: ReadWriteOnce ++ size: 128Mi ++ # storageClass: "" ++ path: /data ++ + resources: {} + # requests: + # cpu: "100m" +``` + +## 4.1.1 ![AppVersion: 2.1.3](https://img.shields.io/static/v1?label=AppVersion&message=2.1.3&color=success&logo=) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2020-03-10 + +* Add values to mount secrets or configmaps as volumes to the traefik pod (#84) + +### Default value changes + +```diff +diff --git a/traefik/values.yaml b/traefik/values.yaml +index 5401832..2a2554f 100644 +--- a/traefik/values.yaml ++++ b/traefik/values.yaml +@@ -20,6 +20,23 @@ rollingUpdate: + maxUnavailable: 1 + maxSurge: 1 + ++ ++# ++# Add volumes to the traefik pod. ++# This can be used to mount a cert pair or a configmap that holds a config.toml file. ++# After the volume has been mounted, add the configs into traefik by using the `additionalArguments` list below, eg: ++# additionalArguments: ++# - "--providers.file.filename=/config/dynamic.toml" ++# - "--tls.certificates.certFile=/certs/tls.crt" ++# - "--tls.certificates.keyFile=/certs/tls.key" ++volumes: [] ++# - name: public-cert ++# mountPath: "/certs" ++# type: secret ++# - name: configs ++# mountPath: "/config" ++# type: configMap ++ + # + # Configure Traefik entry points + # Additional arguments to be passed at Traefik's binary +``` + +## 4.1.0 ![AppVersion: 2.1.3](https://img.shields.io/static/v1?label=AppVersion&message=2.1.3&color=success&logo=) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2020-03-10 + +* Add podAnnotations to the deployment (#83) + +### Default value changes + +```diff +diff --git a/traefik/values.yaml b/traefik/values.yaml +index 5eab74b..5401832 100644 +--- a/traefik/values.yaml ++++ b/traefik/values.yaml +@@ -9,6 +9,8 @@ image: + deployment: + # Number of pods of the deployment + replicas: 1 ++ # Additional pod annotations (e.g. for mesh injection or prometheus scraping) ++ podAnnotations: {} + + additional: + checkNewVersion: true +``` + +## 4.0.0 ![AppVersion: 2.1.3](https://img.shields.io/static/v1?label=AppVersion&message=2.1.3&color=success&logo=) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2020-03-06 + +* Migrate to helm v3 (#94) + + +## 3.5.0 ![AppVersion: 2.1.3](https://img.shields.io/static/v1?label=AppVersion&message=2.1.3&color=success&logo=) ![Helm: v2](https://img.shields.io/static/v1?label=Helm&message=v2&color=inactive&logo=helm) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2020-02-18 + +* Publish helm chart (#81) + + +## 3.4.0 ![AppVersion: 2.1.3](https://img.shields.io/static/v1?label=AppVersion&message=2.1.3&color=success&logo=) ![Helm: v2](https://img.shields.io/static/v1?label=Helm&message=v2&color=inactive&logo=helm) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2020-02-13 + +* fix: tests. +* feat: bump traefik to v2.1.3 +* Enable configuration of global checknewversion and sendanonymoususage (#80) + +### Default value changes + +```diff +diff --git a/traefik/values.yaml b/traefik/values.yaml +index bcc42f8..5eab74b 100644 +--- a/traefik/values.yaml ++++ b/traefik/values.yaml +@@ -1,7 +1,7 @@ + # Default values for Traefik + image: + name: traefik +- tag: 2.1.1 ++ tag: 2.1.3 + + # + # Configure the deployment +@@ -10,6 +10,10 @@ deployment: + # Number of pods of the deployment + replicas: 1 + ++additional: ++ checkNewVersion: true ++ sendAnonymousUsage: true ++ + rollingUpdate: + maxUnavailable: 1 + maxSurge: 1 +``` + +## 3.3.3 ![AppVersion: 2.1.1](https://img.shields.io/static/v1?label=AppVersion&message=2.1.1&color=success&logo=) ![Helm: v2](https://img.shields.io/static/v1?label=Helm&message=v2&color=inactive&logo=helm) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2020-02-05 + +* fix: deployment environment variables. +* fix: chart version. + + +## 3.3.2 ![AppVersion: 2.1.1](https://img.shields.io/static/v1?label=AppVersion&message=2.1.1&color=success&logo=) ![Helm: v2](https://img.shields.io/static/v1?label=Helm&message=v2&color=inactive&logo=helm) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2020-02-03 + +* ix: deployment environment variables. + + +## 3.3.1 ![AppVersion: 2.1.1](https://img.shields.io/static/v1?label=AppVersion&message=2.1.1&color=success&logo=) ![Helm: v2](https://img.shields.io/static/v1?label=Helm&message=v2&color=inactive&logo=helm) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2020-01-27 + +* fix: deployment environment variables. + + +## 3.3.0 ![AppVersion: 2.1.1](https://img.shields.io/static/v1?label=AppVersion&message=2.1.1&color=success&logo=) ![Helm: v2](https://img.shields.io/static/v1?label=Helm&message=v2&color=inactive&logo=helm) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2020-01-24 + +* Enable configuration of environment variables in traefik deployment (#71) + +### Default value changes + +```diff +diff --git a/traefik/values.yaml b/traefik/values.yaml +index 4462359..bcc42f8 100644 +--- a/traefik/values.yaml ++++ b/traefik/values.yaml +@@ -21,6 +21,13 @@ rollingUpdate: + additionalArguments: [] + # - "--providers.kubernetesingress" + ++# Environment variables to be passed to Traefik's binary ++env: {} ++# - name: SOME_VAR ++# value: some-var-value ++# - name: SOME_OTHER_VAR ++# value: some-other-var-value ++ + # + ports: + # The name of this one can't be changed as it is used for the readiness and +``` + +## 3.2.1 ![AppVersion: 2.1.1](https://img.shields.io/static/v1?label=AppVersion&message=2.1.1&color=success&logo=) ![Helm: v2](https://img.shields.io/static/v1?label=Helm&message=v2&color=inactive&logo=helm) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2020-01-22 + +* Add Unit Tests for the chart (#60) + + +## 3.2.0 ![AppVersion: 2.1.1](https://img.shields.io/static/v1?label=AppVersion&message=2.1.1&color=success&logo=) ![Helm: v2](https://img.shields.io/static/v1?label=Helm&message=v2&color=inactive&logo=helm) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2020-01-22 + +* Make NodePort configurable (#67) + +### Default value changes + +```diff +diff --git a/traefik/values.yaml b/traefik/values.yaml +index b1fe42a..4462359 100644 +--- a/traefik/values.yaml ++++ b/traefik/values.yaml +@@ -40,10 +40,14 @@ ports: + port: 8000 + expose: true + exposedPort: 80 ++ # Use nodeport if set. This is useful if you have configured Traefik in a ++ # LoadBalancer ++ # nodePort: 32080 + websecure: + port: 8443 + expose: true + exposedPort: 443 ++ # nodePort: 32443 + + # Options for the main traefik service, where the entrypoints traffic comes + # from. +``` + +## 3.1.0 ![AppVersion: 2.1.1](https://img.shields.io/static/v1?label=AppVersion&message=2.1.1&color=success&logo=) ![Helm: v2](https://img.shields.io/static/v1?label=Helm&message=v2&color=inactive&logo=helm) ![Helm: v3](https://img.shields.io/static/v1?label=Helm&message=v3&color=informational&logo=helm) + +**Release date:** 2020-01-20 + +* Switch Chart linting to ct (#59) + +### Default value changes + +```diff +# Default values for Traefik +image: + name: traefik + tag: 2.1.1 + +# +# Configure the deployment +# +deployment: + # Number of pods of the deployment + replicas: 1 + +rollingUpdate: + maxUnavailable: 1 + maxSurge: 1 + +# +# Configure Traefik entry points +# Additional arguments to be passed at Traefik's binary +## Use curly braces to pass values: `helm install --set="{--providers.kubernetesingress,--global.checknewversion=true}" ." +additionalArguments: [] +# - "--providers.kubernetesingress" + +# +ports: + # The name of this one can't be changed as it is used for the readiness and + # liveness probes, but you can adjust its config to your liking + traefik: + port: 9000 + # Defines whether the port is exposed if service.type is LoadBalancer or + # NodePort. + # + # You SHOULD NOT expose the traefik port on production deployments. + # If you want to access it from outside of your cluster, + # use `kubectl proxy` or create a secure ingress + expose: false + # The exposed port for this service + exposedPort: 9000 + web: + port: 8000 + expose: true + exposedPort: 80 + websecure: + port: 8443 + expose: true + exposedPort: 443 + +# Options for the main traefik service, where the entrypoints traffic comes +# from. +service: + type: LoadBalancer + # Additional annotations (e.g. for cloud provider specific config) + annotations: {} + # Additional entries here will be added to the service spec. Cannot contains + # type, selector or ports entries. + spec: {} + # externalTrafficPolicy: Cluster + # loadBalancerIP: "1.2.3.4" + # clusterIP: "2.3.4.5" + +dashboard: + # Enable the dashboard on Traefik + enable: true + + # Expose the dashboard and api through an ingress route at /dashboard + # and /api This is not secure and SHOULD NOT be enabled on production + # deployments + ingressRoute: false + +logs: + loglevel: WARN +# +resources: {} + # requests: + # cpu: "100m" + # memory: "50Mi" + # limits: + # cpu: "300m" + # memory: "150Mi" +affinity: {} +nodeSelector: {} +tolerations: [] +``` + +--- +Autogenerated from Helm Chart and git history using [helm-changelog](https://github.com/mogensen/helm-changelog) diff --git a/charts/traefik/traefik/31.1.0/Chart.yaml b/charts/traefik/traefik/31.1.0/Chart.yaml new file mode 100644 index 000000000..8ee7376cd --- /dev/null +++ b/charts/traefik/traefik/31.1.0/Chart.yaml @@ -0,0 +1,34 @@ +annotations: + artifacthub.io/changes: "- \"fix: \U0001F41B update CRD to v3.1\"\n- \"feat: ✨ input + validation using schema\"\n- \"feat: ✨ add AllowACMEByPass and improve schema/doc + on ports values\"\n- \"feat: add new webhooks and removes unnecessary ones\"\n- + \"feat(deps): update traefik docker tag to v3.1.3\"\n- \"chore(release): \U0001F680 + publish v31.1.0\"\n" + catalog.cattle.io/certified: partner + catalog.cattle.io/display-name: Traefik Proxy + catalog.cattle.io/kube-version: '>=1.22.0-0' + catalog.cattle.io/release-name: traefik +apiVersion: v2 +appVersion: v3.1.3 +description: A Traefik based Kubernetes ingress controller +home: https://traefik.io/ +icon: file://assets/icons/traefik.png +keywords: +- traefik +- ingress +- networking +kubeVersion: '>=1.22.0-0' +maintainers: +- email: michel.loiseleur@traefik.io + name: mloiseleur +- email: charlie.haley@traefik.io + name: charlie-haley +- email: remi.buisson@traefik.io + name: darkweaver87 +- name: jnoordsij +name: traefik +sources: +- https://github.com/traefik/traefik +- https://github.com/traefik/traefik-helm-chart +type: application +version: 31.1.0 diff --git a/charts/traefik/traefik/31.1.0/EXAMPLES.md b/charts/traefik/traefik/31.1.0/EXAMPLES.md new file mode 100644 index 000000000..8cad75595 --- /dev/null +++ b/charts/traefik/traefik/31.1.0/EXAMPLES.md @@ -0,0 +1,1005 @@ +# Install as a DaemonSet + +Default install is using a `Deployment` but it's possible to use `DaemonSet` + +```yaml +deployment: + kind: DaemonSet +``` + +# Configure traefik Pod parameters + +## Extending /etc/hosts records + +In some specific cases, you'll need to add extra records to the `/etc/hosts` file for the Traefik containers. +You can configure it using [hostAliases](https://kubernetes.io/docs/tasks/network/customize-hosts-file-for-pods/): + +```yaml +deployment: + hostAliases: + - ip: "127.0.0.1" # this is an example + hostnames: + - "foo.local" + - "bar.local" +``` +## Extending DNS config + +In order to configure additional DNS servers for your traefik pod, you can use `dnsConfig` option: + +```yaml +deployment: + dnsConfig: + nameservers: + - 192.0.2.1 # this is an example + searches: + - ns1.svc.cluster-domain.example + - my.dns.search.suffix + options: + - name: ndots + value: "2" + - name: edns0 +``` + +# Install in a dedicated namespace, with limited RBAC + +Default install is using Cluster-wide RBAC but it can be restricted to target namespace. + +```yaml +rbac: + namespaced: true +``` + +# Install with auto-scaling + +When enabling [HPA](https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/) +to adjust replicas count according to CPU Usage, you'll need to set resources and nullify replicas. + +```yaml +deployment: + replicas: null +resources: + requests: + cpu: "100m" + memory: "50Mi" + limits: + cpu: "300m" + memory: "150Mi" +autoscaling: + enabled: true + maxReplicas: 2 + metrics: + - type: Resource + resource: + name: cpu + target: + type: Utilization + averageUtilization: 80 +``` + +# Access Traefik dashboard without exposing it + +This Chart does not expose the Traefik local dashboard by default. It's explained in upstream [documentation](https://doc.traefik.io/traefik/operations/api/) why: + +> Enabling the API in production is not recommended, because it will expose all configuration elements, including sensitive data. + +It says also: + +> In production, it should be at least secured by authentication and authorizations. + +Thus, there are multiple ways to expose the dashboard. For instance, after enabling the creation of dashboard `IngressRoute` in the values: + +```yaml +ingressRoute: + dashboard: + enabled: true +``` + +The traefik admin port can be forwarded locally: + +```bash +kubectl port-forward $(kubectl get pods --selector "app.kubernetes.io/name=traefik" --output=name) 9000:9000 +``` + +This command makes the dashboard accessible on the url: http://127.0.0.1:9000/dashboard/ + +# Publish and protect Traefik Dashboard with basic Auth + +To expose the dashboard in a secure way as [recommended](https://doc.traefik.io/traefik/operations/dashboard/#dashboard-router-rule) +in the documentation, it may be useful to override the router rule to specify +a domain to match, or accept requests on the root path (/) in order to redirect +them to /dashboard/. + +```yaml +# Create an IngressRoute for the dashboard +ingressRoute: + dashboard: + enabled: true + # Custom match rule with host domain + matchRule: Host(`traefik-dashboard.example.com`) + entryPoints: ["websecure"] + # Add custom middlewares : authentication and redirection + middlewares: + - name: traefik-dashboard-auth + +# Create the custom middlewares used by the IngressRoute dashboard (can also be created in another way). +# /!\ Yes, you need to replace "changeme" password with a better one. /!\ +extraObjects: + - apiVersion: v1 + kind: Secret + metadata: + name: traefik-dashboard-auth-secret + type: kubernetes.io/basic-auth + stringData: + username: admin + password: changeme + + - apiVersion: traefik.io/v1alpha1 + kind: Middleware + metadata: + name: traefik-dashboard-auth + spec: + basicAuth: + secret: traefik-dashboard-auth-secret +``` + +# Publish and protect Traefik Dashboard with an Ingress + +To expose the dashboard without IngressRoute, it's more complicated and less +secure. You'll need to create an internal Service exposing Traefik API with +special _traefik_ entrypoint. This internal Service can be created from an other tool, with the `extraObjects` section or using [custom services](#add-custom-internal-services). + +You'll need to double check: +1. Service selector with your setup. +2. Middleware annotation on the ingress, _default_ should be replaced with traefik's namespace + +```yaml +ingressRoute: + dashboard: + enabled: false +additionalArguments: +- "--api.insecure=true" +# Create the service, middleware and Ingress used to expose the dashboard (can also be created in another way). +# /!\ Yes, you need to replace "changeme" password with a better one. /!\ +extraObjects: + - apiVersion: v1 + kind: Service + metadata: + name: traefik-api + spec: + type: ClusterIP + selector: + app.kubernetes.io/name: traefik + app.kubernetes.io/instance: traefik-default + ports: + - port: 8080 + name: traefik + targetPort: 9000 + protocol: TCP + + - apiVersion: v1 + kind: Secret + metadata: + name: traefik-dashboard-auth-secret + type: kubernetes.io/basic-auth + stringData: + username: admin + password: changeme + + - apiVersion: traefik.io/v1alpha1 + kind: Middleware + metadata: + name: traefik-dashboard-auth + spec: + basicAuth: + secret: traefik-dashboard-auth-secret + + - apiVersion: networking.k8s.io/v1 + kind: Ingress + metadata: + name: traefik-dashboard + annotations: + traefik.ingress.kubernetes.io/router.entrypoints: websecure + traefik.ingress.kubernetes.io/router.middlewares: default-traefik-dashboard-auth@kubernetescrd + spec: + rules: + - host: traefik-dashboard.example.com + http: + paths: + - path: / + pathType: Prefix + backend: + service: + name: traefik-api + port: + name: traefik +``` + + +# Install on AWS + +It can use [native AWS support](https://kubernetes.io/docs/concepts/services-networking/service/#aws-nlb-support) on Kubernetes + +```yaml +service: + annotations: + service.beta.kubernetes.io/aws-load-balancer-type: nlb +``` + +Or if [AWS LB controller](https://kubernetes-sigs.github.io/aws-load-balancer-controller/v2.2/guide/service/annotations/#legacy-cloud-provider) is installed : +```yaml +service: + annotations: + service.beta.kubernetes.io/aws-load-balancer-type: nlb-ip +``` + +# Install on GCP + +A [regional IP with a Service](https://cloud.google.com/kubernetes-engine/docs/tutorials/configuring-domain-name-static-ip#use_a_service) can be used +```yaml +service: + spec: + loadBalancerIP: "1.2.3.4" +``` + +Or a [global IP on Ingress](https://cloud.google.com/kubernetes-engine/docs/tutorials/configuring-domain-name-static-ip#use_an_ingress) +```yaml +service: + type: NodePort +extraObjects: + - apiVersion: networking.k8s.io/v1 + kind: Ingress + metadata: + name: traefik + annotations: + kubernetes.io/ingress.global-static-ip-name: "myGlobalIpName" + spec: + defaultBackend: + service: + name: traefik + port: + number: 80 +``` + +Or a [global IP on a Gateway](https://cloud.google.com/kubernetes-engine/docs/how-to/deploying-gateways) with continuous HTTPS encryption. + +```yaml +ports: + websecure: + appProtocol: HTTPS # Hint for Google L7 load balancer +service: + type: ClusterIP +extraObjects: +- apiVersion: gateway.networking.k8s.io/v1beta1 + kind: Gateway + metadata: + name: traefik + annotations: + networking.gke.io/certmap: "myCertificateMap" + spec: + gatewayClassName: gke-l7-global-external-managed + addresses: + - type: NamedAddress + value: "myGlobalIPName" + listeners: + - name: https + protocol: HTTPS + port: 443 +- apiVersion: gateway.networking.k8s.io/v1beta1 + kind: HTTPRoute + metadata: + name: traefik + spec: + parentRefs: + - kind: Gateway + name: traefik + rules: + - backendRefs: + - name: traefik + port: 443 +- apiVersion: networking.gke.io/v1 + kind: HealthCheckPolicy + metadata: + name: traefik + spec: + default: + config: + type: HTTP + httpHealthCheck: + port: 9000 + requestPath: /ping + targetRef: + group: "" + kind: Service + name: traefik +``` + +# Install on Azure + +A [static IP on a resource group](https://learn.microsoft.com/en-us/azure/aks/static-ip) can be used: + +```yaml +service: + spec: + loadBalancerIP: "1.2.3.4" + annotations: + service.beta.kubernetes.io/azure-load-balancer-resource-group: myResourceGroup +``` + +Here is a more complete example, using also native Let's encrypt feature of Traefik Proxy with Azure DNS: + +```yaml +persistence: + enabled: true + size: 128Mi +certResolvers: + letsencrypt: + email: "{{ letsencrypt_email }}" + #caServer: https://acme-v02.api.letsencrypt.org/directory # Production server + caServer: https://acme-staging-v02.api.letsencrypt.org/directory # Staging server + dnsChallenge: + provider: azuredns + storage: /data/acme.json +env: + - name: AZURE_CLIENT_ID + value: "{{ azure_dns_challenge_application_id }}" + - name: AZURE_CLIENT_SECRET + valueFrom: + secretKeyRef: + name: azuredns-secret + key: client-secret + - name: AZURE_SUBSCRIPTION_ID + value: "{{ azure_subscription_id }}" + - name: AZURE_TENANT_ID + value: "{{ azure_tenant_id }}" + - name: AZURE_RESOURCE_GROUP + value: "{{ azure_resource_group }}" +deployment: + initContainers: + - name: volume-permissions + image: busybox:latest + command: ["sh", "-c", "ls -la /; touch /data/acme.json; chmod -v 600 /data/acme.json"] + volumeMounts: + - mountPath: /data + name: data +podSecurityContext: + fsGroup: 65532 + fsGroupChangePolicy: "OnRootMismatch" +service: + spec: + type: LoadBalancer + annotations: + service.beta.kubernetes.io/azure-load-balancer-resource-group: "{{ azure_node_resource_group }}" + service.beta.kubernetes.io/azure-pip-name: "{{ azure_resource_group }}" + service.beta.kubernetes.io/azure-dns-label-name: "{{ azure_resource_group }}" + service.beta.kubernetes.io/azure-allowed-ip-ranges: "{{ ip_range | join(',') }}" +extraObjects: + - apiVersion: v1 + kind: Secret + metadata: + name: azuredns-secret + namespace: traefik + type: Opaque + stringData: + client-secret: "{{ azure_dns_challenge_application_secret }}" +``` + +# Use an IngressClass + +Default install comes with an `IngressClass` resource that can be enabled on providers. + +Here's how one can enable it on CRD & Ingress Kubernetes provider: + +```yaml +ingressClass: + name: traefik +providers: + kubernetesCRD: + ingressClass: traefik + kubernetesIngress: + ingressClass: traefik +``` + +# Use HTTP3 + +By default, it will use a Load balancers with mixed protocols on `websecure` +entrypoint. They are available since v1.20 and in beta as of Kubernetes v1.24. +Availability may depend on your Kubernetes provider. + +When using TCP and UDP with a single service, you may encounter [this issue](https://github.com/kubernetes/kubernetes/issues/47249#issuecomment-587960741) from Kubernetes. +If you want to avoid this issue, you can set `ports.websecure.http3.advertisedPort` +to an other value than 443 + +```yaml +ports: + websecure: + http3: + enabled: true +``` + +You can also create two `Service`, one for TCP and one for UDP: + +```yaml +ports: + websecure: + http3: + enabled: true +service: + single: false +``` + +# Use PROXY protocol on Digital Ocean + +PROXY protocol is a protocol for sending client connection information, such as origin IP addresses and port numbers, to the final backend server, rather than discarding it at the load balancer. + +```yaml +.DOTrustedIPs: &DOTrustedIPs + - 127.0.0.1/32 + - 10.120.0.0/16 + +service: + enabled: true + type: LoadBalancer + annotations: + # This will tell DigitalOcean to enable the proxy protocol. + service.beta.kubernetes.io/do-loadbalancer-enable-proxy-protocol: "true" + spec: + # This is the default and should stay as cluster to keep the DO health checks working. + externalTrafficPolicy: Cluster + +ports: + web: + forwardedHeaders: + trustedIPs: *DOTrustedIPs + proxyProtocol: + trustedIPs: *DOTrustedIPs + websecure: + forwardedHeaders: + trustedIPs: *DOTrustedIPs + proxyProtocol: + trustedIPs: *DOTrustedIPs +``` + +# Enable plugin storage + +This chart follows common security practices: it runs as non root with a readonly root filesystem. +When enabling a plugin which needs storage, you have to add it to the deployment. + +Here is a simple example with crowdsec. You may want to replace with your plugin or see complete exemple on crowdsec [here](https://github.com/maxlerebourg/crowdsec-bouncer-traefik-plugin/blob/main/examples/kubernetes/README.md). + +```yaml +deployment: + additionalVolumes: + - name: plugins +additionalVolumeMounts: +- name: plugins + mountPath: /plugins-storage +additionalArguments: +- "--experimental.plugins.bouncer.moduleName=github.com/maxlerebourg/crowdsec-bouncer-traefik-plugin" +- "--experimental.plugins.bouncer.version=v1.1.9" +``` + +# Use Traefik native Let's Encrypt integration, without cert-manager + +In Traefik Proxy, ACME certificates are stored in a JSON file. + +This file needs to have 0600 permissions, meaning, only the owner of the file has full read and write access to it. +By default, Kubernetes recursively changes ownership and permissions for the content of each volume. + +=> An initContainer can be used to avoid an issue on this sensitive file. +See [#396](https://github.com/traefik/traefik-helm-chart/issues/396) for more details. + +Once the provider is ready, it can be used in an `IngressRoute`: + +```yaml +apiVersion: traefik.containo.us/v1alpha1 +kind: IngressRoute +metadata: + name: [...] +spec: + entryPoints: [...] + routes: [...] + tls: + certResolver: letsencrypt +``` + +See [the list of supported providers](https://doc.traefik.io/traefik/https/acme/#providers) for others. + +## Example with CloudFlare + +This example needs a CloudFlare token in a Kubernetes `Secret` and a working `StorageClass`. + +**Step 1**: Create `Secret` with CloudFlare token: + +```yaml +--- +apiVersion: v1 +kind: Secret +metadata: + name: cloudflare +type: Opaque +stringData: + token: {{ SET_A_VALID_TOKEN_HERE }} +``` + +**Step 2**: + +```yaml +persistence: + enabled: true + storageClass: xxx +certResolvers: + letsencrypt: + dnsChallenge: + provider: cloudflare + storage: /data/acme.json +env: + - name: CF_DNS_API_TOKEN + valueFrom: + secretKeyRef: + name: cloudflare + key: token +deployment: + initContainers: + - name: volume-permissions + image: busybox:latest + command: ["sh", "-c", "touch /data/acme.json; chmod -v 600 /data/acme.json"] + volumeMounts: + - mountPath: /data + name: data +podSecurityContext: + fsGroup: 65532 + fsGroupChangePolicy: "OnRootMismatch" +``` + +# Provide default certificate with cert-manager and CloudFlare DNS + +Setup: + +* cert-manager installed in `cert-manager` namespace +* A cloudflare account on a DNS Zone + +**Step 1**: Create `Secret` and `Issuer` needed by `cert-manager` with your API Token. +See [cert-manager documentation](https://cert-manager.io/docs/configuration/acme/dns01/cloudflare/) +for creating this token with needed rights: + +```yaml +--- +apiVersion: v1 +kind: Secret +metadata: + name: cloudflare + namespace: traefik +type: Opaque +stringData: + api-token: XXX +--- +apiVersion: cert-manager.io/v1 +kind: Issuer +metadata: + name: cloudflare + namespace: traefik +spec: + acme: + server: https://acme-v02.api.letsencrypt.org/directory + email: email@example.com + privateKeySecretRef: + name: cloudflare-key + solvers: + - dns01: + cloudflare: + apiTokenSecretRef: + name: cloudflare + key: api-token +``` + +**Step 2**: Create `Certificate` in traefik namespace + +```yaml +apiVersion: cert-manager.io/v1 +kind: Certificate +metadata: + name: wildcard-example-com + namespace: traefik +spec: + secretName: wildcard-example-com-tls + dnsNames: + - "example.com" + - "*.example.com" + issuerRef: + name: cloudflare + kind: Issuer +``` + +**Step 3**: Check that it's ready + +```bash +kubectl get certificate -n traefik +``` + +If needed, logs of cert-manager pod can give you more information + +**Step 4**: Use it on the TLS Store in **values.yaml** file for this Helm Chart + +```yaml +tlsStore: + default: + defaultCertificate: + secretName: wildcard-example-com-tls +``` + +**Step 5**: Enjoy. All your `IngressRoute` use this certificate by default now. + +They should use websecure entrypoint like this: + +```yaml +apiVersion: traefik.io/v1alpha1 +kind: IngressRoute +metadata: + name: example-com-tls +spec: + entryPoints: + - websecure + routes: + - match: Host(`test.example.com`) + kind: Rule + services: + - name: XXXX + port: 80 +``` + +# Add custom (internal) services + +In some cases you might want to have more than one Traefik service within your cluster, +e.g. a default (external) one and a service that is only exposed internally to pods within your cluster. + +The `service.additionalServices` allows you to add an arbitrary amount of services, +provided as a name to service details mapping; for example you can use the following values: + +```yaml +service: + additionalServices: + internal: + type: ClusterIP + labels: + traefik-service-label: internal +``` + +Ports can then be exposed on this service by using the port name to boolean mapping `expose` on the respective port; +e.g. to expose the `traefik` API port on your internal service so pods within your cluster can use it, you can do: + +```yaml +ports: + traefik: + expose: + # Sensitive data should not be exposed on the internet + # => Keep this disabled ! + default: false + internal: true +``` + +This will then provide an additional Service manifest, looking like this: + +```yaml +--- +# Source: traefik/templates/service.yaml +apiVersion: v1 +kind: Service +metadata: + name: traefik-internal + namespace: traefik +[...] +spec: + type: ClusterIP + selector: + app.kubernetes.io/name: traefik + app.kubernetes.io/instance: traefik-traefik + ports: + - port: 9000 + name: "traefik" + targetPort: traefik + protocol: TCP +``` + +# Use this Chart as a dependency of your own chart + + +First, let's create a default Helm Chart, with Traefik as a dependency. +```bash +helm create foo +cd foo +echo " +dependencies: + - name: traefik + version: "24.0.0" + repository: "https://traefik.github.io/charts" +" >> Chart.yaml +``` + +Second, let's tune some values like enabling HPA: + +```bash +cat <<-EOF >> values.yaml +traefik: + autoscaling: + enabled: true + maxReplicas: 3 +EOF +``` + +Third, one can see if it works as expected: +```bash +helm dependency update +helm dependency build +helm template . | grep -A 14 -B 3 Horizontal +``` + +It should produce this output: + +```yaml +--- +# Source: foo/charts/traefik/templates/hpa.yaml +apiVersion: autoscaling/v2 +kind: HorizontalPodAutoscaler +metadata: + name: release-name-traefik + namespace: flux-system + labels: + app.kubernetes.io/name: traefik + app.kubernetes.io/instance: release-name-flux-system + helm.sh/chart: traefik-24.0.0 + app.kubernetes.io/managed-by: Helm +spec: + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: release-name-traefik + maxReplicas: 3 +``` + +# Configure TLS + +The [TLS options](https://doc.traefik.io/traefik/https/tls/#tls-options) allow one to configure some parameters of the TLS connection. + +```yaml +tlsOptions: + default: + labels: {} + sniStrict: true + custom-options: + labels: {} + curvePreferences: + - CurveP521 + - CurveP384 +``` + +# Use latest build of Traefik v3 from master + +An experimental build of Traefik Proxy is available on a specific repository. + +It can be used with those _values_: + +```yaml +image: + repository: traefik/traefik + tag: experimental-v3.0 +``` + +# Use Prometheus Operator + +An optional support of this operator is included in this Chart. See documentation of this operator for more details. + +It can be used with those _values_: + +```yaml +metrics: + prometheus: + service: + enabled: true + disableAPICheck: false + serviceMonitor: + enabled: true + metricRelabelings: + - sourceLabels: [__name__] + separator: ; + regex: ^fluentd_output_status_buffer_(oldest|newest)_.+ + replacement: $1 + action: drop + relabelings: + - sourceLabels: [__meta_kubernetes_pod_node_name] + separator: ; + regex: ^(.*)$ + targetLabel: nodename + replacement: $1 + action: replace + jobLabel: traefik + interval: 30s + honorLabels: true + prometheusRule: + enabled: true + rules: + - alert: TraefikDown + expr: up{job="traefik"} == 0 + for: 5m + labels: + context: traefik + severity: warning + annotations: + summary: "Traefik Down" + description: "{{ $labels.pod }} on {{ $labels.nodename }} is down" +``` + +# Use kubernetes Gateway API + +One can use the new stable kubernetes gateway API provider setting the following _values_: + +```yaml +providers: + kubernetesGateway: + enabled: true +``` + +

+ +With those values, a whoami service can be exposed with a HTTPRoute + +```yaml +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: whoami +spec: + replicas: 2 + selector: + matchLabels: + app: whoami + template: + metadata: + labels: + app: whoami + spec: + containers: + - name: whoami + image: traefik/whoami + +--- +apiVersion: v1 +kind: Service +metadata: + name: whoami +spec: + selector: + app: whoami + ports: + - protocol: TCP + port: 80 + +--- +apiVersion: gateway.networking.k8s.io/v1 +kind: HTTPRoute +metadata: + name: whoami +spec: + parentRefs: + - name: traefik-gateway + hostnames: + - whoami.docker.localhost + rules: + - matches: + - path: + type: Exact + value: / + + backendRefs: + - name: whoami + port: 80 + weight: 1 +``` + +Once it's applied, whoami should be accessible on http://whoami.docker.localhost/ + +
+ +# Use Kubernetes Gateway API with cert-manager + +One can use the new stable kubernetes gateway API provider with automatic TLS certificates delivery (with cert-manager) setting the following _values_: + +```yaml +providers: + kubernetesGateway: + enabled: true +gateway: + enabled: true + annotations: + cert-manager.io/issuer: selfsigned-issuer + listeners: + websecure: + hostname: whoami.docker.localhost + port: 8443 + protocol: HTTPS + certificateRefs: + - name: whoami-tls +``` + +Install cert-manager: + +```bash +helm repo add jetstack https://charts.jetstack.io --force-update +helm upgrade --install \ +cert-manager jetstack/cert-manager \ +--namespace cert-manager \ +--create-namespace \ +--version v1.15.1 \ +--set crds.enabled=true \ +--set "extraArgs={--enable-gateway-api}" +``` + +
+ +With those values, a whoami service can be exposed with HTTPRoute on both HTTP and HTTPS + +```yaml +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: whoami +spec: + replicas: 2 + selector: + matchLabels: + app: whoami + template: + metadata: + labels: + app: whoami + spec: + containers: + - name: whoami + image: traefik/whoami + +--- +apiVersion: v1 +kind: Service +metadata: + name: whoami +spec: + selector: + app: whoami + ports: + - protocol: TCP + port: 80 + +--- +apiVersion: gateway.networking.k8s.io/v1 +kind: HTTPRoute +metadata: + name: whoami +spec: + parentRefs: + - name: traefik-gateway + hostnames: + - whoami.docker.localhost + rules: + - matches: + - path: + type: Exact + value: / + + backendRefs: + - name: whoami + port: 80 + weight: 1 + +--- +apiVersion: cert-manager.io/v1 +kind: Issuer +metadata: + name: selfsigned-issuer +spec: + selfSigned: {} +``` + +Once it's applied, whoami should be accessible on https://whoami.docker.localhost/ + +
diff --git a/charts/traefik/traefik/31.1.0/Guidelines.md b/charts/traefik/traefik/31.1.0/Guidelines.md new file mode 100644 index 000000000..3b72a40cd --- /dev/null +++ b/charts/traefik/traefik/31.1.0/Guidelines.md @@ -0,0 +1,34 @@ +# Traefik Helm Chart Guidelines + +This document outlines the guidelines for developing, managing and extending the Traefik helm chart. + +This Helm Chart is documented using field description from comments with [helm-docs](https://github.com/norwoodj/helm-docs). + +It comes with a JSON schema generated from values with [helm schema](https://github.com/losisin/helm-values-schema-json) plugin. + +## Feature Example + +```yaml +logs: + general: + # -- Set [logs format](https://doc.traefik.io/traefik/observability/logs/#format) + format: # @schema enum:["common", "json", null]; type:[string, null]; default: "common" +``` + +Documention is on the first comment, starting with `# --` +Specific instructions for schema, when needed, are done with the inline comment starting with `# @schema`. + +## Whitespace + +Extra whitespace is to be avoided in templating. Conditionals should chomp whitespace: + +```yaml +{{- if .Values }} +{{- end }} +``` + +There should be an empty commented line between each primary key in the values.yaml file to separate features from each other. + +## Values YAML Design + +The values.yaml file is designed to be user-friendly. It does not have to resemble the templated configuration if it is not conducive. Similarly, value names to not have to correspond to fields in the template if it is not conducive. diff --git a/charts/traefik/traefik/31.1.0/LICENSE b/charts/traefik/traefik/31.1.0/LICENSE new file mode 100644 index 000000000..907ff8321 --- /dev/null +++ b/charts/traefik/traefik/31.1.0/LICENSE @@ -0,0 +1,202 @@ + 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 2020 Containous + Copyright 2020 Traefik Labs + + 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/traefik/traefik/31.1.0/README.md b/charts/traefik/traefik/31.1.0/README.md new file mode 100644 index 000000000..cd963c199 --- /dev/null +++ b/charts/traefik/traefik/31.1.0/README.md @@ -0,0 +1,158 @@ +# Traefik + +[Traefik](https://traefik.io/) is a modern HTTP reverse proxy and load balancer made to deploy +microservices with ease. + +## Introduction + +Starting with v28.x, this chart now bootstraps Traefik Proxy version 3 as a Kubernetes ingress controller, +using Custom Resources `IngressRoute`: . + +It's possible to use this chart with Traefik Proxy v2 using v27.x +This chart support policy is aligned with [upstream support policy](https://doc.traefik.io/traefik/deprecation/releases/) of Traefik Proxy. + +See [Migration guide from v2 to v3](https://doc.traefik.io/traefik/v3.0/migration/v2-to-v3/) and upgrading section of this chart on CRDs. + +### Philosophy + +The Traefik HelmChart is focused on Traefik deployment configuration. + +To keep this HelmChart as generic as possible we tend +to avoid integrating any third party solutions nor any specific use cases. + +Accordingly, the encouraged approach to fulfill your needs: + +1. Override the default Traefik configuration values ([yaml file or cli](https://helm.sh/docs/chart_template_guide/values_files/)) +2. Append your own configurations (`kubectl apply -f myconf.yaml`) + +[Examples](https://github.com/traefik/traefik-helm-chart/blob/master/EXAMPLES.md) of common usage are provided. + +If needed, one may use [extraObjects](./traefik/tests/values/extra.yaml) or extend this HelmChart [as a Subchart](https://helm.sh/docs/chart_template_guide/subcharts_and_globals/). + +## Installing + +### Prerequisites + +1. [x] Helm **v3 > 3.9.0** [installed](https://helm.sh/docs/using_helm/#installing-helm): `helm version` +2. [x] Traefik's chart repository: `helm repo add traefik https://traefik.github.io/charts` + +### Kubernetes Version Support + +Due to changes in CRD version support, the following versions of the chart are usable and supported on the following Kubernetes versions: + +| | Kubernetes v1.15 and below | Kubernetes v1.16-v1.21 | Kubernetes v1.22 and above | +|-------------------------|-----------------------------|------------------------|----------------------------| +| Chart v9.20.2 and below | [x] | [x] | | +| Chart v10.0.0 and above | | [x] | [x] | +| Chart v22.0.0 and above | | | [x] | + +### CRDs Support of Traefik Proxy + +Due to changes in API Group of Traefik CRDs from `containo.us` to `traefik.io`, this Chart install CRDs needed by default Traefik Proxy version, following this table: + +| | `containo.us` | `traefik.io` | +|-------------------------|-----------------------------|------------------------| +| Chart v22.0.0 and below | [x] | | +| Chart v23.0.0 and above | [x] | [x] | +| Chart v28.0.0 and above | | [x] | + +### Deploying Traefik + +```bash +helm install traefik traefik/traefik +``` + +or: + +```bash +helm install traefik oci://ghcr.io/traefik/helm/traefik +``` + +You can customize the install with a `values` file. There are some [EXAMPLES](./EXAMPLES.md) provided. +Complete documentation on all available parameters is in the [default file](./traefik/values.yaml). + +```bash +helm install -f myvalues.yaml traefik traefik/traefik +``` + +🛂 **Warning**: Helm v2 support was removed in the chart version 10.0.0. + +## Upgrading + +One can check what has changed in the [Changelog](./traefik/Changelog.md). + +:information_source: With Helm v3, CRDs created by this chart can not be updated, cf. the [Helm Documentation on CRDs](https://helm.sh/docs/chart_best_practices/custom_resource_definitions). + +:warning: Please read carefully release notes of this chart before upgrading CRDs. + +```bash +# Update repository +helm repo update +# See current Chart & Traefik version +helm search repo traefik/traefik +# Update CRDs (Traefik Proxy v3 CRDs) +kubectl apply --server-side --force-conflicts -k https://github.com/traefik/traefik-helm-chart/traefik/crds/ +# Upgrade Traefik +helm upgrade traefik traefik/traefik +``` + +New major version indicates that there is an incompatible breaking change. + +#### Upgrade up to 27.X + +When upgrading on Traefik Proxy v2 version, one need to stay at Traefik Helm Chart v27.x. The command to upgrade to the latest Traefik Proxy v2 CRD is: + +```bash +kubectl apply --server-side --force-conflicts -k https://github.com/traefik/traefik-helm-chart/traefik/crds/?ref=v27 +``` + +### Upgrading after 18.X+ + +It's detailed in [release notes](https://github.com/traefik/traefik-helm-chart/releases). + +### Upgrading from 17.x to 18.x + +Since v18.x, this chart by default merges TCP and UDP ports into a single (LoadBalancer) `Service`. +Load balancers with mixed protocols are available since v1.20 and in +[beta as of Kubernetes v1.24](https://kubernetes.io/docs/concepts/services-networking/service/#load-balancers-with-mixed-protocol-types). +Availability may depend on your Kubernetes provider. + +To retain the old default behavior, set `service.single` to `false` in your values. + +When using TCP and UDP with a single service, you may encounter +[this issue](https://github.com/kubernetes/kubernetes/issues/47249#issuecomment-587960741) +from Kubernetes. + +On HTTP/3, if you want to avoid this issue, you can set +`ports.websecure.http3.advertisedPort` to an other value than `443` + +If you were previously using HTTP/3, you should update your values as follows: + - Replace the old value (`true`) of `ports.websecure.http3` with a key `enabled: true` + - Remove `experimental.http3.enabled=true` entry + +### Upgrading from 16.x to 17.x + +Since v17.x, this chart provides unified labels following +[Kubernetes recommendation](https://kubernetes.io/docs/concepts/overview/working-with-objects/common-labels/). + +This version needs to change an immutable field, which is not supported by +Kubernetes and Helm, see [this issue](https://github.com/helm/helm/issues/7350) +for more details. +So you will have to delete your `Service`, `Deployment` or `DaemonSet` in +order to be able to upgrade. + +You may also upgrade by deploying another Traefik to a different namespace and +removing after your first Traefik. + +Alternatively, since version 20.3.0 of this chart, you may set `instanceLabelOverride` to the previous value of that label. +This will override the new `Release.Name-Release.Namespace` pattern to avoid any (longer) downtime. + +## Contributing + +If you want to contribute to this chart, please read the [Contributing Guide](./CONTRIBUTING.md). + +Thanks to all the people who have already contributed! + +
+ + diff --git a/charts/traefik/traefik/31.1.0/VALUES.md b/charts/traefik/traefik/31.1.0/VALUES.md new file mode 100644 index 000000000..b09e144c5 --- /dev/null +++ b/charts/traefik/traefik/31.1.0/VALUES.md @@ -0,0 +1,316 @@ +# traefik + +![Version: 31.1.0](https://img.shields.io/badge/Version-31.1.0-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: v3.1.3](https://img.shields.io/badge/AppVersion-v3.1.3-informational?style=flat-square) + +A Traefik based Kubernetes ingress controller + +**Homepage:** + +## Maintainers + +| Name | Email | Url | +| ---- | ------ | --- | +| mloiseleur | | | +| charlie-haley | | | +| darkweaver87 | | | +| jnoordsij | | | + +## Source Code + +* +* + +## Requirements + +Kubernetes: `>=1.22.0-0` + +## Values + +| Key | Type | Default | Description | +|-----|------|---------|-------------| +| additionalArguments | list | `[]` | Additional arguments to be passed at Traefik's binary See [CLI Reference](https://docs.traefik.io/reference/static-configuration/cli/) Use curly braces to pass values: `helm install --set="additionalArguments={--providers.kubernetesingress.ingressclass=traefik-internal,--log.level=DEBUG}"` | +| additionalVolumeMounts | list | `[]` | Additional volumeMounts to add to the Traefik container | +| affinity | object | `{}` | on nodes where no other traefik pods are scheduled. It should be used when hostNetwork: true to prevent port conflicts | +| autoscaling.enabled | bool | `false` | Create HorizontalPodAutoscaler object. See EXAMPLES.md for more details. | +| certResolvers | object | `{}` | Certificates resolvers configuration. Ref: https://doc.traefik.io/traefik/https/acme/#certificate-resolvers See EXAMPLES.md for more details. | +| commonLabels | object | `{}` | Add additional label to all resources | +| core.defaultRuleSyntax | string | `""` | Can be used to use globally v2 router syntax See https://doc.traefik.io/traefik/v3.0/migration/v2-to-v3/#new-v3-syntax-notable-changes | +| deployment.additionalContainers | list | `[]` | Additional containers (e.g. for metric offloading sidecars) | +| deployment.additionalVolumes | list | `[]` | Additional volumes available for use with initContainers and additionalContainers | +| deployment.annotations | object | `{}` | Additional deployment annotations (e.g. for jaeger-operator sidecar injection) | +| deployment.dnsConfig | object | `{}` | Custom pod [DNS config](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.30/#poddnsconfig-v1-core) | +| deployment.dnsPolicy | string | `""` | Custom pod DNS policy. Apply if `hostNetwork: true` | +| deployment.enabled | bool | `true` | Enable deployment | +| deployment.healthchecksHost | string | `""` | | +| deployment.healthchecksPort | string | `nil` | | +| deployment.healthchecksScheme | string | `nil` | | +| deployment.hostAliases | list | `[]` | Custom [host aliases](https://kubernetes.io/docs/tasks/network/customize-hosts-file-for-pods/) | +| deployment.imagePullSecrets | list | `[]` | Pull secret for fetching traefik container image | +| deployment.initContainers | list | `[]` | Additional initContainers (e.g. for setting file permission as shown below) | +| deployment.kind | string | `"Deployment"` | Deployment or DaemonSet | +| deployment.labels | object | `{}` | Additional deployment labels (e.g. for filtering deployment by custom labels) | +| deployment.lifecycle | object | `{}` | Pod lifecycle actions | +| deployment.livenessPath | string | `""` | Override the liveness path. Default: /ping | +| deployment.minReadySeconds | int | `0` | The minimum number of seconds Traefik needs to be up and running before the DaemonSet/Deployment controller considers it available | +| deployment.podAnnotations | object | `{}` | Additional pod annotations (e.g. for mesh injection or prometheus scraping) It supports templating. One can set it with values like traefik/name: '{{ template "traefik.name" . }}' | +| deployment.podLabels | object | `{}` | Additional Pod labels (e.g. for filtering Pod by custom labels) | +| deployment.readinessPath | string | `""` | | +| deployment.replicas | int | `1` | Number of pods of the deployment (only applies when kind == Deployment) | +| deployment.revisionHistoryLimit | string | `nil` | Number of old history to retain to allow rollback (If not set, default Kubernetes value is set to 10) | +| deployment.runtimeClassName | string | `""` | Set a runtimeClassName on pod | +| deployment.shareProcessNamespace | bool | `false` | Use process namespace sharing | +| deployment.terminationGracePeriodSeconds | int | `60` | Amount of time (in seconds) before Kubernetes will send the SIGKILL signal if Traefik does not shut down | +| env | list | See _values.yaml_ | Environment variables to be passed to Traefik's binary | +| envFrom | list | `[]` | Environment variables to be passed to Traefik's binary from configMaps or secrets | +| experimental.kubernetesGateway.enabled | bool | `false` | Enable traefik experimental GatewayClass CRD | +| experimental.plugins | object | `{}` | Enable traefik experimental plugins | +| extraObjects | list | `[]` | Extra objects to deploy (value evaluated as a template) In some cases, it can avoid the need for additional, extended or adhoc deployments. See #595 for more details and traefik/tests/values/extra.yaml for example. | +| gateway.annotations | object | `{}` | Additional gateway annotations (e.g. for cert-manager.io/issuer) | +| gateway.enabled | bool | `true` | When providers.kubernetesGateway.enabled, deploy a default gateway | +| gateway.listeners | object | `{"web":{"hostname":"","namespacePolicy":null,"port":8000,"protocol":"HTTP"}}` | Define listeners | +| gateway.listeners.web.hostname | string | `""` | Optional hostname. See [Hostname](https://gateway-api.sigs.k8s.io/reference/spec/#gateway.networking.k8s.io/v1.Hostname) | +| gateway.listeners.web.namespacePolicy | string | `nil` | Routes are restricted to namespace of the gateway [by default](https://gateway-api.sigs.k8s.io/reference/spec/#gateway.networking.k8s.io/v1.FromNamespaces | +| gateway.listeners.web.port | int | `8000` | Port is the network port. Multiple listeners may use the same port, subject to the Listener compatibility rules. The port must match a port declared in ports section. | +| gateway.name | string | `""` | Set a custom name to gateway | +| gateway.namespace | string | `""` | By default, Gateway is created in the same `Namespace` than Traefik. | +| gatewayClass.enabled | bool | `true` | When providers.kubernetesGateway.enabled and gateway.enabled, deploy a default gatewayClass | +| gatewayClass.labels | object | `{}` | Additional gatewayClass labels (e.g. for filtering gateway objects by custom labels) | +| gatewayClass.name | string | `""` | Set a custom name to GatewayClass | +| globalArguments | list | `["--global.checknewversion","--global.sendanonymoususage"]` | Global command arguments to be passed to all traefik's pods | +| hostNetwork | bool | `false` | If hostNetwork is true, runs traefik in the host network namespace To prevent unschedulabel pods due to port collisions, if hostNetwork=true and replicas>1, a pod anti-affinity is recommended and will be set if the affinity is left as default. | +| hub.apimanagement.admission.listenAddr | string | `""` | WebHook admission server listen address. Default: "0.0.0.0:9943". | +| hub.apimanagement.admission.secretName | string | `""` | Certificate of the WebHook admission server. Default: "hub-agent-cert". | +| hub.apimanagement.enabled | bool | `false` | Set to true in order to enable API Management. Requires a valid license token. | +| hub.ratelimit.redis.cluster | string | `nil` | Enable Redis Cluster. Default: true. | +| hub.ratelimit.redis.database | string | `nil` | Database used to store information. Default: "0". | +| hub.ratelimit.redis.endpoints | string | `""` | Endpoints of the Redis instances to connect to. Default: "". | +| hub.ratelimit.redis.password | string | `""` | The password to use when connecting to Redis endpoints. Default: "". | +| hub.ratelimit.redis.sentinel.masterset | string | `""` | Name of the set of main nodes to use for main selection. Required when using Sentinel. Default: "". | +| hub.ratelimit.redis.sentinel.password | string | `""` | Password to use for sentinel authentication (can be different from endpoint password). Default: "". | +| hub.ratelimit.redis.sentinel.username | string | `""` | Username to use for sentinel authentication (can be different from endpoint username). Default: "". | +| hub.ratelimit.redis.timeout | string | `""` | Timeout applied on connection with redis. Default: "0s". | +| hub.ratelimit.redis.tls.ca | string | `""` | Path to the certificate authority used for the secured connection. | +| hub.ratelimit.redis.tls.cert | string | `""` | Path to the public certificate used for the secure connection. | +| hub.ratelimit.redis.tls.insecureSkipVerify | bool | `false` | When insecureSkipVerify is set to true, the TLS connection accepts any certificate presented by the server. Default: false. | +| hub.ratelimit.redis.tls.key | string | `""` | Path to the private key used for the secure connection. | +| hub.ratelimit.redis.username | string | `""` | The username to use when connecting to Redis endpoints. Default: "". | +| hub.sendlogs | string | `nil` | | +| hub.token | string | `""` | Name of `Secret` with key 'token' set to a valid license token. It enables API Gateway. | +| image.pullPolicy | string | `"IfNotPresent"` | Traefik image pull policy | +| image.registry | string | `"docker.io"` | Traefik image host registry | +| image.repository | string | `"traefik"` | Traefik image repository | +| image.tag | string | `nil` | defaults to appVersion | +| ingressClass | object | `{"enabled":true,"isDefaultClass":true,"name":""}` | Create a default IngressClass for Traefik | +| ingressRoute.dashboard.annotations | object | `{}` | Additional ingressRoute annotations (e.g. for kubernetes.io/ingress.class) | +| ingressRoute.dashboard.enabled | bool | `false` | Create an IngressRoute for the dashboard | +| ingressRoute.dashboard.entryPoints | list | `["traefik"]` | Specify the allowed entrypoints to use for the dashboard ingress route, (e.g. traefik, web, websecure). By default, it's using traefik entrypoint, which is not exposed. /!\ Do not expose your dashboard without any protection over the internet /!\ | +| ingressRoute.dashboard.labels | object | `{}` | Additional ingressRoute labels (e.g. for filtering IngressRoute by custom labels) | +| ingressRoute.dashboard.matchRule | string | `"PathPrefix(`/dashboard`) || PathPrefix(`/api`)"` | The router match rule used for the dashboard ingressRoute | +| ingressRoute.dashboard.middlewares | list | `[]` | Additional ingressRoute middlewares (e.g. for authentication) | +| ingressRoute.dashboard.services | list | `[{"kind":"TraefikService","name":"api@internal"}]` | The internal service used for the dashboard ingressRoute | +| ingressRoute.dashboard.tls | object | `{}` | TLS options (e.g. secret containing certificate) | +| ingressRoute.healthcheck.annotations | object | `{}` | Additional ingressRoute annotations (e.g. for kubernetes.io/ingress.class) | +| ingressRoute.healthcheck.enabled | bool | `false` | Create an IngressRoute for the healthcheck probe | +| ingressRoute.healthcheck.entryPoints | list | `["traefik"]` | Specify the allowed entrypoints to use for the healthcheck ingress route, (e.g. traefik, web, websecure). By default, it's using traefik entrypoint, which is not exposed. | +| ingressRoute.healthcheck.labels | object | `{}` | Additional ingressRoute labels (e.g. for filtering IngressRoute by custom labels) | +| ingressRoute.healthcheck.matchRule | string | `"PathPrefix(`/ping`)"` | The router match rule used for the healthcheck ingressRoute | +| ingressRoute.healthcheck.middlewares | list | `[]` | Additional ingressRoute middlewares (e.g. for authentication) | +| ingressRoute.healthcheck.services | list | `[{"kind":"TraefikService","name":"ping@internal"}]` | The internal service used for the healthcheck ingressRoute | +| ingressRoute.healthcheck.tls | object | `{}` | TLS options (e.g. secret containing certificate) | +| instanceLabelOverride | string | `""` | | +| livenessProbe.failureThreshold | int | `3` | The number of consecutive failures allowed before considering the probe as failed. | +| livenessProbe.initialDelaySeconds | int | `2` | The number of seconds to wait before starting the first probe. | +| livenessProbe.periodSeconds | int | `10` | The number of seconds to wait between consecutive probes. | +| livenessProbe.successThreshold | int | `1` | The minimum consecutive successes required to consider the probe successful. | +| livenessProbe.timeoutSeconds | int | `2` | The number of seconds to wait for a probe response before considering it as failed. | +| logs.access.addInternals | bool | `false` | Enables accessLogs for internal resources. Default: false. | +| logs.access.bufferingSize | string | `nil` | Set [bufferingSize](https://doc.traefik.io/traefik/observability/access-logs/#bufferingsize) | +| logs.access.enabled | bool | `false` | To enable access logs | +| logs.access.fields.general.defaultmode | string | `"keep"` | Set default mode for fields.names | +| logs.access.fields.general.names | object | `{}` | Names of the fields to limit. | +| logs.access.fields.headers | object | `{"defaultmode":"drop","names":{}}` | [Limit logged fields or headers](https://doc.traefik.io/traefik/observability/access-logs/#limiting-the-fieldsincluding-headers) | +| logs.access.fields.headers.defaultmode | string | `"drop"` | Set default mode for fields.headers | +| logs.access.filters | object | `{}` | Set [filtering](https://docs.traefik.io/observability/access-logs/#filtering) | +| logs.access.format | string | `nil` | Set [access log format](https://doc.traefik.io/traefik/observability/access-logs/#format) | +| logs.access.minduration | string | `""` | | +| logs.access.retryattempts | bool | `false` | | +| logs.access.statuscodes | string | `""` | | +| logs.general.filePath | string | `""` | To write the logs into a log file, use the filePath option. | +| logs.general.format | string | `nil` | Set [logs format](https://doc.traefik.io/traefik/observability/logs/#format) | +| logs.general.level | string | `"INFO"` | Alternative logging levels are DEBUG, PANIC, FATAL, ERROR, WARN, and INFO. | +| logs.general.noColor | bool | `false` | When set to true and format is common, it disables the colorized output. | +| metrics.addInternals | bool | `false` | | +| metrics.otlp.addEntryPointsLabels | string | `nil` | Enable metrics on entry points. Default: true | +| metrics.otlp.addRoutersLabels | string | `nil` | Enable metrics on routers. Default: false | +| metrics.otlp.addServicesLabels | string | `nil` | Enable metrics on services. Default: true | +| metrics.otlp.enabled | bool | `false` | Set to true in order to enable the OpenTelemetry metrics | +| metrics.otlp.explicitBoundaries | list | `[]` | Explicit boundaries for Histogram data points. Default: [.005, .01, .025, .05, .1, .25, .5, 1, 2.5, 5, 10] | +| metrics.otlp.grpc.enabled | bool | `false` | Set to true in order to send metrics to the OpenTelemetry Collector using gRPC | +| metrics.otlp.grpc.endpoint | string | `""` | Format: ://:. Default: http://localhost:4318/v1/metrics | +| metrics.otlp.grpc.insecure | bool | `false` | Allows reporter to send metrics to the OpenTelemetry Collector without using a secured protocol. | +| metrics.otlp.grpc.tls.ca | string | `""` | The path to the certificate authority, it defaults to the system bundle. | +| metrics.otlp.grpc.tls.cert | string | `""` | The path to the public certificate. When using this option, setting the key option is required. | +| metrics.otlp.grpc.tls.insecureSkipVerify | bool | `false` | When set to true, the TLS connection accepts any certificate presented by the server regardless of the hostnames it covers. | +| metrics.otlp.grpc.tls.key | string | `""` | The path to the private key. When using this option, setting the cert option is required. | +| metrics.otlp.http.enabled | bool | `false` | Set to true in order to send metrics to the OpenTelemetry Collector using HTTP. | +| metrics.otlp.http.endpoint | string | `""` | Format: ://:. Default: http://localhost:4318/v1/metrics | +| metrics.otlp.http.headers | object | `{}` | Additional headers sent with metrics by the reporter to the OpenTelemetry Collector. | +| metrics.otlp.http.tls.ca | string | `""` | The path to the certificate authority, it defaults to the system bundle. | +| metrics.otlp.http.tls.cert | string | `""` | The path to the public certificate. When using this option, setting the key option is required. | +| metrics.otlp.http.tls.insecureSkipVerify | string | `nil` | When set to true, the TLS connection accepts any certificate presented by the server regardless of the hostnames it covers. | +| metrics.otlp.http.tls.key | string | `""` | The path to the private key. When using this option, setting the cert option is required. | +| metrics.otlp.pushInterval | string | `""` | Interval at which metrics are sent to the OpenTelemetry Collector. Default: 10s | +| metrics.prometheus.addEntryPointsLabels | string | `nil` | | +| metrics.prometheus.addRoutersLabels | string | `nil` | | +| metrics.prometheus.addServicesLabels | string | `nil` | | +| metrics.prometheus.buckets | string | `""` | | +| metrics.prometheus.disableAPICheck | string | `nil` | When set to true, it won't check if Prometheus Operator CRDs are deployed | +| metrics.prometheus.entryPoint | string | `"metrics"` | Entry point used to expose metrics. | +| metrics.prometheus.manualRouting | bool | `false` | | +| metrics.prometheus.prometheusRule.additionalLabels | object | `{}` | | +| metrics.prometheus.prometheusRule.enabled | bool | `false` | Enable optional CR for Prometheus Operator. See EXAMPLES.md for more details. | +| metrics.prometheus.prometheusRule.namespace | string | `""` | | +| metrics.prometheus.service.annotations | object | `{}` | | +| metrics.prometheus.service.enabled | bool | `false` | Create a dedicated metrics service to use with ServiceMonitor | +| metrics.prometheus.service.labels | object | `{}` | | +| metrics.prometheus.serviceMonitor.additionalLabels | object | `{}` | | +| metrics.prometheus.serviceMonitor.enableHttp2 | bool | `false` | | +| metrics.prometheus.serviceMonitor.enabled | bool | `false` | Enable optional CR for Prometheus Operator. See EXAMPLES.md for more details. | +| metrics.prometheus.serviceMonitor.followRedirects | bool | `false` | | +| metrics.prometheus.serviceMonitor.honorLabels | bool | `false` | | +| metrics.prometheus.serviceMonitor.honorTimestamps | bool | `false` | | +| metrics.prometheus.serviceMonitor.interval | string | `""` | | +| metrics.prometheus.serviceMonitor.jobLabel | string | `""` | | +| metrics.prometheus.serviceMonitor.metricRelabelings | list | `[]` | | +| metrics.prometheus.serviceMonitor.namespace | string | `""` | | +| metrics.prometheus.serviceMonitor.namespaceSelector | object | `{}` | | +| metrics.prometheus.serviceMonitor.relabelings | list | `[]` | | +| metrics.prometheus.serviceMonitor.scrapeTimeout | string | `""` | | +| namespaceOverride | string | `""` | This field override the default Release Namespace for Helm. It will not affect optional CRDs such as `ServiceMonitor` and `PrometheusRules` | +| nodeSelector | object | `{}` | nodeSelector is the simplest recommended form of node selection constraint. | +| persistence.accessMode | string | `"ReadWriteOnce"` | | +| persistence.annotations | object | `{}` | | +| persistence.enabled | bool | `false` | Enable persistence using Persistent Volume Claims ref: http://kubernetes.io/docs/user-guide/persistent-volumes/ It can be used to store TLS certificates, see `storage` in certResolvers | +| persistence.existingClaim | string | `""` | | +| persistence.name | string | `"data"` | | +| persistence.path | string | `"/data"` | | +| persistence.size | string | `"128Mi"` | | +| persistence.storageClass | string | `""` | | +| persistence.subPath | string | `""` | Only mount a subpath of the Volume into the pod | +| persistence.volumeName | string | `""` | | +| podDisruptionBudget | object | `{"enabled":false,"maxUnavailable":null,"minAvailable":null}` | [Pod Disruption Budget](https://kubernetes.io/docs/reference/kubernetes-api/policy-resources/pod-disruption-budget-v1/) | +| podSecurityContext | object | See _values.yaml_ | [Pod Security Context](https://kubernetes.io/docs/reference/kubernetes-api/workload-resources/pod-v1/#security-context) | +| podSecurityPolicy | object | `{"enabled":false}` | Enable to create a PodSecurityPolicy and assign it to the Service Account via RoleBinding or ClusterRoleBinding | +| ports.metrics.expose | object | `{"default":false}` | You may not want to expose the metrics port on production deployments. If you want to access it from outside your cluster, use `kubectl port-forward` or create a secure ingress | +| ports.metrics.exposedPort | int | `9100` | The exposed port for this service | +| ports.metrics.port | int | `9100` | When using hostNetwork, use another port to avoid conflict with node exporter: https://github.com/prometheus/prometheus/wiki/Default-port-allocations | +| ports.metrics.protocol | string | `"TCP"` | The port protocol (TCP/UDP) | +| ports.traefik.expose | object | `{"default":false}` | You SHOULD NOT expose the traefik port on production deployments. If you want to access it from outside your cluster, use `kubectl port-forward` or create a secure ingress | +| ports.traefik.exposedPort | int | `9000` | The exposed port for this service | +| ports.traefik.hostIP | string | `nil` | Use hostIP if set. If not set, Kubernetes will default to 0.0.0.0, which means it's listening on all your interfaces and all your IPs. You may want to set this value if you need traefik to listen on specific interface only. | +| ports.traefik.hostPort | string | `nil` | Use hostPort if set. | +| ports.traefik.port | int | `9000` | | +| ports.traefik.protocol | string | `"TCP"` | The port protocol (TCP/UDP) | +| ports.web.expose.default | bool | `true` | | +| ports.web.exposedPort | int | `80` | | +| ports.web.forwardedHeaders.insecure | bool | `false` | | +| ports.web.forwardedHeaders.trustedIPs | list | `[]` | Trust forwarded headers information (X-Forwarded-*). | +| ports.web.nodePort | string | `nil` | See [upstream documentation](https://kubernetes.io/docs/concepts/services-networking/service/#type-nodeport) | +| ports.web.port | int | `8000` | | +| ports.web.protocol | string | `"TCP"` | | +| ports.web.proxyProtocol.insecure | bool | `false` | | +| ports.web.proxyProtocol.trustedIPs | list | `[]` | Enable the Proxy Protocol header parsing for the entry point | +| ports.web.redirectTo | object | `{}` | | +| ports.web.targetPort | string | `nil` | | +| ports.web.transport | object | `{"keepAliveMaxRequests":null,"keepAliveMaxTime":null,"lifeCycle":{"graceTimeOut":null,"requestAcceptGraceTimeout":null},"respondingTimeouts":{"idleTimeout":null,"readTimeout":null,"writeTimeout":null}}` | Set transport settings for the entrypoint; see also https://doc.traefik.io/traefik/routing/entrypoints/#transport | +| ports.websecure.allowACMEByPass | bool | `false` | See [upstream documentation](https://doc.traefik.io/traefik/routing/entrypoints/#allowacmebypass) | +| ports.websecure.appProtocol | string | `nil` | See [upstream documentation](https://kubernetes.io/docs/concepts/services-networking/service/#application-protocol) | +| ports.websecure.containerPort | string | `nil` | | +| ports.websecure.expose.default | bool | `true` | | +| ports.websecure.exposedPort | int | `443` | | +| ports.websecure.forwardedHeaders.insecure | bool | `false` | | +| ports.websecure.forwardedHeaders.trustedIPs | list | `[]` | Trust forwarded headers information (X-Forwarded-*). | +| ports.websecure.hostPort | string | `nil` | | +| ports.websecure.http3.advertisedPort | string | `nil` | | +| ports.websecure.http3.enabled | bool | `false` | | +| ports.websecure.middlewares | list | `[]` | /!\ It introduces here a link between your static configuration and your dynamic configuration /!\ It follows the provider naming convention: https://doc.traefik.io/traefik/providers/overview/#provider-namespace - namespace-name1@kubernetescrd - namespace-name2@kubernetescrd | +| ports.websecure.nodePort | string | `nil` | See [upstream documentation](https://kubernetes.io/docs/concepts/services-networking/service/#type-nodeport) | +| ports.websecure.port | int | `8443` | | +| ports.websecure.protocol | string | `"TCP"` | | +| ports.websecure.proxyProtocol.insecure | bool | `false` | | +| ports.websecure.proxyProtocol.trustedIPs | list | `[]` | Enable the Proxy Protocol header parsing for the entry point | +| ports.websecure.targetPort | string | `nil` | | +| ports.websecure.tls | object | `{"certResolver":"","domains":[],"enabled":true,"options":""}` | See [upstream documentation](https://doc.traefik.io/traefik/routing/entrypoints/#tls) | +| ports.websecure.transport | object | `{"keepAliveMaxRequests":null,"keepAliveMaxTime":null,"lifeCycle":{"graceTimeOut":null,"requestAcceptGraceTimeout":null},"respondingTimeouts":{"idleTimeout":null,"readTimeout":null,"writeTimeout":null}}` | See [upstream documentation](https://doc.traefik.io/traefik/routing/entrypoints/#transport) | +| priorityClassName | string | `""` | [Pod Priority and Preemption](https://kubernetes.io/docs/concepts/scheduling-eviction/pod-priority-preemption/) | +| providers.file.content | string | `""` | File content (YAML format, go template supported) (see https://doc.traefik.io/traefik/providers/file/) | +| providers.file.enabled | bool | `false` | Create a file provider | +| providers.file.watch | bool | `true` | Allows Traefik to automatically watch for file changes | +| providers.kubernetesCRD.allowCrossNamespace | bool | `false` | Allows IngressRoute to reference resources in namespace other than theirs | +| providers.kubernetesCRD.allowEmptyServices | bool | `true` | Allows to return 503 when there is no endpoints available | +| providers.kubernetesCRD.allowExternalNameServices | bool | `false` | Allows to reference ExternalName services in IngressRoute | +| providers.kubernetesCRD.enabled | bool | `true` | Load Kubernetes IngressRoute provider | +| providers.kubernetesCRD.ingressClass | string | `""` | When the parameter is set, only resources containing an annotation with the same value are processed. Otherwise, resources missing the annotation, having an empty value, or the value traefik are processed. It will also set required annotation on Dashboard and Healthcheck IngressRoute when enabled. | +| providers.kubernetesCRD.namespaces | list | `[]` | Array of namespaces to watch. If left empty, Traefik watches all namespaces. | +| providers.kubernetesCRD.nativeLBByDefault | bool | `false` | Defines whether to use Native Kubernetes load-balancing mode by default. | +| providers.kubernetesGateway.enabled | bool | `false` | Enable Traefik Gateway provider for Gateway API | +| providers.kubernetesGateway.experimentalChannel | bool | `false` | Toggles support for the Experimental Channel resources (Gateway API release channels documentation). This option currently enables support for TCPRoute and TLSRoute. | +| providers.kubernetesGateway.labelselector | string | `""` | A label selector can be defined to filter on specific GatewayClass objects only. | +| providers.kubernetesGateway.namespaces | list | `[]` | Array of namespaces to watch. If left empty, Traefik watches all namespaces. | +| providers.kubernetesIngress.allowEmptyServices | bool | `true` | Allows to return 503 when there is no endpoints available | +| providers.kubernetesIngress.allowExternalNameServices | bool | `false` | Allows to reference ExternalName services in Ingress | +| providers.kubernetesIngress.enabled | bool | `true` | Load Kubernetes Ingress provider | +| providers.kubernetesIngress.ingressClass | string | `nil` | When ingressClass is set, only Ingresses containing an annotation with the same value are processed. Otherwise, Ingresses missing the annotation, having an empty value, or the value traefik are processed. | +| providers.kubernetesIngress.namespaces | list | `[]` | Array of namespaces to watch. If left empty, Traefik watches all namespaces. | +| providers.kubernetesIngress.nativeLBByDefault | bool | `false` | Defines whether to use Native Kubernetes load-balancing mode by default. | +| providers.kubernetesIngress.publishedService.enabled | bool | `false` | | +| rbac | object | `{"aggregateTo":[],"enabled":true,"namespaced":false,"secretResourceNames":[]}` | Whether Role Based Access Control objects like roles and rolebindings should be created | +| readinessProbe.failureThreshold | int | `1` | The number of consecutive failures allowed before considering the probe as failed. | +| readinessProbe.initialDelaySeconds | int | `2` | The number of seconds to wait before starting the first probe. | +| readinessProbe.periodSeconds | int | `10` | The number of seconds to wait between consecutive probes. | +| readinessProbe.successThreshold | int | `1` | The minimum consecutive successes required to consider the probe successful. | +| readinessProbe.timeoutSeconds | int | `2` | The number of seconds to wait for a probe response before considering it as failed. | +| resources | object | `{}` | [Resources](https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/) for `traefik` container. | +| securityContext | object | See _values.yaml_ | [SecurityContext](https://kubernetes.io/docs/reference/kubernetes-api/workload-resources/pod-v1/#security-context-1) | +| service.additionalServices | object | `{}` | | +| service.annotations | object | `{}` | Additional annotations applied to both TCP and UDP services (e.g. for cloud provider specific config) | +| service.annotationsTCP | object | `{}` | Additional annotations for TCP service only | +| service.annotationsUDP | object | `{}` | Additional annotations for UDP service only | +| service.enabled | bool | `true` | | +| service.externalIPs | list | `[]` | | +| service.labels | object | `{}` | Additional service labels (e.g. for filtering Service by custom labels) | +| service.loadBalancerSourceRanges | list | `[]` | | +| service.single | bool | `true` | | +| service.spec | object | `{}` | Cannot contain type, selector or ports entries. | +| service.type | string | `"LoadBalancer"` | | +| serviceAccount | object | `{"name":""}` | The service account the pods will use to interact with the Kubernetes API | +| serviceAccountAnnotations | object | `{}` | Additional serviceAccount annotations (e.g. for oidc authentication) | +| startupProbe | object | `{}` | Define [Startup Probe](https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/#define-startup-probes) | +| tlsOptions | object | `{}` | TLS Options are created as [TLSOption CRDs](https://doc.traefik.io/traefik/https/tls/#tls-options) When using `labelSelector`, you'll need to set labels on tlsOption accordingly. See EXAMPLE.md for details. | +| tlsStore | object | `{}` | TLS Store are created as [TLSStore CRDs](https://doc.traefik.io/traefik/https/tls/#default-certificate). This is useful if you want to set a default certificate. See EXAMPLE.md for details. | +| tolerations | list | `[]` | Tolerations allow the scheduler to schedule pods with matching taints. | +| topologySpreadConstraints | list | `[]` | You can use topology spread constraints to control how Pods are spread across your cluster among failure-domains. | +| tracing | object | `{"addInternals":false,"otlp":{"enabled":false,"grpc":{"enabled":false,"endpoint":"","insecure":false,"tls":{"ca":"","cert":"","insecureSkipVerify":false,"key":""}},"http":{"enabled":false,"endpoint":"","headers":{},"tls":{"ca":"","cert":"","insecureSkipVerify":false,"key":""}}}}` | https://doc.traefik.io/traefik/observability/tracing/overview/ | +| tracing.addInternals | bool | `false` | Enables tracing for internal resources. Default: false. | +| tracing.otlp.enabled | bool | `false` | See https://doc.traefik.io/traefik/v3.0/observability/tracing/opentelemetry/ | +| tracing.otlp.grpc.enabled | bool | `false` | Set to true in order to send metrics to the OpenTelemetry Collector using gRPC | +| tracing.otlp.grpc.endpoint | string | `""` | Format: ://:. Default: http://localhost:4318/v1/metrics | +| tracing.otlp.grpc.insecure | bool | `false` | Allows reporter to send metrics to the OpenTelemetry Collector without using a secured protocol. | +| tracing.otlp.grpc.tls.ca | string | `""` | The path to the certificate authority, it defaults to the system bundle. | +| tracing.otlp.grpc.tls.cert | string | `""` | The path to the public certificate. When using this option, setting the key option is required. | +| tracing.otlp.grpc.tls.insecureSkipVerify | bool | `false` | When set to true, the TLS connection accepts any certificate presented by the server regardless of the hostnames it covers. | +| tracing.otlp.grpc.tls.key | string | `""` | The path to the private key. When using this option, setting the cert option is required. | +| tracing.otlp.http.enabled | bool | `false` | Set to true in order to send metrics to the OpenTelemetry Collector using HTTP. | +| tracing.otlp.http.endpoint | string | `""` | Format: ://:. Default: http://localhost:4318/v1/metrics | +| tracing.otlp.http.headers | object | `{}` | Additional headers sent with metrics by the reporter to the OpenTelemetry Collector. | +| tracing.otlp.http.tls.ca | string | `""` | The path to the certificate authority, it defaults to the system bundle. | +| tracing.otlp.http.tls.cert | string | `""` | The path to the public certificate. When using this option, setting the key option is required. | +| tracing.otlp.http.tls.insecureSkipVerify | bool | `false` | When set to true, the TLS connection accepts any certificate presented by the server regardless of the hostnames it covers. | +| tracing.otlp.http.tls.key | string | `""` | The path to the private key. When using this option, setting the cert option is required. | +| updateStrategy.rollingUpdate.maxSurge | int | `1` | | +| updateStrategy.rollingUpdate.maxUnavailable | int | `0` | | +| updateStrategy.type | string | `"RollingUpdate"` | Customize updateStrategy: RollingUpdate or OnDelete | +| volumes | list | `[]` | Add volumes to the traefik pod. The volume name will be passed to tpl. This can be used to mount a cert pair or a configmap that holds a config.toml file. After the volume has been mounted, add the configs into traefik by using the `additionalArguments` list below, eg: `additionalArguments: - "--providers.file.filename=/config/dynamic.toml" - "--ping" - "--ping.entrypoint=web"` | + +---------------------------------------------- +Autogenerated from chart metadata using [helm-docs v1.14.2](https://github.com/norwoodj/helm-docs/releases/v1.14.2) diff --git a/charts/traefik/traefik/31.1.0/app-readme.md b/charts/traefik/traefik/31.1.0/app-readme.md new file mode 100644 index 000000000..7289af5d0 --- /dev/null +++ b/charts/traefik/traefik/31.1.0/app-readme.md @@ -0,0 +1,5 @@ +# Traefik Proxy + +[Traefik](https://traefik.io/) is a modern HTTP reverse proxy and load balancer made to deploy microservices with ease. + +This chart bootstraps Traefik version 2 as a Kubernetes ingress controller. diff --git a/charts/traefik/traefik/31.1.0/crds/gateway-standard-install-v1.1.0.yaml b/charts/traefik/traefik/31.1.0/crds/gateway-standard-install-v1.1.0.yaml new file mode 100644 index 000000000..fcb65e66f --- /dev/null +++ b/charts/traefik/traefik/31.1.0/crds/gateway-standard-install-v1.1.0.yaml @@ -0,0 +1,13478 @@ +# Copyright 2024 The Kubernetes Authors. +# +# 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. + +# +# Gateway API Standard channel install +# +--- +# +# config/crd/standard/gateway.networking.k8s.io_gatewayclasses.yaml +# +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + api-approved.kubernetes.io: https://github.com/kubernetes-sigs/gateway-api/pull/2997 + gateway.networking.k8s.io/bundle-version: v1.1.0 + gateway.networking.k8s.io/channel: standard + creationTimestamp: null + name: gatewayclasses.gateway.networking.k8s.io +spec: + group: gateway.networking.k8s.io + names: + categories: + - gateway-api + kind: GatewayClass + listKind: GatewayClassList + plural: gatewayclasses + shortNames: + - gc + singular: gatewayclass + scope: Cluster + versions: + - additionalPrinterColumns: + - jsonPath: .spec.controllerName + name: Controller + type: string + - jsonPath: .status.conditions[?(@.type=="Accepted")].status + name: Accepted + type: string + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + - jsonPath: .spec.description + name: Description + priority: 1 + type: string + name: v1 + schema: + openAPIV3Schema: + description: |- + GatewayClass describes a class of Gateways available to the user for creating + Gateway resources. + + + It is recommended that this resource be used as a template for Gateways. This + means that a Gateway is based on the state of the GatewayClass at the time it + was created and changes to the GatewayClass or associated parameters are not + propagated down to existing Gateways. This recommendation is intended to + limit the blast radius of changes to GatewayClass or associated parameters. + If implementations choose to propagate GatewayClass changes to existing + Gateways, that MUST be clearly documented by the implementation. + + + Whenever one or more Gateways are using a GatewayClass, implementations SHOULD + add the `gateway-exists-finalizer.gateway.networking.k8s.io` finalizer on the + associated GatewayClass. This ensures that a GatewayClass associated with a + Gateway is not deleted while in use. + + + GatewayClass is a Cluster level resource. + 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: Spec defines the desired state of GatewayClass. + properties: + controllerName: + description: |- + ControllerName is the name of the controller that is managing Gateways of + this class. The value of this field MUST be a domain prefixed path. + + + Example: "example.net/gateway-controller". + + + This field is not mutable and cannot be empty. + + + Support: Core + maxLength: 253 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*\/[A-Za-z0-9\/\-._~%!$&'()*+,;=:]+$ + type: string + x-kubernetes-validations: + - message: Value is immutable + rule: self == oldSelf + description: + description: Description helps describe a GatewayClass with more details. + maxLength: 64 + type: string + parametersRef: + description: |- + ParametersRef is a reference to a resource that contains the configuration + parameters corresponding to the GatewayClass. This is optional if the + controller does not require any additional configuration. + + + ParametersRef can reference a standard Kubernetes resource, i.e. ConfigMap, + or an implementation-specific custom resource. The resource can be + cluster-scoped or namespace-scoped. + + + If the referent cannot be found, the GatewayClass's "InvalidParameters" + status condition will be true. + + + A Gateway for this GatewayClass may provide its own `parametersRef`. When both are specified, + the merging behavior is implementation specific. + It is generally recommended that GatewayClass provides defaults that can be overridden by a Gateway. + + + Support: Implementation-specific + properties: + group: + description: Group is the group of the referent. + maxLength: 253 + pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + kind: + description: Kind is kind of the referent. + maxLength: 63 + minLength: 1 + pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ + type: string + name: + description: Name is the name of the referent. + maxLength: 253 + minLength: 1 + type: string + namespace: + description: |- + Namespace is the namespace of the referent. + This field is required when referring to a Namespace-scoped resource and + MUST be unset when referring to a Cluster-scoped resource. + maxLength: 63 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ + type: string + required: + - group + - kind + - name + type: object + required: + - controllerName + type: object + status: + default: + conditions: + - lastTransitionTime: "1970-01-01T00:00:00Z" + message: Waiting for controller + reason: Waiting + status: Unknown + type: Accepted + description: |- + Status defines the current state of GatewayClass. + + + Implementations MUST populate status on all GatewayClass resources which + specify their controller name. + properties: + conditions: + default: + - lastTransitionTime: "1970-01-01T00:00:00Z" + message: Waiting for controller + reason: Pending + status: Unknown + type: Accepted + description: |- + Conditions is the current status from the controller for + this GatewayClass. + + + Controllers should prefer to publish conditions using values + of GatewayClassConditionType for the type of each Condition. + 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 + maxItems: 8 + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + type: object + required: + - spec + type: object + served: true + storage: true + subresources: + status: {} + - additionalPrinterColumns: + - jsonPath: .spec.controllerName + name: Controller + type: string + - jsonPath: .status.conditions[?(@.type=="Accepted")].status + name: Accepted + type: string + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + - jsonPath: .spec.description + name: Description + priority: 1 + type: string + name: v1beta1 + schema: + openAPIV3Schema: + description: |- + GatewayClass describes a class of Gateways available to the user for creating + Gateway resources. + + + It is recommended that this resource be used as a template for Gateways. This + means that a Gateway is based on the state of the GatewayClass at the time it + was created and changes to the GatewayClass or associated parameters are not + propagated down to existing Gateways. This recommendation is intended to + limit the blast radius of changes to GatewayClass or associated parameters. + If implementations choose to propagate GatewayClass changes to existing + Gateways, that MUST be clearly documented by the implementation. + + + Whenever one or more Gateways are using a GatewayClass, implementations SHOULD + add the `gateway-exists-finalizer.gateway.networking.k8s.io` finalizer on the + associated GatewayClass. This ensures that a GatewayClass associated with a + Gateway is not deleted while in use. + + + GatewayClass is a Cluster level resource. + 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: Spec defines the desired state of GatewayClass. + properties: + controllerName: + description: |- + ControllerName is the name of the controller that is managing Gateways of + this class. The value of this field MUST be a domain prefixed path. + + + Example: "example.net/gateway-controller". + + + This field is not mutable and cannot be empty. + + + Support: Core + maxLength: 253 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*\/[A-Za-z0-9\/\-._~%!$&'()*+,;=:]+$ + type: string + x-kubernetes-validations: + - message: Value is immutable + rule: self == oldSelf + description: + description: Description helps describe a GatewayClass with more details. + maxLength: 64 + type: string + parametersRef: + description: |- + ParametersRef is a reference to a resource that contains the configuration + parameters corresponding to the GatewayClass. This is optional if the + controller does not require any additional configuration. + + + ParametersRef can reference a standard Kubernetes resource, i.e. ConfigMap, + or an implementation-specific custom resource. The resource can be + cluster-scoped or namespace-scoped. + + + If the referent cannot be found, the GatewayClass's "InvalidParameters" + status condition will be true. + + + A Gateway for this GatewayClass may provide its own `parametersRef`. When both are specified, + the merging behavior is implementation specific. + It is generally recommended that GatewayClass provides defaults that can be overridden by a Gateway. + + + Support: Implementation-specific + properties: + group: + description: Group is the group of the referent. + maxLength: 253 + pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + kind: + description: Kind is kind of the referent. + maxLength: 63 + minLength: 1 + pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ + type: string + name: + description: Name is the name of the referent. + maxLength: 253 + minLength: 1 + type: string + namespace: + description: |- + Namespace is the namespace of the referent. + This field is required when referring to a Namespace-scoped resource and + MUST be unset when referring to a Cluster-scoped resource. + maxLength: 63 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ + type: string + required: + - group + - kind + - name + type: object + required: + - controllerName + type: object + status: + default: + conditions: + - lastTransitionTime: "1970-01-01T00:00:00Z" + message: Waiting for controller + reason: Waiting + status: Unknown + type: Accepted + description: |- + Status defines the current state of GatewayClass. + + + Implementations MUST populate status on all GatewayClass resources which + specify their controller name. + properties: + conditions: + default: + - lastTransitionTime: "1970-01-01T00:00:00Z" + message: Waiting for controller + reason: Pending + status: Unknown + type: Accepted + description: |- + Conditions is the current status from the controller for + this GatewayClass. + + + Controllers should prefer to publish conditions using values + of GatewayClassConditionType for the type of each Condition. + 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 + maxItems: 8 + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + type: object + required: + - spec + type: object + served: true + storage: false + subresources: + status: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: null + storedVersions: null +--- +# +# config/crd/standard/gateway.networking.k8s.io_gateways.yaml +# +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + api-approved.kubernetes.io: https://github.com/kubernetes-sigs/gateway-api/pull/2997 + gateway.networking.k8s.io/bundle-version: v1.1.0 + gateway.networking.k8s.io/channel: standard + creationTimestamp: null + name: gateways.gateway.networking.k8s.io +spec: + group: gateway.networking.k8s.io + names: + categories: + - gateway-api + kind: Gateway + listKind: GatewayList + plural: gateways + shortNames: + - gtw + singular: gateway + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .spec.gatewayClassName + name: Class + type: string + - jsonPath: .status.addresses[*].value + name: Address + type: string + - jsonPath: .status.conditions[?(@.type=="Programmed")].status + name: Programmed + type: string + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v1 + schema: + openAPIV3Schema: + description: |- + Gateway represents an instance of a service-traffic handling infrastructure + by binding Listeners to a set of IP addresses. + 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: Spec defines the desired state of Gateway. + properties: + addresses: + description: |+ + Addresses requested for this Gateway. This is optional and behavior can + depend on the implementation. If a value is set in the spec and the + requested address is invalid or unavailable, the implementation MUST + indicate this in the associated entry in GatewayStatus.Addresses. + + + The Addresses field represents a request for the address(es) on the + "outside of the Gateway", that traffic bound for this Gateway will use. + This could be the IP address or hostname of an external load balancer or + other networking infrastructure, or some other address that traffic will + be sent to. + + + If no Addresses are specified, the implementation MAY schedule the + Gateway in an implementation-specific manner, assigning an appropriate + set of Addresses. + + + The implementation MUST bind all Listeners to every GatewayAddress that + it assigns to the Gateway and add a corresponding entry in + GatewayStatus.Addresses. + + + Support: Extended + + + items: + description: GatewayAddress describes an address that can be bound + to a Gateway. + oneOf: + - properties: + type: + enum: + - IPAddress + value: + anyOf: + - format: ipv4 + - format: ipv6 + - properties: + type: + not: + enum: + - IPAddress + properties: + type: + default: IPAddress + description: Type of the address. + maxLength: 253 + minLength: 1 + pattern: ^Hostname|IPAddress|NamedAddress|[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*\/[A-Za-z0-9\/\-._~%!$&'()*+,;=:]+$ + type: string + value: + description: |- + Value of the address. The validity of the values will depend + on the type and support by the controller. + + + Examples: `1.2.3.4`, `128::1`, `my-ip-address`. + maxLength: 253 + minLength: 1 + type: string + required: + - value + type: object + x-kubernetes-validations: + - message: Hostname value must only contain valid characters (matching + ^(\*\.)?[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$) + rule: 'self.type == ''Hostname'' ? self.value.matches(r"""^(\*\.)?[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$"""): + true' + maxItems: 16 + type: array + x-kubernetes-validations: + - message: IPAddress values must be unique + rule: 'self.all(a1, a1.type == ''IPAddress'' ? self.exists_one(a2, + a2.type == a1.type && a2.value == a1.value) : true )' + - message: Hostname values must be unique + rule: 'self.all(a1, a1.type == ''Hostname'' ? self.exists_one(a2, + a2.type == a1.type && a2.value == a1.value) : true )' + gatewayClassName: + description: |- + GatewayClassName used for this Gateway. This is the name of a + GatewayClass resource. + maxLength: 253 + minLength: 1 + type: string + listeners: + description: |- + Listeners associated with this Gateway. Listeners define + logical endpoints that are bound on this Gateway's addresses. + At least one Listener MUST be specified. + + + Each Listener in a set of Listeners (for example, in a single Gateway) + MUST be _distinct_, in that a traffic flow MUST be able to be assigned to + exactly one listener. (This section uses "set of Listeners" rather than + "Listeners in a single Gateway" because implementations MAY merge configuration + from multiple Gateways onto a single data plane, and these rules _also_ + apply in that case). + + + Practically, this means that each listener in a set MUST have a unique + combination of Port, Protocol, and, if supported by the protocol, Hostname. + + + Some combinations of port, protocol, and TLS settings are considered + Core support and MUST be supported by implementations based on their + targeted conformance profile: + + + HTTP Profile + + + 1. HTTPRoute, Port: 80, Protocol: HTTP + 2. HTTPRoute, Port: 443, Protocol: HTTPS, TLS Mode: Terminate, TLS keypair provided + + + TLS Profile + + + 1. TLSRoute, Port: 443, Protocol: TLS, TLS Mode: Passthrough + + + "Distinct" Listeners have the following property: + + + The implementation can match inbound requests to a single distinct + Listener. When multiple Listeners share values for fields (for + example, two Listeners with the same Port value), the implementation + can match requests to only one of the Listeners using other + Listener fields. + + + For example, the following Listener scenarios are distinct: + + + 1. Multiple Listeners with the same Port that all use the "HTTP" + Protocol that all have unique Hostname values. + 2. Multiple Listeners with the same Port that use either the "HTTPS" or + "TLS" Protocol that all have unique Hostname values. + 3. A mixture of "TCP" and "UDP" Protocol Listeners, where no Listener + with the same Protocol has the same Port value. + + + Some fields in the Listener struct have possible values that affect + whether the Listener is distinct. Hostname is particularly relevant + for HTTP or HTTPS protocols. + + + When using the Hostname value to select between same-Port, same-Protocol + Listeners, the Hostname value must be different on each Listener for the + Listener to be distinct. + + + When the Listeners are distinct based on Hostname, inbound request + hostnames MUST match from the most specific to least specific Hostname + values to choose the correct Listener and its associated set of Routes. + + + Exact matches must be processed before wildcard matches, and wildcard + matches must be processed before fallback (empty Hostname value) + matches. For example, `"foo.example.com"` takes precedence over + `"*.example.com"`, and `"*.example.com"` takes precedence over `""`. + + + Additionally, if there are multiple wildcard entries, more specific + wildcard entries must be processed before less specific wildcard entries. + For example, `"*.foo.example.com"` takes precedence over `"*.example.com"`. + The precise definition here is that the higher the number of dots in the + hostname to the right of the wildcard character, the higher the precedence. + + + The wildcard character will match any number of characters _and dots_ to + the left, however, so `"*.example.com"` will match both + `"foo.bar.example.com"` _and_ `"bar.example.com"`. + + + If a set of Listeners contains Listeners that are not distinct, then those + Listeners are Conflicted, and the implementation MUST set the "Conflicted" + condition in the Listener Status to "True". + + + Implementations MAY choose to accept a Gateway with some Conflicted + Listeners only if they only accept the partial Listener set that contains + no Conflicted Listeners. To put this another way, implementations may + accept a partial Listener set only if they throw out *all* the conflicting + Listeners. No picking one of the conflicting listeners as the winner. + This also means that the Gateway must have at least one non-conflicting + Listener in this case, otherwise it violates the requirement that at + least one Listener must be present. + + + The implementation MUST set a "ListenersNotValid" condition on the + Gateway Status when the Gateway contains Conflicted Listeners whether or + not they accept the Gateway. That Condition SHOULD clearly + indicate in the Message which Listeners are conflicted, and which are + Accepted. Additionally, the Listener status for those listeners SHOULD + indicate which Listeners are conflicted and not Accepted. + + + A Gateway's Listeners are considered "compatible" if: + + + 1. They are distinct. + 2. The implementation can serve them in compliance with the Addresses + requirement that all Listeners are available on all assigned + addresses. + + + Compatible combinations in Extended support are expected to vary across + implementations. A combination that is compatible for one implementation + may not be compatible for another. + + + For example, an implementation that cannot serve both TCP and UDP listeners + on the same address, or cannot mix HTTPS and generic TLS listens on the same port + would not consider those cases compatible, even though they are distinct. + + + Note that requests SHOULD match at most one Listener. For example, if + Listeners are defined for "foo.example.com" and "*.example.com", a + request to "foo.example.com" SHOULD only be routed using routes attached + to the "foo.example.com" Listener (and not the "*.example.com" Listener). + This concept is known as "Listener Isolation". Implementations that do + not support Listener Isolation MUST clearly document this. + + + Implementations MAY merge separate Gateways onto a single set of + Addresses if all Listeners across all Gateways are compatible. + + + Support: Core + items: + description: |- + Listener embodies the concept of a logical endpoint where a Gateway accepts + network connections. + properties: + allowedRoutes: + default: + namespaces: + from: Same + description: |- + AllowedRoutes defines the types of routes that MAY be attached to a + Listener and the trusted namespaces where those Route resources MAY be + present. + + + Although a client request may match multiple route rules, only one rule + may ultimately receive the request. Matching precedence MUST be + determined in order of the following criteria: + + + * The most specific match as defined by the Route type. + * The oldest Route based on creation timestamp. For example, a Route with + a creation timestamp of "2020-09-08 01:02:03" is given precedence over + a Route with a creation timestamp of "2020-09-08 01:02:04". + * If everything else is equivalent, the Route appearing first in + alphabetical order (namespace/name) should be given precedence. For + example, foo/bar is given precedence over foo/baz. + + + All valid rules within a Route attached to this Listener should be + implemented. Invalid Route rules can be ignored (sometimes that will mean + the full Route). If a Route rule transitions from valid to invalid, + support for that Route rule should be dropped to ensure consistency. For + example, even if a filter specified by a Route rule is invalid, the rest + of the rules within that Route should still be supported. + + + Support: Core + properties: + kinds: + description: |- + Kinds specifies the groups and kinds of Routes that are allowed to bind + to this Gateway Listener. When unspecified or empty, the kinds of Routes + selected are determined using the Listener protocol. + + + A RouteGroupKind MUST correspond to kinds of Routes that are compatible + with the application protocol specified in the Listener's Protocol field. + If an implementation does not support or recognize this resource type, it + MUST set the "ResolvedRefs" condition to False for this Listener with the + "InvalidRouteKinds" reason. + + + Support: Core + items: + description: RouteGroupKind indicates the group and kind + of a Route resource. + properties: + group: + default: gateway.networking.k8s.io + description: Group is the group of the Route. + maxLength: 253 + pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + kind: + description: Kind is the kind of the Route. + maxLength: 63 + minLength: 1 + pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ + type: string + required: + - kind + type: object + maxItems: 8 + type: array + namespaces: + default: + from: Same + description: |- + Namespaces indicates namespaces from which Routes may be attached to this + Listener. This is restricted to the namespace of this Gateway by default. + + + Support: Core + properties: + from: + default: Same + description: |- + From indicates where Routes will be selected for this Gateway. Possible + values are: + + + * All: Routes in all namespaces may be used by this Gateway. + * Selector: Routes in namespaces selected by the selector may be used by + this Gateway. + * Same: Only Routes in the same namespace may be used by this Gateway. + + + Support: Core + enum: + - All + - Selector + - Same + type: string + selector: + description: |- + Selector must be specified when From is set to "Selector". In that case, + only Routes in Namespaces matching this Selector will be selected by this + Gateway. This field is ignored for other values of "From". + + + Support: Core + 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 + type: object + type: object + hostname: + description: |- + Hostname specifies the virtual hostname to match for protocol types that + define this concept. When unspecified, all hostnames are matched. This + field is ignored for protocols that don't require hostname based + matching. + + + Implementations MUST apply Hostname matching appropriately for each of + the following protocols: + + + * TLS: The Listener Hostname MUST match the SNI. + * HTTP: The Listener Hostname MUST match the Host header of the request. + * HTTPS: The Listener Hostname SHOULD match at both the TLS and HTTP + protocol layers as described above. If an implementation does not + ensure that both the SNI and Host header match the Listener hostname, + it MUST clearly document that. + + + For HTTPRoute and TLSRoute resources, there is an interaction with the + `spec.hostnames` array. When both listener and route specify hostnames, + there MUST be an intersection between the values for a Route to be + accepted. For more information, refer to the Route specific Hostnames + documentation. + + + Hostnames that are prefixed with a wildcard label (`*.`) are interpreted + as a suffix match. That means that a match for `*.example.com` would match + both `test.example.com`, and `foo.test.example.com`, but not `example.com`. + + + Support: Core + maxLength: 253 + minLength: 1 + pattern: ^(\*\.)?[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + name: + description: |- + Name is the name of the Listener. This name MUST be unique within a + Gateway. + + + Support: Core + maxLength: 253 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + port: + description: |- + Port is the network port. Multiple listeners may use the + same port, subject to the Listener compatibility rules. + + + Support: Core + format: int32 + maximum: 65535 + minimum: 1 + type: integer + protocol: + description: |- + Protocol specifies the network protocol this listener expects to receive. + + + Support: Core + maxLength: 255 + minLength: 1 + pattern: ^[a-zA-Z0-9]([-a-zSA-Z0-9]*[a-zA-Z0-9])?$|[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*\/[A-Za-z0-9]+$ + type: string + tls: + description: |- + TLS is the TLS configuration for the Listener. This field is required if + the Protocol field is "HTTPS" or "TLS". It is invalid to set this field + if the Protocol field is "HTTP", "TCP", or "UDP". + + + The association of SNIs to Certificate defined in GatewayTLSConfig is + defined based on the Hostname field for this listener. + + + The GatewayClass MUST use the longest matching SNI out of all + available certificates for any TLS handshake. + + + Support: Core + properties: + certificateRefs: + description: |- + CertificateRefs contains a series of references to Kubernetes objects that + contains TLS certificates and private keys. These certificates are used to + establish a TLS handshake for requests that match the hostname of the + associated listener. + + + A single CertificateRef to a Kubernetes Secret has "Core" support. + Implementations MAY choose to support attaching multiple certificates to + a Listener, but this behavior is implementation-specific. + + + References to a resource in different namespace are invalid UNLESS there + is a ReferenceGrant in the target namespace that allows the certificate + to be attached. If a ReferenceGrant does not allow this reference, the + "ResolvedRefs" condition MUST be set to False for this listener with the + "RefNotPermitted" reason. + + + This field is required to have at least one element when the mode is set + to "Terminate" (default) and is optional otherwise. + + + CertificateRefs can reference to standard Kubernetes resources, i.e. + Secret, or implementation-specific custom resources. + + + Support: Core - A single reference to a Kubernetes Secret of type kubernetes.io/tls + + + Support: Implementation-specific (More than one reference or other resource types) + items: + description: |- + SecretObjectReference identifies an API object including its namespace, + defaulting to Secret. + + + The API object must be valid in the cluster; the Group and Kind must + be registered in the cluster for this reference to be valid. + + + References to objects with invalid Group and Kind are not valid, and must + be rejected by the implementation, with appropriate Conditions set + on the containing object. + properties: + group: + default: "" + description: |- + Group is the group of the referent. For example, "gateway.networking.k8s.io". + When unspecified or empty string, core API group is inferred. + maxLength: 253 + pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + kind: + default: Secret + description: Kind is kind of the referent. For example + "Secret". + maxLength: 63 + minLength: 1 + pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ + type: string + name: + description: Name is the name of the referent. + maxLength: 253 + minLength: 1 + type: string + namespace: + description: |- + Namespace is the namespace of the referenced object. When unspecified, the local + namespace is inferred. + + + Note that when a namespace different than the local namespace is specified, + a ReferenceGrant object is required in the referent namespace to allow that + namespace's owner to accept the reference. See the ReferenceGrant + documentation for details. + + + Support: Core + maxLength: 63 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ + type: string + required: + - name + type: object + maxItems: 64 + type: array + mode: + default: Terminate + description: |- + Mode defines the TLS behavior for the TLS session initiated by the client. + There are two possible modes: + + + - Terminate: The TLS session between the downstream client and the + Gateway is terminated at the Gateway. This mode requires certificates + to be specified in some way, such as populating the certificateRefs + field. + - Passthrough: The TLS session is NOT terminated by the Gateway. This + implies that the Gateway can't decipher the TLS stream except for + the ClientHello message of the TLS protocol. The certificateRefs field + is ignored in this mode. + + + Support: Core + enum: + - Terminate + - Passthrough + type: string + options: + additionalProperties: + description: |- + AnnotationValue is the value of an annotation in Gateway API. This is used + for validation of maps such as TLS options. This roughly matches Kubernetes + annotation validation, although the length validation in that case is based + on the entire size of the annotations struct. + maxLength: 4096 + minLength: 0 + type: string + description: |- + Options are a list of key/value pairs to enable extended TLS + configuration for each implementation. For example, configuring the + minimum TLS version or supported cipher suites. + + + A set of common keys MAY be defined by the API in the future. To avoid + any ambiguity, implementation-specific definitions MUST use + domain-prefixed names, such as `example.com/my-custom-option`. + Un-prefixed names are reserved for key names defined by Gateway API. + + + Support: Implementation-specific + maxProperties: 16 + type: object + type: object + x-kubernetes-validations: + - message: certificateRefs or options must be specified when + mode is Terminate + rule: 'self.mode == ''Terminate'' ? size(self.certificateRefs) + > 0 || size(self.options) > 0 : true' + required: + - name + - port + - protocol + type: object + maxItems: 64 + minItems: 1 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + x-kubernetes-validations: + - message: tls must not be specified for protocols ['HTTP', 'TCP', + 'UDP'] + rule: 'self.all(l, l.protocol in [''HTTP'', ''TCP'', ''UDP''] ? + !has(l.tls) : true)' + - message: tls mode must be Terminate for protocol HTTPS + rule: 'self.all(l, (l.protocol == ''HTTPS'' && has(l.tls)) ? (l.tls.mode + == '''' || l.tls.mode == ''Terminate'') : true)' + - message: hostname must not be specified for protocols ['TCP', 'UDP'] + rule: 'self.all(l, l.protocol in [''TCP'', ''UDP''] ? (!has(l.hostname) + || l.hostname == '''') : true)' + - message: Listener name must be unique within the Gateway + rule: self.all(l1, self.exists_one(l2, l1.name == l2.name)) + - message: Combination of port, protocol and hostname must be unique + for each listener + rule: 'self.all(l1, self.exists_one(l2, l1.port == l2.port && l1.protocol + == l2.protocol && (has(l1.hostname) && has(l2.hostname) ? l1.hostname + == l2.hostname : !has(l1.hostname) && !has(l2.hostname))))' + required: + - gatewayClassName + - listeners + type: object + status: + default: + conditions: + - lastTransitionTime: "1970-01-01T00:00:00Z" + message: Waiting for controller + reason: Pending + status: Unknown + type: Accepted + - lastTransitionTime: "1970-01-01T00:00:00Z" + message: Waiting for controller + reason: Pending + status: Unknown + type: Programmed + description: Status defines the current state of Gateway. + properties: + addresses: + description: |+ + Addresses lists the network addresses that have been bound to the + Gateway. + + + This list may differ from the addresses provided in the spec under some + conditions: + + + * no addresses are specified, all addresses are dynamically assigned + * a combination of specified and dynamic addresses are assigned + * a specified address was unusable (e.g. already in use) + + + items: + description: GatewayStatusAddress describes a network address that + is bound to a Gateway. + oneOf: + - properties: + type: + enum: + - IPAddress + value: + anyOf: + - format: ipv4 + - format: ipv6 + - properties: + type: + not: + enum: + - IPAddress + properties: + type: + default: IPAddress + description: Type of the address. + maxLength: 253 + minLength: 1 + pattern: ^Hostname|IPAddress|NamedAddress|[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*\/[A-Za-z0-9\/\-._~%!$&'()*+,;=:]+$ + type: string + value: + description: |- + Value of the address. The validity of the values will depend + on the type and support by the controller. + + + Examples: `1.2.3.4`, `128::1`, `my-ip-address`. + maxLength: 253 + minLength: 1 + type: string + required: + - value + type: object + x-kubernetes-validations: + - message: Hostname value must only contain valid characters (matching + ^(\*\.)?[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$) + rule: 'self.type == ''Hostname'' ? self.value.matches(r"""^(\*\.)?[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$"""): + true' + maxItems: 16 + type: array + conditions: + default: + - lastTransitionTime: "1970-01-01T00:00:00Z" + message: Waiting for controller + reason: Pending + status: Unknown + type: Accepted + - lastTransitionTime: "1970-01-01T00:00:00Z" + message: Waiting for controller + reason: Pending + status: Unknown + type: Programmed + description: |- + Conditions describe the current conditions of the Gateway. + + + Implementations should prefer to express Gateway conditions + using the `GatewayConditionType` and `GatewayConditionReason` + constants so that operators and tools can converge on a common + vocabulary to describe Gateway state. + + + Known condition types are: + + + * "Accepted" + * "Programmed" + * "Ready" + 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 + maxItems: 8 + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + listeners: + description: Listeners provide status for each unique listener port + defined in the Spec. + items: + description: ListenerStatus is the status associated with a Listener. + properties: + attachedRoutes: + description: |- + AttachedRoutes represents the total number of Routes that have been + successfully attached to this Listener. + + + Successful attachment of a Route to a Listener is based solely on the + combination of the AllowedRoutes field on the corresponding Listener + and the Route's ParentRefs field. A Route is successfully attached to + a Listener when it is selected by the Listener's AllowedRoutes field + AND the Route has a valid ParentRef selecting the whole Gateway + resource or a specific Listener as a parent resource (more detail on + attachment semantics can be found in the documentation on the various + Route kinds ParentRefs fields). Listener or Route status does not impact + successful attachment, i.e. the AttachedRoutes field count MUST be set + for Listeners with condition Accepted: false and MUST count successfully + attached Routes that may themselves have Accepted: false conditions. + + + Uses for this field include troubleshooting Route attachment and + measuring blast radius/impact of changes to a Listener. + format: int32 + type: integer + conditions: + description: Conditions describe the current condition of this + listener. + 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 + maxItems: 8 + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + name: + description: Name is the name of the Listener that this status + corresponds to. + maxLength: 253 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + supportedKinds: + description: |- + SupportedKinds is the list indicating the Kinds supported by this + listener. This MUST represent the kinds an implementation supports for + that Listener configuration. + + + If kinds are specified in Spec that are not supported, they MUST NOT + appear in this list and an implementation MUST set the "ResolvedRefs" + condition to "False" with the "InvalidRouteKinds" reason. If both valid + and invalid Route kinds are specified, the implementation MUST + reference the valid Route kinds that have been specified. + items: + description: RouteGroupKind indicates the group and kind of + a Route resource. + properties: + group: + default: gateway.networking.k8s.io + description: Group is the group of the Route. + maxLength: 253 + pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + kind: + description: Kind is the kind of the Route. + maxLength: 63 + minLength: 1 + pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ + type: string + required: + - kind + type: object + maxItems: 8 + type: array + required: + - attachedRoutes + - conditions + - name + - supportedKinds + type: object + maxItems: 64 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + type: object + required: + - spec + type: object + served: true + storage: true + subresources: + status: {} + - additionalPrinterColumns: + - jsonPath: .spec.gatewayClassName + name: Class + type: string + - jsonPath: .status.addresses[*].value + name: Address + type: string + - jsonPath: .status.conditions[?(@.type=="Programmed")].status + name: Programmed + type: string + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v1beta1 + schema: + openAPIV3Schema: + description: |- + Gateway represents an instance of a service-traffic handling infrastructure + by binding Listeners to a set of IP addresses. + 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: Spec defines the desired state of Gateway. + properties: + addresses: + description: |+ + Addresses requested for this Gateway. This is optional and behavior can + depend on the implementation. If a value is set in the spec and the + requested address is invalid or unavailable, the implementation MUST + indicate this in the associated entry in GatewayStatus.Addresses. + + + The Addresses field represents a request for the address(es) on the + "outside of the Gateway", that traffic bound for this Gateway will use. + This could be the IP address or hostname of an external load balancer or + other networking infrastructure, or some other address that traffic will + be sent to. + + + If no Addresses are specified, the implementation MAY schedule the + Gateway in an implementation-specific manner, assigning an appropriate + set of Addresses. + + + The implementation MUST bind all Listeners to every GatewayAddress that + it assigns to the Gateway and add a corresponding entry in + GatewayStatus.Addresses. + + + Support: Extended + + + items: + description: GatewayAddress describes an address that can be bound + to a Gateway. + oneOf: + - properties: + type: + enum: + - IPAddress + value: + anyOf: + - format: ipv4 + - format: ipv6 + - properties: + type: + not: + enum: + - IPAddress + properties: + type: + default: IPAddress + description: Type of the address. + maxLength: 253 + minLength: 1 + pattern: ^Hostname|IPAddress|NamedAddress|[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*\/[A-Za-z0-9\/\-._~%!$&'()*+,;=:]+$ + type: string + value: + description: |- + Value of the address. The validity of the values will depend + on the type and support by the controller. + + + Examples: `1.2.3.4`, `128::1`, `my-ip-address`. + maxLength: 253 + minLength: 1 + type: string + required: + - value + type: object + x-kubernetes-validations: + - message: Hostname value must only contain valid characters (matching + ^(\*\.)?[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$) + rule: 'self.type == ''Hostname'' ? self.value.matches(r"""^(\*\.)?[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$"""): + true' + maxItems: 16 + type: array + x-kubernetes-validations: + - message: IPAddress values must be unique + rule: 'self.all(a1, a1.type == ''IPAddress'' ? self.exists_one(a2, + a2.type == a1.type && a2.value == a1.value) : true )' + - message: Hostname values must be unique + rule: 'self.all(a1, a1.type == ''Hostname'' ? self.exists_one(a2, + a2.type == a1.type && a2.value == a1.value) : true )' + gatewayClassName: + description: |- + GatewayClassName used for this Gateway. This is the name of a + GatewayClass resource. + maxLength: 253 + minLength: 1 + type: string + listeners: + description: |- + Listeners associated with this Gateway. Listeners define + logical endpoints that are bound on this Gateway's addresses. + At least one Listener MUST be specified. + + + Each Listener in a set of Listeners (for example, in a single Gateway) + MUST be _distinct_, in that a traffic flow MUST be able to be assigned to + exactly one listener. (This section uses "set of Listeners" rather than + "Listeners in a single Gateway" because implementations MAY merge configuration + from multiple Gateways onto a single data plane, and these rules _also_ + apply in that case). + + + Practically, this means that each listener in a set MUST have a unique + combination of Port, Protocol, and, if supported by the protocol, Hostname. + + + Some combinations of port, protocol, and TLS settings are considered + Core support and MUST be supported by implementations based on their + targeted conformance profile: + + + HTTP Profile + + + 1. HTTPRoute, Port: 80, Protocol: HTTP + 2. HTTPRoute, Port: 443, Protocol: HTTPS, TLS Mode: Terminate, TLS keypair provided + + + TLS Profile + + + 1. TLSRoute, Port: 443, Protocol: TLS, TLS Mode: Passthrough + + + "Distinct" Listeners have the following property: + + + The implementation can match inbound requests to a single distinct + Listener. When multiple Listeners share values for fields (for + example, two Listeners with the same Port value), the implementation + can match requests to only one of the Listeners using other + Listener fields. + + + For example, the following Listener scenarios are distinct: + + + 1. Multiple Listeners with the same Port that all use the "HTTP" + Protocol that all have unique Hostname values. + 2. Multiple Listeners with the same Port that use either the "HTTPS" or + "TLS" Protocol that all have unique Hostname values. + 3. A mixture of "TCP" and "UDP" Protocol Listeners, where no Listener + with the same Protocol has the same Port value. + + + Some fields in the Listener struct have possible values that affect + whether the Listener is distinct. Hostname is particularly relevant + for HTTP or HTTPS protocols. + + + When using the Hostname value to select between same-Port, same-Protocol + Listeners, the Hostname value must be different on each Listener for the + Listener to be distinct. + + + When the Listeners are distinct based on Hostname, inbound request + hostnames MUST match from the most specific to least specific Hostname + values to choose the correct Listener and its associated set of Routes. + + + Exact matches must be processed before wildcard matches, and wildcard + matches must be processed before fallback (empty Hostname value) + matches. For example, `"foo.example.com"` takes precedence over + `"*.example.com"`, and `"*.example.com"` takes precedence over `""`. + + + Additionally, if there are multiple wildcard entries, more specific + wildcard entries must be processed before less specific wildcard entries. + For example, `"*.foo.example.com"` takes precedence over `"*.example.com"`. + The precise definition here is that the higher the number of dots in the + hostname to the right of the wildcard character, the higher the precedence. + + + The wildcard character will match any number of characters _and dots_ to + the left, however, so `"*.example.com"` will match both + `"foo.bar.example.com"` _and_ `"bar.example.com"`. + + + If a set of Listeners contains Listeners that are not distinct, then those + Listeners are Conflicted, and the implementation MUST set the "Conflicted" + condition in the Listener Status to "True". + + + Implementations MAY choose to accept a Gateway with some Conflicted + Listeners only if they only accept the partial Listener set that contains + no Conflicted Listeners. To put this another way, implementations may + accept a partial Listener set only if they throw out *all* the conflicting + Listeners. No picking one of the conflicting listeners as the winner. + This also means that the Gateway must have at least one non-conflicting + Listener in this case, otherwise it violates the requirement that at + least one Listener must be present. + + + The implementation MUST set a "ListenersNotValid" condition on the + Gateway Status when the Gateway contains Conflicted Listeners whether or + not they accept the Gateway. That Condition SHOULD clearly + indicate in the Message which Listeners are conflicted, and which are + Accepted. Additionally, the Listener status for those listeners SHOULD + indicate which Listeners are conflicted and not Accepted. + + + A Gateway's Listeners are considered "compatible" if: + + + 1. They are distinct. + 2. The implementation can serve them in compliance with the Addresses + requirement that all Listeners are available on all assigned + addresses. + + + Compatible combinations in Extended support are expected to vary across + implementations. A combination that is compatible for one implementation + may not be compatible for another. + + + For example, an implementation that cannot serve both TCP and UDP listeners + on the same address, or cannot mix HTTPS and generic TLS listens on the same port + would not consider those cases compatible, even though they are distinct. + + + Note that requests SHOULD match at most one Listener. For example, if + Listeners are defined for "foo.example.com" and "*.example.com", a + request to "foo.example.com" SHOULD only be routed using routes attached + to the "foo.example.com" Listener (and not the "*.example.com" Listener). + This concept is known as "Listener Isolation". Implementations that do + not support Listener Isolation MUST clearly document this. + + + Implementations MAY merge separate Gateways onto a single set of + Addresses if all Listeners across all Gateways are compatible. + + + Support: Core + items: + description: |- + Listener embodies the concept of a logical endpoint where a Gateway accepts + network connections. + properties: + allowedRoutes: + default: + namespaces: + from: Same + description: |- + AllowedRoutes defines the types of routes that MAY be attached to a + Listener and the trusted namespaces where those Route resources MAY be + present. + + + Although a client request may match multiple route rules, only one rule + may ultimately receive the request. Matching precedence MUST be + determined in order of the following criteria: + + + * The most specific match as defined by the Route type. + * The oldest Route based on creation timestamp. For example, a Route with + a creation timestamp of "2020-09-08 01:02:03" is given precedence over + a Route with a creation timestamp of "2020-09-08 01:02:04". + * If everything else is equivalent, the Route appearing first in + alphabetical order (namespace/name) should be given precedence. For + example, foo/bar is given precedence over foo/baz. + + + All valid rules within a Route attached to this Listener should be + implemented. Invalid Route rules can be ignored (sometimes that will mean + the full Route). If a Route rule transitions from valid to invalid, + support for that Route rule should be dropped to ensure consistency. For + example, even if a filter specified by a Route rule is invalid, the rest + of the rules within that Route should still be supported. + + + Support: Core + properties: + kinds: + description: |- + Kinds specifies the groups and kinds of Routes that are allowed to bind + to this Gateway Listener. When unspecified or empty, the kinds of Routes + selected are determined using the Listener protocol. + + + A RouteGroupKind MUST correspond to kinds of Routes that are compatible + with the application protocol specified in the Listener's Protocol field. + If an implementation does not support or recognize this resource type, it + MUST set the "ResolvedRefs" condition to False for this Listener with the + "InvalidRouteKinds" reason. + + + Support: Core + items: + description: RouteGroupKind indicates the group and kind + of a Route resource. + properties: + group: + default: gateway.networking.k8s.io + description: Group is the group of the Route. + maxLength: 253 + pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + kind: + description: Kind is the kind of the Route. + maxLength: 63 + minLength: 1 + pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ + type: string + required: + - kind + type: object + maxItems: 8 + type: array + namespaces: + default: + from: Same + description: |- + Namespaces indicates namespaces from which Routes may be attached to this + Listener. This is restricted to the namespace of this Gateway by default. + + + Support: Core + properties: + from: + default: Same + description: |- + From indicates where Routes will be selected for this Gateway. Possible + values are: + + + * All: Routes in all namespaces may be used by this Gateway. + * Selector: Routes in namespaces selected by the selector may be used by + this Gateway. + * Same: Only Routes in the same namespace may be used by this Gateway. + + + Support: Core + enum: + - All + - Selector + - Same + type: string + selector: + description: |- + Selector must be specified when From is set to "Selector". In that case, + only Routes in Namespaces matching this Selector will be selected by this + Gateway. This field is ignored for other values of "From". + + + Support: Core + 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 + type: object + type: object + hostname: + description: |- + Hostname specifies the virtual hostname to match for protocol types that + define this concept. When unspecified, all hostnames are matched. This + field is ignored for protocols that don't require hostname based + matching. + + + Implementations MUST apply Hostname matching appropriately for each of + the following protocols: + + + * TLS: The Listener Hostname MUST match the SNI. + * HTTP: The Listener Hostname MUST match the Host header of the request. + * HTTPS: The Listener Hostname SHOULD match at both the TLS and HTTP + protocol layers as described above. If an implementation does not + ensure that both the SNI and Host header match the Listener hostname, + it MUST clearly document that. + + + For HTTPRoute and TLSRoute resources, there is an interaction with the + `spec.hostnames` array. When both listener and route specify hostnames, + there MUST be an intersection between the values for a Route to be + accepted. For more information, refer to the Route specific Hostnames + documentation. + + + Hostnames that are prefixed with a wildcard label (`*.`) are interpreted + as a suffix match. That means that a match for `*.example.com` would match + both `test.example.com`, and `foo.test.example.com`, but not `example.com`. + + + Support: Core + maxLength: 253 + minLength: 1 + pattern: ^(\*\.)?[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + name: + description: |- + Name is the name of the Listener. This name MUST be unique within a + Gateway. + + + Support: Core + maxLength: 253 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + port: + description: |- + Port is the network port. Multiple listeners may use the + same port, subject to the Listener compatibility rules. + + + Support: Core + format: int32 + maximum: 65535 + minimum: 1 + type: integer + protocol: + description: |- + Protocol specifies the network protocol this listener expects to receive. + + + Support: Core + maxLength: 255 + minLength: 1 + pattern: ^[a-zA-Z0-9]([-a-zSA-Z0-9]*[a-zA-Z0-9])?$|[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*\/[A-Za-z0-9]+$ + type: string + tls: + description: |- + TLS is the TLS configuration for the Listener. This field is required if + the Protocol field is "HTTPS" or "TLS". It is invalid to set this field + if the Protocol field is "HTTP", "TCP", or "UDP". + + + The association of SNIs to Certificate defined in GatewayTLSConfig is + defined based on the Hostname field for this listener. + + + The GatewayClass MUST use the longest matching SNI out of all + available certificates for any TLS handshake. + + + Support: Core + properties: + certificateRefs: + description: |- + CertificateRefs contains a series of references to Kubernetes objects that + contains TLS certificates and private keys. These certificates are used to + establish a TLS handshake for requests that match the hostname of the + associated listener. + + + A single CertificateRef to a Kubernetes Secret has "Core" support. + Implementations MAY choose to support attaching multiple certificates to + a Listener, but this behavior is implementation-specific. + + + References to a resource in different namespace are invalid UNLESS there + is a ReferenceGrant in the target namespace that allows the certificate + to be attached. If a ReferenceGrant does not allow this reference, the + "ResolvedRefs" condition MUST be set to False for this listener with the + "RefNotPermitted" reason. + + + This field is required to have at least one element when the mode is set + to "Terminate" (default) and is optional otherwise. + + + CertificateRefs can reference to standard Kubernetes resources, i.e. + Secret, or implementation-specific custom resources. + + + Support: Core - A single reference to a Kubernetes Secret of type kubernetes.io/tls + + + Support: Implementation-specific (More than one reference or other resource types) + items: + description: |- + SecretObjectReference identifies an API object including its namespace, + defaulting to Secret. + + + The API object must be valid in the cluster; the Group and Kind must + be registered in the cluster for this reference to be valid. + + + References to objects with invalid Group and Kind are not valid, and must + be rejected by the implementation, with appropriate Conditions set + on the containing object. + properties: + group: + default: "" + description: |- + Group is the group of the referent. For example, "gateway.networking.k8s.io". + When unspecified or empty string, core API group is inferred. + maxLength: 253 + pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + kind: + default: Secret + description: Kind is kind of the referent. For example + "Secret". + maxLength: 63 + minLength: 1 + pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ + type: string + name: + description: Name is the name of the referent. + maxLength: 253 + minLength: 1 + type: string + namespace: + description: |- + Namespace is the namespace of the referenced object. When unspecified, the local + namespace is inferred. + + + Note that when a namespace different than the local namespace is specified, + a ReferenceGrant object is required in the referent namespace to allow that + namespace's owner to accept the reference. See the ReferenceGrant + documentation for details. + + + Support: Core + maxLength: 63 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ + type: string + required: + - name + type: object + maxItems: 64 + type: array + mode: + default: Terminate + description: |- + Mode defines the TLS behavior for the TLS session initiated by the client. + There are two possible modes: + + + - Terminate: The TLS session between the downstream client and the + Gateway is terminated at the Gateway. This mode requires certificates + to be specified in some way, such as populating the certificateRefs + field. + - Passthrough: The TLS session is NOT terminated by the Gateway. This + implies that the Gateway can't decipher the TLS stream except for + the ClientHello message of the TLS protocol. The certificateRefs field + is ignored in this mode. + + + Support: Core + enum: + - Terminate + - Passthrough + type: string + options: + additionalProperties: + description: |- + AnnotationValue is the value of an annotation in Gateway API. This is used + for validation of maps such as TLS options. This roughly matches Kubernetes + annotation validation, although the length validation in that case is based + on the entire size of the annotations struct. + maxLength: 4096 + minLength: 0 + type: string + description: |- + Options are a list of key/value pairs to enable extended TLS + configuration for each implementation. For example, configuring the + minimum TLS version or supported cipher suites. + + + A set of common keys MAY be defined by the API in the future. To avoid + any ambiguity, implementation-specific definitions MUST use + domain-prefixed names, such as `example.com/my-custom-option`. + Un-prefixed names are reserved for key names defined by Gateway API. + + + Support: Implementation-specific + maxProperties: 16 + type: object + type: object + x-kubernetes-validations: + - message: certificateRefs or options must be specified when + mode is Terminate + rule: 'self.mode == ''Terminate'' ? size(self.certificateRefs) + > 0 || size(self.options) > 0 : true' + required: + - name + - port + - protocol + type: object + maxItems: 64 + minItems: 1 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + x-kubernetes-validations: + - message: tls must not be specified for protocols ['HTTP', 'TCP', + 'UDP'] + rule: 'self.all(l, l.protocol in [''HTTP'', ''TCP'', ''UDP''] ? + !has(l.tls) : true)' + - message: tls mode must be Terminate for protocol HTTPS + rule: 'self.all(l, (l.protocol == ''HTTPS'' && has(l.tls)) ? (l.tls.mode + == '''' || l.tls.mode == ''Terminate'') : true)' + - message: hostname must not be specified for protocols ['TCP', 'UDP'] + rule: 'self.all(l, l.protocol in [''TCP'', ''UDP''] ? (!has(l.hostname) + || l.hostname == '''') : true)' + - message: Listener name must be unique within the Gateway + rule: self.all(l1, self.exists_one(l2, l1.name == l2.name)) + - message: Combination of port, protocol and hostname must be unique + for each listener + rule: 'self.all(l1, self.exists_one(l2, l1.port == l2.port && l1.protocol + == l2.protocol && (has(l1.hostname) && has(l2.hostname) ? l1.hostname + == l2.hostname : !has(l1.hostname) && !has(l2.hostname))))' + required: + - gatewayClassName + - listeners + type: object + status: + default: + conditions: + - lastTransitionTime: "1970-01-01T00:00:00Z" + message: Waiting for controller + reason: Pending + status: Unknown + type: Accepted + - lastTransitionTime: "1970-01-01T00:00:00Z" + message: Waiting for controller + reason: Pending + status: Unknown + type: Programmed + description: Status defines the current state of Gateway. + properties: + addresses: + description: |+ + Addresses lists the network addresses that have been bound to the + Gateway. + + + This list may differ from the addresses provided in the spec under some + conditions: + + + * no addresses are specified, all addresses are dynamically assigned + * a combination of specified and dynamic addresses are assigned + * a specified address was unusable (e.g. already in use) + + + items: + description: GatewayStatusAddress describes a network address that + is bound to a Gateway. + oneOf: + - properties: + type: + enum: + - IPAddress + value: + anyOf: + - format: ipv4 + - format: ipv6 + - properties: + type: + not: + enum: + - IPAddress + properties: + type: + default: IPAddress + description: Type of the address. + maxLength: 253 + minLength: 1 + pattern: ^Hostname|IPAddress|NamedAddress|[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*\/[A-Za-z0-9\/\-._~%!$&'()*+,;=:]+$ + type: string + value: + description: |- + Value of the address. The validity of the values will depend + on the type and support by the controller. + + + Examples: `1.2.3.4`, `128::1`, `my-ip-address`. + maxLength: 253 + minLength: 1 + type: string + required: + - value + type: object + x-kubernetes-validations: + - message: Hostname value must only contain valid characters (matching + ^(\*\.)?[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$) + rule: 'self.type == ''Hostname'' ? self.value.matches(r"""^(\*\.)?[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$"""): + true' + maxItems: 16 + type: array + conditions: + default: + - lastTransitionTime: "1970-01-01T00:00:00Z" + message: Waiting for controller + reason: Pending + status: Unknown + type: Accepted + - lastTransitionTime: "1970-01-01T00:00:00Z" + message: Waiting for controller + reason: Pending + status: Unknown + type: Programmed + description: |- + Conditions describe the current conditions of the Gateway. + + + Implementations should prefer to express Gateway conditions + using the `GatewayConditionType` and `GatewayConditionReason` + constants so that operators and tools can converge on a common + vocabulary to describe Gateway state. + + + Known condition types are: + + + * "Accepted" + * "Programmed" + * "Ready" + 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 + maxItems: 8 + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + listeners: + description: Listeners provide status for each unique listener port + defined in the Spec. + items: + description: ListenerStatus is the status associated with a Listener. + properties: + attachedRoutes: + description: |- + AttachedRoutes represents the total number of Routes that have been + successfully attached to this Listener. + + + Successful attachment of a Route to a Listener is based solely on the + combination of the AllowedRoutes field on the corresponding Listener + and the Route's ParentRefs field. A Route is successfully attached to + a Listener when it is selected by the Listener's AllowedRoutes field + AND the Route has a valid ParentRef selecting the whole Gateway + resource or a specific Listener as a parent resource (more detail on + attachment semantics can be found in the documentation on the various + Route kinds ParentRefs fields). Listener or Route status does not impact + successful attachment, i.e. the AttachedRoutes field count MUST be set + for Listeners with condition Accepted: false and MUST count successfully + attached Routes that may themselves have Accepted: false conditions. + + + Uses for this field include troubleshooting Route attachment and + measuring blast radius/impact of changes to a Listener. + format: int32 + type: integer + conditions: + description: Conditions describe the current condition of this + listener. + 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 + maxItems: 8 + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + name: + description: Name is the name of the Listener that this status + corresponds to. + maxLength: 253 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + supportedKinds: + description: |- + SupportedKinds is the list indicating the Kinds supported by this + listener. This MUST represent the kinds an implementation supports for + that Listener configuration. + + + If kinds are specified in Spec that are not supported, they MUST NOT + appear in this list and an implementation MUST set the "ResolvedRefs" + condition to "False" with the "InvalidRouteKinds" reason. If both valid + and invalid Route kinds are specified, the implementation MUST + reference the valid Route kinds that have been specified. + items: + description: RouteGroupKind indicates the group and kind of + a Route resource. + properties: + group: + default: gateway.networking.k8s.io + description: Group is the group of the Route. + maxLength: 253 + pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + kind: + description: Kind is the kind of the Route. + maxLength: 63 + minLength: 1 + pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ + type: string + required: + - kind + type: object + maxItems: 8 + type: array + required: + - attachedRoutes + - conditions + - name + - supportedKinds + type: object + maxItems: 64 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + type: object + required: + - spec + type: object + served: true + storage: false + subresources: + status: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: null + storedVersions: null +--- +# +# config/crd/standard/gateway.networking.k8s.io_grpcroutes.yaml +# +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + api-approved.kubernetes.io: https://github.com/kubernetes-sigs/gateway-api/pull/2997 + gateway.networking.k8s.io/bundle-version: v1.1.0 + gateway.networking.k8s.io/channel: standard + creationTimestamp: null + name: grpcroutes.gateway.networking.k8s.io +spec: + group: gateway.networking.k8s.io + names: + categories: + - gateway-api + kind: GRPCRoute + listKind: GRPCRouteList + plural: grpcroutes + singular: grpcroute + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .spec.hostnames + name: Hostnames + type: string + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v1 + schema: + openAPIV3Schema: + description: |- + GRPCRoute provides a way to route gRPC requests. This includes the capability + to match requests by hostname, gRPC service, gRPC method, or HTTP/2 header. + Filters can be used to specify additional processing steps. Backends specify + where matching requests will be routed. + + + GRPCRoute falls under extended support within the Gateway API. Within the + following specification, the word "MUST" indicates that an implementation + supporting GRPCRoute must conform to the indicated requirement, but an + implementation not supporting this route type need not follow the requirement + unless explicitly indicated. + + + Implementations supporting `GRPCRoute` with the `HTTPS` `ProtocolType` MUST + accept HTTP/2 connections without an initial upgrade from HTTP/1.1, i.e. via + ALPN. If the implementation does not support this, then it MUST set the + "Accepted" condition to "False" for the affected listener with a reason of + "UnsupportedProtocol". Implementations MAY also accept HTTP/2 connections + with an upgrade from HTTP/1. + + + Implementations supporting `GRPCRoute` with the `HTTP` `ProtocolType` MUST + support HTTP/2 over cleartext TCP (h2c, + https://www.rfc-editor.org/rfc/rfc7540#section-3.1) without an initial + upgrade from HTTP/1.1, i.e. with prior knowledge + (https://www.rfc-editor.org/rfc/rfc7540#section-3.4). If the implementation + does not support this, then it MUST set the "Accepted" condition to "False" + for the affected listener with a reason of "UnsupportedProtocol". + Implementations MAY also accept HTTP/2 connections with an upgrade from + HTTP/1, i.e. without prior knowledge. + 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: Spec defines the desired state of GRPCRoute. + properties: + hostnames: + description: |- + Hostnames defines a set of hostnames to match against the GRPC + Host header to select a GRPCRoute to process the request. This matches + the RFC 1123 definition of a hostname with 2 notable exceptions: + + + 1. IPs are not allowed. + 2. A hostname may be prefixed with a wildcard label (`*.`). The wildcard + label MUST appear by itself as the first label. + + + If a hostname is specified by both the Listener and GRPCRoute, there + MUST be at least one intersecting hostname for the GRPCRoute to be + attached to the Listener. For example: + + + * A Listener with `test.example.com` as the hostname matches GRPCRoutes + that have either not specified any hostnames, or have specified at + least one of `test.example.com` or `*.example.com`. + * A Listener with `*.example.com` as the hostname matches GRPCRoutes + that have either not specified any hostnames or have specified at least + one hostname that matches the Listener hostname. For example, + `test.example.com` and `*.example.com` would both match. On the other + hand, `example.com` and `test.example.net` would not match. + + + Hostnames that are prefixed with a wildcard label (`*.`) are interpreted + as a suffix match. That means that a match for `*.example.com` would match + both `test.example.com`, and `foo.test.example.com`, but not `example.com`. + + + If both the Listener and GRPCRoute have specified hostnames, any + GRPCRoute hostnames that do not match the Listener hostname MUST be + ignored. For example, if a Listener specified `*.example.com`, and the + GRPCRoute specified `test.example.com` and `test.example.net`, + `test.example.net` MUST NOT be considered for a match. + + + If both the Listener and GRPCRoute have specified hostnames, and none + match with the criteria above, then the GRPCRoute MUST NOT be accepted by + the implementation. The implementation MUST raise an 'Accepted' Condition + with a status of `False` in the corresponding RouteParentStatus. + + + If a Route (A) of type HTTPRoute or GRPCRoute is attached to a + Listener and that listener already has another Route (B) of the other + type attached and the intersection of the hostnames of A and B is + non-empty, then the implementation MUST accept exactly one of these two + routes, determined by the following criteria, in order: + + + * The oldest Route based on creation timestamp. + * The Route appearing first in alphabetical order by + "{namespace}/{name}". + + + The rejected Route MUST raise an 'Accepted' condition with a status of + 'False' in the corresponding RouteParentStatus. + + + Support: Core + items: + description: |- + Hostname is the fully qualified domain name of a network host. This matches + the RFC 1123 definition of a hostname with 2 notable exceptions: + + + 1. IPs are not allowed. + 2. A hostname may be prefixed with a wildcard label (`*.`). The wildcard + label must appear by itself as the first label. + + + Hostname can be "precise" which is a domain name without the terminating + dot of a network host (e.g. "foo.example.com") or "wildcard", which is a + domain name prefixed with a single wildcard label (e.g. `*.example.com`). + + + Note that as per RFC1035 and RFC1123, a *label* must consist of lower case + alphanumeric characters or '-', and must start and end with an alphanumeric + character. No other punctuation is allowed. + maxLength: 253 + minLength: 1 + pattern: ^(\*\.)?[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + maxItems: 16 + type: array + parentRefs: + description: |+ + ParentRefs references the resources (usually Gateways) that a Route wants + to be attached to. Note that the referenced parent resource needs to + allow this for the attachment to be complete. For Gateways, that means + the Gateway needs to allow attachment from Routes of this kind and + namespace. For Services, that means the Service must either be in the same + namespace for a "producer" route, or the mesh implementation must support + and allow "consumer" routes for the referenced Service. ReferenceGrant is + not applicable for governing ParentRefs to Services - it is not possible to + create a "producer" route for a Service in a different namespace from the + Route. + + + There are two kinds of parent resources with "Core" support: + + + * Gateway (Gateway conformance profile) + * Service (Mesh conformance profile, ClusterIP Services only) + + + This API may be extended in the future to support additional kinds of parent + resources. + + + ParentRefs must be _distinct_. This means either that: + + + * They select different objects. If this is the case, then parentRef + entries are distinct. In terms of fields, this means that the + multi-part key defined by `group`, `kind`, `namespace`, and `name` must + be unique across all parentRef entries in the Route. + * They do not select different objects, but for each optional field used, + each ParentRef that selects the same object must set the same set of + optional fields to different values. If one ParentRef sets a + combination of optional fields, all must set the same combination. + + + Some examples: + + + * If one ParentRef sets `sectionName`, all ParentRefs referencing the + same object must also set `sectionName`. + * If one ParentRef sets `port`, all ParentRefs referencing the same + object must also set `port`. + * If one ParentRef sets `sectionName` and `port`, all ParentRefs + referencing the same object must also set `sectionName` and `port`. + + + It is possible to separately reference multiple distinct objects that may + be collapsed by an implementation. For example, some implementations may + choose to merge compatible Gateway Listeners together. If that is the + case, the list of routes attached to those resources should also be + merged. + + + Note that for ParentRefs that cross namespace boundaries, there are specific + rules. Cross-namespace references are only valid if they are explicitly + allowed by something in the namespace they are referring to. For example, + Gateway has the AllowedRoutes field, and ReferenceGrant provides a + generic way to enable other kinds of cross-namespace reference. + + + + + + + + + items: + description: |- + ParentReference identifies an API object (usually a Gateway) that can be considered + a parent of this resource (usually a route). There are two kinds of parent resources + with "Core" support: + + + * Gateway (Gateway conformance profile) + * Service (Mesh conformance profile, ClusterIP Services only) + + + This API may be extended in the future to support additional kinds of parent + resources. + + + The API object must be valid in the cluster; the Group and Kind must + be registered in the cluster for this reference to be valid. + properties: + group: + default: gateway.networking.k8s.io + description: |- + Group is the group of the referent. + When unspecified, "gateway.networking.k8s.io" is inferred. + To set the core API group (such as for a "Service" kind referent), + Group must be explicitly set to "" (empty string). + + + Support: Core + maxLength: 253 + pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + kind: + default: Gateway + description: |- + Kind is kind of the referent. + + + There are two kinds of parent resources with "Core" support: + + + * Gateway (Gateway conformance profile) + * Service (Mesh conformance profile, ClusterIP Services only) + + + Support for other resources is Implementation-Specific. + maxLength: 63 + minLength: 1 + pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ + type: string + name: + description: |- + Name is the name of the referent. + + + Support: Core + maxLength: 253 + minLength: 1 + type: string + namespace: + description: |- + Namespace is the namespace of the referent. When unspecified, this refers + to the local namespace of the Route. + + + Note that there are specific rules for ParentRefs which cross namespace + boundaries. Cross-namespace references are only valid if they are explicitly + allowed by something in the namespace they are referring to. For example: + Gateway has the AllowedRoutes field, and ReferenceGrant provides a + generic way to enable any other kind of cross-namespace reference. + + + + + + Support: Core + maxLength: 63 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ + type: string + port: + description: |- + Port is the network port this Route targets. It can be interpreted + differently based on the type of parent resource. + + + When the parent resource is a Gateway, this targets all listeners + listening on the specified port that also support this kind of Route(and + select this Route). It's not recommended to set `Port` unless the + networking behaviors specified in a Route must apply to a specific port + as opposed to a listener(s) whose port(s) may be changed. When both Port + and SectionName are specified, the name and port of the selected listener + must match both specified values. + + + + + + Implementations MAY choose to support other parent resources. + Implementations supporting other types of parent resources MUST clearly + document how/if Port is interpreted. + + + For the purpose of status, an attachment is considered successful as + long as the parent resource accepts it partially. For example, Gateway + listeners can restrict which Routes can attach to them by Route kind, + namespace, or hostname. If 1 of 2 Gateway listeners accept attachment + from the referencing Route, the Route MUST be considered successfully + attached. If no Gateway listeners accept attachment from this Route, + the Route MUST be considered detached from the Gateway. + + + Support: Extended + format: int32 + maximum: 65535 + minimum: 1 + type: integer + sectionName: + description: |- + SectionName is the name of a section within the target resource. In the + following resources, SectionName is interpreted as the following: + + + * Gateway: Listener name. When both Port (experimental) and SectionName + are specified, the name and port of the selected listener must match + both specified values. + * Service: Port name. When both Port (experimental) and SectionName + are specified, the name and port of the selected listener must match + both specified values. + + + Implementations MAY choose to support attaching Routes to other resources. + If that is the case, they MUST clearly document how SectionName is + interpreted. + + + When unspecified (empty string), this will reference the entire resource. + For the purpose of status, an attachment is considered successful if at + least one section in the parent resource accepts it. For example, Gateway + listeners can restrict which Routes can attach to them by Route kind, + namespace, or hostname. If 1 of 2 Gateway listeners accept attachment from + the referencing Route, the Route MUST be considered successfully + attached. If no Gateway listeners accept attachment from this Route, the + Route MUST be considered detached from the Gateway. + + + Support: Core + maxLength: 253 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + required: + - name + type: object + maxItems: 32 + type: array + x-kubernetes-validations: + - message: sectionName must be specified when parentRefs includes + 2 or more references to the same parent + rule: 'self.all(p1, self.all(p2, p1.group == p2.group && p1.kind + == p2.kind && p1.name == p2.name && (((!has(p1.__namespace__) + || p1.__namespace__ == '''') && (!has(p2.__namespace__) || p2.__namespace__ + == '''')) || (has(p1.__namespace__) && has(p2.__namespace__) && + p1.__namespace__ == p2.__namespace__ )) ? ((!has(p1.sectionName) + || p1.sectionName == '''') == (!has(p2.sectionName) || p2.sectionName + == '''')) : true))' + - message: sectionName must be unique when parentRefs includes 2 or + more references to the same parent + rule: self.all(p1, self.exists_one(p2, p1.group == p2.group && p1.kind + == p2.kind && p1.name == p2.name && (((!has(p1.__namespace__) + || p1.__namespace__ == '') && (!has(p2.__namespace__) || p2.__namespace__ + == '')) || (has(p1.__namespace__) && has(p2.__namespace__) && + p1.__namespace__ == p2.__namespace__ )) && (((!has(p1.sectionName) + || p1.sectionName == '') && (!has(p2.sectionName) || p2.sectionName + == '')) || (has(p1.sectionName) && has(p2.sectionName) && p1.sectionName + == p2.sectionName)))) + rules: + description: Rules are a list of GRPC matchers, filters and actions. + items: + description: |- + GRPCRouteRule defines the semantics for matching a gRPC request based on + conditions (matches), processing it (filters), and forwarding the request to + an API object (backendRefs). + properties: + backendRefs: + description: |- + BackendRefs defines the backend(s) where matching requests should be + sent. + + + Failure behavior here depends on how many BackendRefs are specified and + how many are invalid. + + + If *all* entries in BackendRefs are invalid, and there are also no filters + specified in this route rule, *all* traffic which matches this rule MUST + receive an `UNAVAILABLE` status. + + + See the GRPCBackendRef definition for the rules about what makes a single + GRPCBackendRef invalid. + + + When a GRPCBackendRef is invalid, `UNAVAILABLE` statuses MUST be returned for + requests that would have otherwise been routed to an invalid backend. If + multiple backends are specified, and some are invalid, the proportion of + requests that would otherwise have been routed to an invalid backend + MUST receive an `UNAVAILABLE` status. + + + For example, if two backends are specified with equal weights, and one is + invalid, 50 percent of traffic MUST receive an `UNAVAILABLE` status. + Implementations may choose how that 50 percent is determined. + + + Support: Core for Kubernetes Service + + + Support: Implementation-specific for any other resource + + + Support for weight: Core + items: + description: |- + GRPCBackendRef defines how a GRPCRoute forwards a gRPC request. + + + Note that when a namespace different than the local namespace is specified, a + ReferenceGrant object is required in the referent namespace to allow that + namespace's owner to accept the reference. See the ReferenceGrant + documentation for details. + + + + + + When the BackendRef points to a Kubernetes Service, implementations SHOULD + honor the appProtocol field if it is set for the target Service Port. + + + Implementations supporting appProtocol SHOULD recognize the Kubernetes + Standard Application Protocols defined in KEP-3726. + + + If a Service appProtocol isn't specified, an implementation MAY infer the + backend protocol through its own means. Implementations MAY infer the + protocol from the Route type referring to the backend Service. + + + If a Route is not able to send traffic to the backend using the specified + protocol then the backend is considered invalid. Implementations MUST set the + "ResolvedRefs" condition to "False" with the "UnsupportedProtocol" reason. + + + + properties: + filters: + description: |- + Filters defined at this level MUST be executed if and only if the + request is being forwarded to the backend defined here. + + + Support: Implementation-specific (For broader support of filters, use the + Filters field in GRPCRouteRule.) + items: + description: |- + GRPCRouteFilter defines processing steps that must be completed during the + request or response lifecycle. GRPCRouteFilters are meant as an extension + point to express processing that may be done in Gateway implementations. Some + examples include request or response modification, implementing + authentication strategies, rate-limiting, and traffic shaping. API + guarantee/conformance is defined based on the type of the filter. + properties: + extensionRef: + description: |- + ExtensionRef is an optional, implementation-specific extension to the + "filter" behavior. For example, resource "myroutefilter" in group + "networking.example.net"). ExtensionRef MUST NOT be used for core and + extended filters. + + + Support: Implementation-specific + + + This filter can be used multiple times within the same rule. + properties: + group: + description: |- + Group is the group of the referent. For example, "gateway.networking.k8s.io". + When unspecified or empty string, core API group is inferred. + maxLength: 253 + pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + kind: + description: Kind is kind of the referent. For + example "HTTPRoute" or "Service". + maxLength: 63 + minLength: 1 + pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ + type: string + name: + description: Name is the name of the referent. + maxLength: 253 + minLength: 1 + type: string + required: + - group + - kind + - name + type: object + requestHeaderModifier: + description: |- + RequestHeaderModifier defines a schema for a filter that modifies request + headers. + + + Support: Core + properties: + add: + description: |- + Add adds the given header(s) (name, value) to the request + before the action. It appends to any existing values associated + with the header name. + + + Input: + GET /foo HTTP/1.1 + my-header: foo + + + Config: + add: + - name: "my-header" + value: "bar,baz" + + + Output: + GET /foo HTTP/1.1 + my-header: foo,bar,baz + items: + description: HTTPHeader represents an HTTP + Header name and value as defined by RFC + 7230. + properties: + name: + description: |- + Name is the name of the HTTP Header to be matched. Name matching MUST be + case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + + + If multiple entries specify equivalent header names, the first entry with + an equivalent name MUST be considered for a match. Subsequent entries + with an equivalent header name MUST be ignored. Due to the + case-insensitivity of header names, "foo" and "Foo" are considered + equivalent. + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP + Header to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + remove: + description: |- + Remove the given header(s) from the HTTP request before the action. The + value of Remove is a list of HTTP header names. Note that the header + names are case-insensitive (see + https://datatracker.ietf.org/doc/html/rfc2616#section-4.2). + + + Input: + GET /foo HTTP/1.1 + my-header1: foo + my-header2: bar + my-header3: baz + + + Config: + remove: ["my-header1", "my-header3"] + + + Output: + GET /foo HTTP/1.1 + my-header2: bar + items: + type: string + maxItems: 16 + type: array + x-kubernetes-list-type: set + set: + description: |- + Set overwrites the request with the given header (name, value) + before the action. + + + Input: + GET /foo HTTP/1.1 + my-header: foo + + + Config: + set: + - name: "my-header" + value: "bar" + + + Output: + GET /foo HTTP/1.1 + my-header: bar + items: + description: HTTPHeader represents an HTTP + Header name and value as defined by RFC + 7230. + properties: + name: + description: |- + Name is the name of the HTTP Header to be matched. Name matching MUST be + case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + + + If multiple entries specify equivalent header names, the first entry with + an equivalent name MUST be considered for a match. Subsequent entries + with an equivalent header name MUST be ignored. Due to the + case-insensitivity of header names, "foo" and "Foo" are considered + equivalent. + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP + Header to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + type: object + requestMirror: + description: |- + RequestMirror defines a schema for a filter that mirrors requests. + Requests are sent to the specified destination, but responses from + that destination are ignored. + + + This filter can be used multiple times within the same rule. Note that + not all implementations will be able to support mirroring to multiple + backends. + + + Support: Extended + properties: + backendRef: + description: |- + BackendRef references a resource where mirrored requests are sent. + + + Mirrored requests must be sent only to a single destination endpoint + within this BackendRef, irrespective of how many endpoints are present + within this BackendRef. + + + If the referent cannot be found, this BackendRef is invalid and must be + dropped from the Gateway. The controller must ensure the "ResolvedRefs" + condition on the Route status is set to `status: False` and not configure + this backend in the underlying implementation. + + + If there is a cross-namespace reference to an *existing* object + that is not allowed by a ReferenceGrant, the controller must ensure the + "ResolvedRefs" condition on the Route is set to `status: False`, + with the "RefNotPermitted" reason and not configure this backend in the + underlying implementation. + + + In either error case, the Message of the `ResolvedRefs` Condition + should be used to provide more detail about the problem. + + + Support: Extended for Kubernetes Service + + + Support: Implementation-specific for any other resource + properties: + group: + default: "" + description: |- + Group is the group of the referent. For example, "gateway.networking.k8s.io". + When unspecified or empty string, core API group is inferred. + maxLength: 253 + pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + kind: + default: Service + description: |- + Kind is the Kubernetes resource kind of the referent. For example + "Service". + + + Defaults to "Service" when not specified. + + + ExternalName services can refer to CNAME DNS records that may live + outside of the cluster and as such are difficult to reason about in + terms of conformance. They also may not be safe to forward to (see + CVE-2021-25740 for more information). Implementations SHOULD NOT + support ExternalName Services. + + + Support: Core (Services with a type other than ExternalName) + + + Support: Implementation-specific (Services with type ExternalName) + maxLength: 63 + minLength: 1 + pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ + type: string + name: + description: Name is the name of the referent. + maxLength: 253 + minLength: 1 + type: string + namespace: + description: |- + Namespace is the namespace of the backend. When unspecified, the local + namespace is inferred. + + + Note that when a namespace different than the local namespace is specified, + a ReferenceGrant object is required in the referent namespace to allow that + namespace's owner to accept the reference. See the ReferenceGrant + documentation for details. + + + Support: Core + maxLength: 63 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ + type: string + port: + description: |- + Port specifies the destination port number to use for this resource. + Port is required when the referent is a Kubernetes Service. In this + case, the port number is the service port number, not the target port. + For other resources, destination port might be derived from the referent + resource or this field. + format: int32 + maximum: 65535 + minimum: 1 + type: integer + required: + - name + type: object + x-kubernetes-validations: + - message: Must have port for Service reference + rule: '(size(self.group) == 0 && self.kind + == ''Service'') ? has(self.port) : true' + required: + - backendRef + type: object + responseHeaderModifier: + description: |- + ResponseHeaderModifier defines a schema for a filter that modifies response + headers. + + + Support: Extended + properties: + add: + description: |- + Add adds the given header(s) (name, value) to the request + before the action. It appends to any existing values associated + with the header name. + + + Input: + GET /foo HTTP/1.1 + my-header: foo + + + Config: + add: + - name: "my-header" + value: "bar,baz" + + + Output: + GET /foo HTTP/1.1 + my-header: foo,bar,baz + items: + description: HTTPHeader represents an HTTP + Header name and value as defined by RFC + 7230. + properties: + name: + description: |- + Name is the name of the HTTP Header to be matched. Name matching MUST be + case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + + + If multiple entries specify equivalent header names, the first entry with + an equivalent name MUST be considered for a match. Subsequent entries + with an equivalent header name MUST be ignored. Due to the + case-insensitivity of header names, "foo" and "Foo" are considered + equivalent. + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP + Header to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + remove: + description: |- + Remove the given header(s) from the HTTP request before the action. The + value of Remove is a list of HTTP header names. Note that the header + names are case-insensitive (see + https://datatracker.ietf.org/doc/html/rfc2616#section-4.2). + + + Input: + GET /foo HTTP/1.1 + my-header1: foo + my-header2: bar + my-header3: baz + + + Config: + remove: ["my-header1", "my-header3"] + + + Output: + GET /foo HTTP/1.1 + my-header2: bar + items: + type: string + maxItems: 16 + type: array + x-kubernetes-list-type: set + set: + description: |- + Set overwrites the request with the given header (name, value) + before the action. + + + Input: + GET /foo HTTP/1.1 + my-header: foo + + + Config: + set: + - name: "my-header" + value: "bar" + + + Output: + GET /foo HTTP/1.1 + my-header: bar + items: + description: HTTPHeader represents an HTTP + Header name and value as defined by RFC + 7230. + properties: + name: + description: |- + Name is the name of the HTTP Header to be matched. Name matching MUST be + case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + + + If multiple entries specify equivalent header names, the first entry with + an equivalent name MUST be considered for a match. Subsequent entries + with an equivalent header name MUST be ignored. Due to the + case-insensitivity of header names, "foo" and "Foo" are considered + equivalent. + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP + Header to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + type: object + type: + description: |+ + Type identifies the type of filter to apply. As with other API fields, + types are classified into three conformance levels: + + + - Core: Filter types and their corresponding configuration defined by + "Support: Core" in this package, e.g. "RequestHeaderModifier". All + implementations supporting GRPCRoute MUST support core filters. + + + - Extended: Filter types and their corresponding configuration defined by + "Support: Extended" in this package, e.g. "RequestMirror". Implementers + are encouraged to support extended filters. + + + - Implementation-specific: Filters that are defined and supported by specific vendors. + In the future, filters showing convergence in behavior across multiple + implementations will be considered for inclusion in extended or core + conformance levels. Filter-specific configuration for such filters + is specified using the ExtensionRef field. `Type` MUST be set to + "ExtensionRef" for custom filters. + + + Implementers are encouraged to define custom implementation types to + extend the core API with implementation-specific behavior. + + + If a reference to a custom filter type cannot be resolved, the filter + MUST NOT be skipped. Instead, requests that would have been processed by + that filter MUST receive a HTTP error response. + + + enum: + - ResponseHeaderModifier + - RequestHeaderModifier + - RequestMirror + - ExtensionRef + type: string + required: + - type + type: object + x-kubernetes-validations: + - message: filter.requestHeaderModifier must be nil + if the filter.type is not RequestHeaderModifier + rule: '!(has(self.requestHeaderModifier) && self.type + != ''RequestHeaderModifier'')' + - message: filter.requestHeaderModifier must be specified + for RequestHeaderModifier filter.type + rule: '!(!has(self.requestHeaderModifier) && self.type + == ''RequestHeaderModifier'')' + - message: filter.responseHeaderModifier must be nil + if the filter.type is not ResponseHeaderModifier + rule: '!(has(self.responseHeaderModifier) && self.type + != ''ResponseHeaderModifier'')' + - message: filter.responseHeaderModifier must be specified + for ResponseHeaderModifier filter.type + rule: '!(!has(self.responseHeaderModifier) && self.type + == ''ResponseHeaderModifier'')' + - message: filter.requestMirror must be nil if the filter.type + is not RequestMirror + rule: '!(has(self.requestMirror) && self.type != ''RequestMirror'')' + - message: filter.requestMirror must be specified for + RequestMirror filter.type + rule: '!(!has(self.requestMirror) && self.type == + ''RequestMirror'')' + - message: filter.extensionRef must be nil if the filter.type + is not ExtensionRef + rule: '!(has(self.extensionRef) && self.type != ''ExtensionRef'')' + - message: filter.extensionRef must be specified for + ExtensionRef filter.type + rule: '!(!has(self.extensionRef) && self.type == ''ExtensionRef'')' + maxItems: 16 + type: array + x-kubernetes-validations: + - message: RequestHeaderModifier filter cannot be repeated + rule: self.filter(f, f.type == 'RequestHeaderModifier').size() + <= 1 + - message: ResponseHeaderModifier filter cannot be repeated + rule: self.filter(f, f.type == 'ResponseHeaderModifier').size() + <= 1 + group: + default: "" + description: |- + Group is the group of the referent. For example, "gateway.networking.k8s.io". + When unspecified or empty string, core API group is inferred. + maxLength: 253 + pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + kind: + default: Service + description: |- + Kind is the Kubernetes resource kind of the referent. For example + "Service". + + + Defaults to "Service" when not specified. + + + ExternalName services can refer to CNAME DNS records that may live + outside of the cluster and as such are difficult to reason about in + terms of conformance. They also may not be safe to forward to (see + CVE-2021-25740 for more information). Implementations SHOULD NOT + support ExternalName Services. + + + Support: Core (Services with a type other than ExternalName) + + + Support: Implementation-specific (Services with type ExternalName) + maxLength: 63 + minLength: 1 + pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ + type: string + name: + description: Name is the name of the referent. + maxLength: 253 + minLength: 1 + type: string + namespace: + description: |- + Namespace is the namespace of the backend. When unspecified, the local + namespace is inferred. + + + Note that when a namespace different than the local namespace is specified, + a ReferenceGrant object is required in the referent namespace to allow that + namespace's owner to accept the reference. See the ReferenceGrant + documentation for details. + + + Support: Core + maxLength: 63 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ + type: string + port: + description: |- + Port specifies the destination port number to use for this resource. + Port is required when the referent is a Kubernetes Service. In this + case, the port number is the service port number, not the target port. + For other resources, destination port might be derived from the referent + resource or this field. + format: int32 + maximum: 65535 + minimum: 1 + type: integer + weight: + default: 1 + description: |- + Weight specifies the proportion of requests forwarded to the referenced + backend. This is computed as weight/(sum of all weights in this + BackendRefs list). For non-zero values, there may be some epsilon from + the exact proportion defined here depending on the precision an + implementation supports. Weight is not a percentage and the sum of + weights does not need to equal 100. + + + If only one backend is specified and it has a weight greater than 0, 100% + of the traffic is forwarded to that backend. If weight is set to 0, no + traffic should be forwarded for this entry. If unspecified, weight + defaults to 1. + + + Support for this field varies based on the context where used. + format: int32 + maximum: 1000000 + minimum: 0 + type: integer + required: + - name + type: object + x-kubernetes-validations: + - message: Must have port for Service reference + rule: '(size(self.group) == 0 && self.kind == ''Service'') + ? has(self.port) : true' + maxItems: 16 + type: array + filters: + description: |- + Filters define the filters that are applied to requests that match + this rule. + + + The effects of ordering of multiple behaviors are currently unspecified. + This can change in the future based on feedback during the alpha stage. + + + Conformance-levels at this level are defined based on the type of filter: + + + - ALL core filters MUST be supported by all implementations that support + GRPCRoute. + - Implementers are encouraged to support extended filters. + - Implementation-specific custom filters have no API guarantees across + implementations. + + + Specifying the same filter multiple times is not supported unless explicitly + indicated in the filter. + + + If an implementation can not support a combination of filters, it must clearly + document that limitation. In cases where incompatible or unsupported + filters are specified and cause the `Accepted` condition to be set to status + `False`, implementations may use the `IncompatibleFilters` reason to specify + this configuration error. + + + Support: Core + items: + description: |- + GRPCRouteFilter defines processing steps that must be completed during the + request or response lifecycle. GRPCRouteFilters are meant as an extension + point to express processing that may be done in Gateway implementations. Some + examples include request or response modification, implementing + authentication strategies, rate-limiting, and traffic shaping. API + guarantee/conformance is defined based on the type of the filter. + properties: + extensionRef: + description: |- + ExtensionRef is an optional, implementation-specific extension to the + "filter" behavior. For example, resource "myroutefilter" in group + "networking.example.net"). ExtensionRef MUST NOT be used for core and + extended filters. + + + Support: Implementation-specific + + + This filter can be used multiple times within the same rule. + properties: + group: + description: |- + Group is the group of the referent. For example, "gateway.networking.k8s.io". + When unspecified or empty string, core API group is inferred. + maxLength: 253 + pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + kind: + description: Kind is kind of the referent. For example + "HTTPRoute" or "Service". + maxLength: 63 + minLength: 1 + pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ + type: string + name: + description: Name is the name of the referent. + maxLength: 253 + minLength: 1 + type: string + required: + - group + - kind + - name + type: object + requestHeaderModifier: + description: |- + RequestHeaderModifier defines a schema for a filter that modifies request + headers. + + + Support: Core + properties: + add: + description: |- + Add adds the given header(s) (name, value) to the request + before the action. It appends to any existing values associated + with the header name. + + + Input: + GET /foo HTTP/1.1 + my-header: foo + + + Config: + add: + - name: "my-header" + value: "bar,baz" + + + Output: + GET /foo HTTP/1.1 + my-header: foo,bar,baz + items: + description: HTTPHeader represents an HTTP Header + name and value as defined by RFC 7230. + properties: + name: + description: |- + Name is the name of the HTTP Header to be matched. Name matching MUST be + case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + + + If multiple entries specify equivalent header names, the first entry with + an equivalent name MUST be considered for a match. Subsequent entries + with an equivalent header name MUST be ignored. Due to the + case-insensitivity of header names, "foo" and "Foo" are considered + equivalent. + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP Header + to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + remove: + description: |- + Remove the given header(s) from the HTTP request before the action. The + value of Remove is a list of HTTP header names. Note that the header + names are case-insensitive (see + https://datatracker.ietf.org/doc/html/rfc2616#section-4.2). + + + Input: + GET /foo HTTP/1.1 + my-header1: foo + my-header2: bar + my-header3: baz + + + Config: + remove: ["my-header1", "my-header3"] + + + Output: + GET /foo HTTP/1.1 + my-header2: bar + items: + type: string + maxItems: 16 + type: array + x-kubernetes-list-type: set + set: + description: |- + Set overwrites the request with the given header (name, value) + before the action. + + + Input: + GET /foo HTTP/1.1 + my-header: foo + + + Config: + set: + - name: "my-header" + value: "bar" + + + Output: + GET /foo HTTP/1.1 + my-header: bar + items: + description: HTTPHeader represents an HTTP Header + name and value as defined by RFC 7230. + properties: + name: + description: |- + Name is the name of the HTTP Header to be matched. Name matching MUST be + case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + + + If multiple entries specify equivalent header names, the first entry with + an equivalent name MUST be considered for a match. Subsequent entries + with an equivalent header name MUST be ignored. Due to the + case-insensitivity of header names, "foo" and "Foo" are considered + equivalent. + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP Header + to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + type: object + requestMirror: + description: |- + RequestMirror defines a schema for a filter that mirrors requests. + Requests are sent to the specified destination, but responses from + that destination are ignored. + + + This filter can be used multiple times within the same rule. Note that + not all implementations will be able to support mirroring to multiple + backends. + + + Support: Extended + properties: + backendRef: + description: |- + BackendRef references a resource where mirrored requests are sent. + + + Mirrored requests must be sent only to a single destination endpoint + within this BackendRef, irrespective of how many endpoints are present + within this BackendRef. + + + If the referent cannot be found, this BackendRef is invalid and must be + dropped from the Gateway. The controller must ensure the "ResolvedRefs" + condition on the Route status is set to `status: False` and not configure + this backend in the underlying implementation. + + + If there is a cross-namespace reference to an *existing* object + that is not allowed by a ReferenceGrant, the controller must ensure the + "ResolvedRefs" condition on the Route is set to `status: False`, + with the "RefNotPermitted" reason and not configure this backend in the + underlying implementation. + + + In either error case, the Message of the `ResolvedRefs` Condition + should be used to provide more detail about the problem. + + + Support: Extended for Kubernetes Service + + + Support: Implementation-specific for any other resource + properties: + group: + default: "" + description: |- + Group is the group of the referent. For example, "gateway.networking.k8s.io". + When unspecified or empty string, core API group is inferred. + maxLength: 253 + pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + kind: + default: Service + description: |- + Kind is the Kubernetes resource kind of the referent. For example + "Service". + + + Defaults to "Service" when not specified. + + + ExternalName services can refer to CNAME DNS records that may live + outside of the cluster and as such are difficult to reason about in + terms of conformance. They also may not be safe to forward to (see + CVE-2021-25740 for more information). Implementations SHOULD NOT + support ExternalName Services. + + + Support: Core (Services with a type other than ExternalName) + + + Support: Implementation-specific (Services with type ExternalName) + maxLength: 63 + minLength: 1 + pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ + type: string + name: + description: Name is the name of the referent. + maxLength: 253 + minLength: 1 + type: string + namespace: + description: |- + Namespace is the namespace of the backend. When unspecified, the local + namespace is inferred. + + + Note that when a namespace different than the local namespace is specified, + a ReferenceGrant object is required in the referent namespace to allow that + namespace's owner to accept the reference. See the ReferenceGrant + documentation for details. + + + Support: Core + maxLength: 63 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ + type: string + port: + description: |- + Port specifies the destination port number to use for this resource. + Port is required when the referent is a Kubernetes Service. In this + case, the port number is the service port number, not the target port. + For other resources, destination port might be derived from the referent + resource or this field. + format: int32 + maximum: 65535 + minimum: 1 + type: integer + required: + - name + type: object + x-kubernetes-validations: + - message: Must have port for Service reference + rule: '(size(self.group) == 0 && self.kind == ''Service'') + ? has(self.port) : true' + required: + - backendRef + type: object + responseHeaderModifier: + description: |- + ResponseHeaderModifier defines a schema for a filter that modifies response + headers. + + + Support: Extended + properties: + add: + description: |- + Add adds the given header(s) (name, value) to the request + before the action. It appends to any existing values associated + with the header name. + + + Input: + GET /foo HTTP/1.1 + my-header: foo + + + Config: + add: + - name: "my-header" + value: "bar,baz" + + + Output: + GET /foo HTTP/1.1 + my-header: foo,bar,baz + items: + description: HTTPHeader represents an HTTP Header + name and value as defined by RFC 7230. + properties: + name: + description: |- + Name is the name of the HTTP Header to be matched. Name matching MUST be + case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + + + If multiple entries specify equivalent header names, the first entry with + an equivalent name MUST be considered for a match. Subsequent entries + with an equivalent header name MUST be ignored. Due to the + case-insensitivity of header names, "foo" and "Foo" are considered + equivalent. + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP Header + to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + remove: + description: |- + Remove the given header(s) from the HTTP request before the action. The + value of Remove is a list of HTTP header names. Note that the header + names are case-insensitive (see + https://datatracker.ietf.org/doc/html/rfc2616#section-4.2). + + + Input: + GET /foo HTTP/1.1 + my-header1: foo + my-header2: bar + my-header3: baz + + + Config: + remove: ["my-header1", "my-header3"] + + + Output: + GET /foo HTTP/1.1 + my-header2: bar + items: + type: string + maxItems: 16 + type: array + x-kubernetes-list-type: set + set: + description: |- + Set overwrites the request with the given header (name, value) + before the action. + + + Input: + GET /foo HTTP/1.1 + my-header: foo + + + Config: + set: + - name: "my-header" + value: "bar" + + + Output: + GET /foo HTTP/1.1 + my-header: bar + items: + description: HTTPHeader represents an HTTP Header + name and value as defined by RFC 7230. + properties: + name: + description: |- + Name is the name of the HTTP Header to be matched. Name matching MUST be + case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + + + If multiple entries specify equivalent header names, the first entry with + an equivalent name MUST be considered for a match. Subsequent entries + with an equivalent header name MUST be ignored. Due to the + case-insensitivity of header names, "foo" and "Foo" are considered + equivalent. + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP Header + to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + type: object + type: + description: |+ + Type identifies the type of filter to apply. As with other API fields, + types are classified into three conformance levels: + + + - Core: Filter types and their corresponding configuration defined by + "Support: Core" in this package, e.g. "RequestHeaderModifier". All + implementations supporting GRPCRoute MUST support core filters. + + + - Extended: Filter types and their corresponding configuration defined by + "Support: Extended" in this package, e.g. "RequestMirror". Implementers + are encouraged to support extended filters. + + + - Implementation-specific: Filters that are defined and supported by specific vendors. + In the future, filters showing convergence in behavior across multiple + implementations will be considered for inclusion in extended or core + conformance levels. Filter-specific configuration for such filters + is specified using the ExtensionRef field. `Type` MUST be set to + "ExtensionRef" for custom filters. + + + Implementers are encouraged to define custom implementation types to + extend the core API with implementation-specific behavior. + + + If a reference to a custom filter type cannot be resolved, the filter + MUST NOT be skipped. Instead, requests that would have been processed by + that filter MUST receive a HTTP error response. + + + enum: + - ResponseHeaderModifier + - RequestHeaderModifier + - RequestMirror + - ExtensionRef + type: string + required: + - type + type: object + x-kubernetes-validations: + - message: filter.requestHeaderModifier must be nil if the + filter.type is not RequestHeaderModifier + rule: '!(has(self.requestHeaderModifier) && self.type != + ''RequestHeaderModifier'')' + - message: filter.requestHeaderModifier must be specified + for RequestHeaderModifier filter.type + rule: '!(!has(self.requestHeaderModifier) && self.type == + ''RequestHeaderModifier'')' + - message: filter.responseHeaderModifier must be nil if the + filter.type is not ResponseHeaderModifier + rule: '!(has(self.responseHeaderModifier) && self.type != + ''ResponseHeaderModifier'')' + - message: filter.responseHeaderModifier must be specified + for ResponseHeaderModifier filter.type + rule: '!(!has(self.responseHeaderModifier) && self.type + == ''ResponseHeaderModifier'')' + - message: filter.requestMirror must be nil if the filter.type + is not RequestMirror + rule: '!(has(self.requestMirror) && self.type != ''RequestMirror'')' + - message: filter.requestMirror must be specified for RequestMirror + filter.type + rule: '!(!has(self.requestMirror) && self.type == ''RequestMirror'')' + - message: filter.extensionRef must be nil if the filter.type + is not ExtensionRef + rule: '!(has(self.extensionRef) && self.type != ''ExtensionRef'')' + - message: filter.extensionRef must be specified for ExtensionRef + filter.type + rule: '!(!has(self.extensionRef) && self.type == ''ExtensionRef'')' + maxItems: 16 + type: array + x-kubernetes-validations: + - message: RequestHeaderModifier filter cannot be repeated + rule: self.filter(f, f.type == 'RequestHeaderModifier').size() + <= 1 + - message: ResponseHeaderModifier filter cannot be repeated + rule: self.filter(f, f.type == 'ResponseHeaderModifier').size() + <= 1 + matches: + description: |- + Matches define conditions used for matching the rule against incoming + gRPC requests. Each match is independent, i.e. this rule will be matched + if **any** one of the matches is satisfied. + + + For example, take the following matches configuration: + + + ``` + matches: + - method: + service: foo.bar + headers: + values: + version: 2 + - method: + service: foo.bar.v2 + ``` + + + For a request to match against this rule, it MUST satisfy + EITHER of the two conditions: + + + - service of foo.bar AND contains the header `version: 2` + - service of foo.bar.v2 + + + See the documentation for GRPCRouteMatch on how to specify multiple + match conditions to be ANDed together. + + + If no matches are specified, the implementation MUST match every gRPC request. + + + Proxy or Load Balancer routing configuration generated from GRPCRoutes + MUST prioritize rules based on the following criteria, continuing on + ties. Merging MUST not be done between GRPCRoutes and HTTPRoutes. + Precedence MUST be given to the rule with the largest number of: + + + * Characters in a matching non-wildcard hostname. + * Characters in a matching hostname. + * Characters in a matching service. + * Characters in a matching method. + * Header matches. + + + If ties still exist across multiple Routes, matching precedence MUST be + determined in order of the following criteria, continuing on ties: + + + * The oldest Route based on creation timestamp. + * The Route appearing first in alphabetical order by + "{namespace}/{name}". + + + If ties still exist within the Route that has been given precedence, + matching precedence MUST be granted to the first matching rule meeting + the above criteria. + items: + description: |- + GRPCRouteMatch defines the predicate used to match requests to a given + action. Multiple match types are ANDed together, i.e. the match will + evaluate to true only if all conditions are satisfied. + + + For example, the match below will match a gRPC request only if its service + is `foo` AND it contains the `version: v1` header: + + + ``` + matches: + - method: + type: Exact + service: "foo" + headers: + - name: "version" + value "v1" + + + ``` + properties: + headers: + description: |- + Headers specifies gRPC request header matchers. Multiple match values are + ANDed together, meaning, a request MUST match all the specified headers + to select the route. + items: + description: |- + GRPCHeaderMatch describes how to select a gRPC route by matching gRPC request + headers. + properties: + name: + description: |- + Name is the name of the gRPC Header to be matched. + + + If multiple entries specify equivalent header names, only the first + entry with an equivalent name MUST be considered for a match. Subsequent + entries with an equivalent header name MUST be ignored. Due to the + case-insensitivity of header names, "foo" and "Foo" are considered + equivalent. + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + type: + default: Exact + description: Type specifies how to match against + the value of the header. + enum: + - Exact + - RegularExpression + type: string + value: + description: Value is the value of the gRPC Header + to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + method: + description: |- + Method specifies a gRPC request service/method matcher. If this field is + not specified, all services and methods will match. + properties: + method: + description: |- + Value of the method to match against. If left empty or omitted, will + match all services. + + + At least one of Service and Method MUST be a non-empty string. + maxLength: 1024 + type: string + service: + description: |- + Value of the service to match against. If left empty or omitted, will + match any service. + + + At least one of Service and Method MUST be a non-empty string. + maxLength: 1024 + type: string + type: + default: Exact + description: |- + Type specifies how to match against the service and/or method. + Support: Core (Exact with service and method specified) + + + Support: Implementation-specific (Exact with method specified but no service specified) + + + Support: Implementation-specific (RegularExpression) + enum: + - Exact + - RegularExpression + type: string + type: object + x-kubernetes-validations: + - message: One or both of 'service' or 'method' must be + specified + rule: 'has(self.type) ? has(self.service) || has(self.method) + : true' + - message: service must only contain valid characters + (matching ^(?i)\.?[a-z_][a-z_0-9]*(\.[a-z_][a-z_0-9]*)*$) + rule: '(!has(self.type) || self.type == ''Exact'') && + has(self.service) ? self.service.matches(r"""^(?i)\.?[a-z_][a-z_0-9]*(\.[a-z_][a-z_0-9]*)*$"""): + true' + - message: method must only contain valid characters (matching + ^[A-Za-z_][A-Za-z_0-9]*$) + rule: '(!has(self.type) || self.type == ''Exact'') && + has(self.method) ? self.method.matches(r"""^[A-Za-z_][A-Za-z_0-9]*$"""): + true' + type: object + maxItems: 8 + type: array + type: object + maxItems: 16 + type: array + type: object + status: + description: Status defines the current state of GRPCRoute. + properties: + parents: + description: |- + Parents is a list of parent resources (usually Gateways) that are + associated with the route, and the status of the route with respect to + each parent. When this route attaches to a parent, the controller that + manages the parent must add an entry to this list when the controller + first sees the route and should update the entry as appropriate when the + route or gateway is modified. + + + Note that parent references that cannot be resolved by an implementation + of this API will not be added to this list. Implementations of this API + can only populate Route status for the Gateways/parent resources they are + responsible for. + + + A maximum of 32 Gateways will be represented in this list. An empty list + means the route has not been attached to any Gateway. + items: + description: |- + RouteParentStatus describes the status of a route with respect to an + associated Parent. + properties: + conditions: + description: |- + Conditions describes the status of the route with respect to the Gateway. + Note that the route's availability is also subject to the Gateway's own + status conditions and listener status. + + + If the Route's ParentRef specifies an existing Gateway that supports + Routes of this kind AND that Gateway's controller has sufficient access, + then that Gateway's controller MUST set the "Accepted" condition on the + Route, to indicate whether the route has been accepted or rejected by the + Gateway, and why. + + + A Route MUST be considered "Accepted" if at least one of the Route's + rules is implemented by the Gateway. + + + There are a number of cases where the "Accepted" condition may not be set + due to lack of controller visibility, that includes when: + + + * The Route refers to a non-existent parent. + * The Route is of a type that the controller does not support. + * The Route is in a namespace the controller does not have access to. + 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 + maxItems: 8 + minItems: 1 + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + controllerName: + description: |- + ControllerName is a domain/path string that indicates the name of the + controller that wrote this status. This corresponds with the + controllerName field on GatewayClass. + + + Example: "example.net/gateway-controller". + + + The format of this field is DOMAIN "/" PATH, where DOMAIN and PATH are + valid Kubernetes names + (https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names). + + + Controllers MUST populate this field when writing status. Controllers should ensure that + entries to status populated with their ControllerName are cleaned up when they are no + longer necessary. + maxLength: 253 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*\/[A-Za-z0-9\/\-._~%!$&'()*+,;=:]+$ + type: string + parentRef: + description: |- + ParentRef corresponds with a ParentRef in the spec that this + RouteParentStatus struct describes the status of. + properties: + group: + default: gateway.networking.k8s.io + description: |- + Group is the group of the referent. + When unspecified, "gateway.networking.k8s.io" is inferred. + To set the core API group (such as for a "Service" kind referent), + Group must be explicitly set to "" (empty string). + + + Support: Core + maxLength: 253 + pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + kind: + default: Gateway + description: |- + Kind is kind of the referent. + + + There are two kinds of parent resources with "Core" support: + + + * Gateway (Gateway conformance profile) + * Service (Mesh conformance profile, ClusterIP Services only) + + + Support for other resources is Implementation-Specific. + maxLength: 63 + minLength: 1 + pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ + type: string + name: + description: |- + Name is the name of the referent. + + + Support: Core + maxLength: 253 + minLength: 1 + type: string + namespace: + description: |- + Namespace is the namespace of the referent. When unspecified, this refers + to the local namespace of the Route. + + + Note that there are specific rules for ParentRefs which cross namespace + boundaries. Cross-namespace references are only valid if they are explicitly + allowed by something in the namespace they are referring to. For example: + Gateway has the AllowedRoutes field, and ReferenceGrant provides a + generic way to enable any other kind of cross-namespace reference. + + + + + + Support: Core + maxLength: 63 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ + type: string + port: + description: |- + Port is the network port this Route targets. It can be interpreted + differently based on the type of parent resource. + + + When the parent resource is a Gateway, this targets all listeners + listening on the specified port that also support this kind of Route(and + select this Route). It's not recommended to set `Port` unless the + networking behaviors specified in a Route must apply to a specific port + as opposed to a listener(s) whose port(s) may be changed. When both Port + and SectionName are specified, the name and port of the selected listener + must match both specified values. + + + + + + Implementations MAY choose to support other parent resources. + Implementations supporting other types of parent resources MUST clearly + document how/if Port is interpreted. + + + For the purpose of status, an attachment is considered successful as + long as the parent resource accepts it partially. For example, Gateway + listeners can restrict which Routes can attach to them by Route kind, + namespace, or hostname. If 1 of 2 Gateway listeners accept attachment + from the referencing Route, the Route MUST be considered successfully + attached. If no Gateway listeners accept attachment from this Route, + the Route MUST be considered detached from the Gateway. + + + Support: Extended + format: int32 + maximum: 65535 + minimum: 1 + type: integer + sectionName: + description: |- + SectionName is the name of a section within the target resource. In the + following resources, SectionName is interpreted as the following: + + + * Gateway: Listener name. When both Port (experimental) and SectionName + are specified, the name and port of the selected listener must match + both specified values. + * Service: Port name. When both Port (experimental) and SectionName + are specified, the name and port of the selected listener must match + both specified values. + + + Implementations MAY choose to support attaching Routes to other resources. + If that is the case, they MUST clearly document how SectionName is + interpreted. + + + When unspecified (empty string), this will reference the entire resource. + For the purpose of status, an attachment is considered successful if at + least one section in the parent resource accepts it. For example, Gateway + listeners can restrict which Routes can attach to them by Route kind, + namespace, or hostname. If 1 of 2 Gateway listeners accept attachment from + the referencing Route, the Route MUST be considered successfully + attached. If no Gateway listeners accept attachment from this Route, the + Route MUST be considered detached from the Gateway. + + + Support: Core + maxLength: 253 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + required: + - name + type: object + required: + - controllerName + - parentRef + type: object + maxItems: 32 + type: array + required: + - parents + type: object + type: object + served: true + storage: true + subresources: + status: {} + - deprecated: true + deprecationWarning: The v1alpha2 version of GRPCRoute has been deprecated and + will be removed in a future release of the API. Please upgrade to v1. + name: v1alpha2 + schema: + openAPIV3Schema: + description: |- + GRPCRoute provides a way to route gRPC requests. This includes the capability + to match requests by hostname, gRPC service, gRPC method, or HTTP/2 header. + Filters can be used to specify additional processing steps. Backends specify + where matching requests will be routed. + + + GRPCRoute falls under extended support within the Gateway API. Within the + following specification, the word "MUST" indicates that an implementation + supporting GRPCRoute must conform to the indicated requirement, but an + implementation not supporting this route type need not follow the requirement + unless explicitly indicated. + + + Implementations supporting `GRPCRoute` with the `HTTPS` `ProtocolType` MUST + accept HTTP/2 connections without an initial upgrade from HTTP/1.1, i.e. via + ALPN. If the implementation does not support this, then it MUST set the + "Accepted" condition to "False" for the affected listener with a reason of + "UnsupportedProtocol". Implementations MAY also accept HTTP/2 connections + with an upgrade from HTTP/1. + + + Implementations supporting `GRPCRoute` with the `HTTP` `ProtocolType` MUST + support HTTP/2 over cleartext TCP (h2c, + https://www.rfc-editor.org/rfc/rfc7540#section-3.1) without an initial + upgrade from HTTP/1.1, i.e. with prior knowledge + (https://www.rfc-editor.org/rfc/rfc7540#section-3.4). If the implementation + does not support this, then it MUST set the "Accepted" condition to "False" + for the affected listener with a reason of "UnsupportedProtocol". + Implementations MAY also accept HTTP/2 connections with an upgrade from + HTTP/1, i.e. without prior knowledge. + 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: Spec defines the desired state of GRPCRoute. + properties: + hostnames: + description: |- + Hostnames defines a set of hostnames to match against the GRPC + Host header to select a GRPCRoute to process the request. This matches + the RFC 1123 definition of a hostname with 2 notable exceptions: + + + 1. IPs are not allowed. + 2. A hostname may be prefixed with a wildcard label (`*.`). The wildcard + label MUST appear by itself as the first label. + + + If a hostname is specified by both the Listener and GRPCRoute, there + MUST be at least one intersecting hostname for the GRPCRoute to be + attached to the Listener. For example: + + + * A Listener with `test.example.com` as the hostname matches GRPCRoutes + that have either not specified any hostnames, or have specified at + least one of `test.example.com` or `*.example.com`. + * A Listener with `*.example.com` as the hostname matches GRPCRoutes + that have either not specified any hostnames or have specified at least + one hostname that matches the Listener hostname. For example, + `test.example.com` and `*.example.com` would both match. On the other + hand, `example.com` and `test.example.net` would not match. + + + Hostnames that are prefixed with a wildcard label (`*.`) are interpreted + as a suffix match. That means that a match for `*.example.com` would match + both `test.example.com`, and `foo.test.example.com`, but not `example.com`. + + + If both the Listener and GRPCRoute have specified hostnames, any + GRPCRoute hostnames that do not match the Listener hostname MUST be + ignored. For example, if a Listener specified `*.example.com`, and the + GRPCRoute specified `test.example.com` and `test.example.net`, + `test.example.net` MUST NOT be considered for a match. + + + If both the Listener and GRPCRoute have specified hostnames, and none + match with the criteria above, then the GRPCRoute MUST NOT be accepted by + the implementation. The implementation MUST raise an 'Accepted' Condition + with a status of `False` in the corresponding RouteParentStatus. + + + If a Route (A) of type HTTPRoute or GRPCRoute is attached to a + Listener and that listener already has another Route (B) of the other + type attached and the intersection of the hostnames of A and B is + non-empty, then the implementation MUST accept exactly one of these two + routes, determined by the following criteria, in order: + + + * The oldest Route based on creation timestamp. + * The Route appearing first in alphabetical order by + "{namespace}/{name}". + + + The rejected Route MUST raise an 'Accepted' condition with a status of + 'False' in the corresponding RouteParentStatus. + + + Support: Core + items: + description: |- + Hostname is the fully qualified domain name of a network host. This matches + the RFC 1123 definition of a hostname with 2 notable exceptions: + + + 1. IPs are not allowed. + 2. A hostname may be prefixed with a wildcard label (`*.`). The wildcard + label must appear by itself as the first label. + + + Hostname can be "precise" which is a domain name without the terminating + dot of a network host (e.g. "foo.example.com") or "wildcard", which is a + domain name prefixed with a single wildcard label (e.g. `*.example.com`). + + + Note that as per RFC1035 and RFC1123, a *label* must consist of lower case + alphanumeric characters or '-', and must start and end with an alphanumeric + character. No other punctuation is allowed. + maxLength: 253 + minLength: 1 + pattern: ^(\*\.)?[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + maxItems: 16 + type: array + parentRefs: + description: |+ + ParentRefs references the resources (usually Gateways) that a Route wants + to be attached to. Note that the referenced parent resource needs to + allow this for the attachment to be complete. For Gateways, that means + the Gateway needs to allow attachment from Routes of this kind and + namespace. For Services, that means the Service must either be in the same + namespace for a "producer" route, or the mesh implementation must support + and allow "consumer" routes for the referenced Service. ReferenceGrant is + not applicable for governing ParentRefs to Services - it is not possible to + create a "producer" route for a Service in a different namespace from the + Route. + + + There are two kinds of parent resources with "Core" support: + + + * Gateway (Gateway conformance profile) + * Service (Mesh conformance profile, ClusterIP Services only) + + + This API may be extended in the future to support additional kinds of parent + resources. + + + ParentRefs must be _distinct_. This means either that: + + + * They select different objects. If this is the case, then parentRef + entries are distinct. In terms of fields, this means that the + multi-part key defined by `group`, `kind`, `namespace`, and `name` must + be unique across all parentRef entries in the Route. + * They do not select different objects, but for each optional field used, + each ParentRef that selects the same object must set the same set of + optional fields to different values. If one ParentRef sets a + combination of optional fields, all must set the same combination. + + + Some examples: + + + * If one ParentRef sets `sectionName`, all ParentRefs referencing the + same object must also set `sectionName`. + * If one ParentRef sets `port`, all ParentRefs referencing the same + object must also set `port`. + * If one ParentRef sets `sectionName` and `port`, all ParentRefs + referencing the same object must also set `sectionName` and `port`. + + + It is possible to separately reference multiple distinct objects that may + be collapsed by an implementation. For example, some implementations may + choose to merge compatible Gateway Listeners together. If that is the + case, the list of routes attached to those resources should also be + merged. + + + Note that for ParentRefs that cross namespace boundaries, there are specific + rules. Cross-namespace references are only valid if they are explicitly + allowed by something in the namespace they are referring to. For example, + Gateway has the AllowedRoutes field, and ReferenceGrant provides a + generic way to enable other kinds of cross-namespace reference. + + + + + + + + + items: + description: |- + ParentReference identifies an API object (usually a Gateway) that can be considered + a parent of this resource (usually a route). There are two kinds of parent resources + with "Core" support: + + + * Gateway (Gateway conformance profile) + * Service (Mesh conformance profile, ClusterIP Services only) + + + This API may be extended in the future to support additional kinds of parent + resources. + + + The API object must be valid in the cluster; the Group and Kind must + be registered in the cluster for this reference to be valid. + properties: + group: + default: gateway.networking.k8s.io + description: |- + Group is the group of the referent. + When unspecified, "gateway.networking.k8s.io" is inferred. + To set the core API group (such as for a "Service" kind referent), + Group must be explicitly set to "" (empty string). + + + Support: Core + maxLength: 253 + pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + kind: + default: Gateway + description: |- + Kind is kind of the referent. + + + There are two kinds of parent resources with "Core" support: + + + * Gateway (Gateway conformance profile) + * Service (Mesh conformance profile, ClusterIP Services only) + + + Support for other resources is Implementation-Specific. + maxLength: 63 + minLength: 1 + pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ + type: string + name: + description: |- + Name is the name of the referent. + + + Support: Core + maxLength: 253 + minLength: 1 + type: string + namespace: + description: |- + Namespace is the namespace of the referent. When unspecified, this refers + to the local namespace of the Route. + + + Note that there are specific rules for ParentRefs which cross namespace + boundaries. Cross-namespace references are only valid if they are explicitly + allowed by something in the namespace they are referring to. For example: + Gateway has the AllowedRoutes field, and ReferenceGrant provides a + generic way to enable any other kind of cross-namespace reference. + + + + + + Support: Core + maxLength: 63 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ + type: string + port: + description: |- + Port is the network port this Route targets. It can be interpreted + differently based on the type of parent resource. + + + When the parent resource is a Gateway, this targets all listeners + listening on the specified port that also support this kind of Route(and + select this Route). It's not recommended to set `Port` unless the + networking behaviors specified in a Route must apply to a specific port + as opposed to a listener(s) whose port(s) may be changed. When both Port + and SectionName are specified, the name and port of the selected listener + must match both specified values. + + + + + + Implementations MAY choose to support other parent resources. + Implementations supporting other types of parent resources MUST clearly + document how/if Port is interpreted. + + + For the purpose of status, an attachment is considered successful as + long as the parent resource accepts it partially. For example, Gateway + listeners can restrict which Routes can attach to them by Route kind, + namespace, or hostname. If 1 of 2 Gateway listeners accept attachment + from the referencing Route, the Route MUST be considered successfully + attached. If no Gateway listeners accept attachment from this Route, + the Route MUST be considered detached from the Gateway. + + + Support: Extended + format: int32 + maximum: 65535 + minimum: 1 + type: integer + sectionName: + description: |- + SectionName is the name of a section within the target resource. In the + following resources, SectionName is interpreted as the following: + + + * Gateway: Listener name. When both Port (experimental) and SectionName + are specified, the name and port of the selected listener must match + both specified values. + * Service: Port name. When both Port (experimental) and SectionName + are specified, the name and port of the selected listener must match + both specified values. + + + Implementations MAY choose to support attaching Routes to other resources. + If that is the case, they MUST clearly document how SectionName is + interpreted. + + + When unspecified (empty string), this will reference the entire resource. + For the purpose of status, an attachment is considered successful if at + least one section in the parent resource accepts it. For example, Gateway + listeners can restrict which Routes can attach to them by Route kind, + namespace, or hostname. If 1 of 2 Gateway listeners accept attachment from + the referencing Route, the Route MUST be considered successfully + attached. If no Gateway listeners accept attachment from this Route, the + Route MUST be considered detached from the Gateway. + + + Support: Core + maxLength: 253 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + required: + - name + type: object + maxItems: 32 + type: array + x-kubernetes-validations: + - message: sectionName must be specified when parentRefs includes + 2 or more references to the same parent + rule: 'self.all(p1, self.all(p2, p1.group == p2.group && p1.kind + == p2.kind && p1.name == p2.name && (((!has(p1.__namespace__) + || p1.__namespace__ == '''') && (!has(p2.__namespace__) || p2.__namespace__ + == '''')) || (has(p1.__namespace__) && has(p2.__namespace__) && + p1.__namespace__ == p2.__namespace__ )) ? ((!has(p1.sectionName) + || p1.sectionName == '''') == (!has(p2.sectionName) || p2.sectionName + == '''')) : true))' + - message: sectionName must be unique when parentRefs includes 2 or + more references to the same parent + rule: self.all(p1, self.exists_one(p2, p1.group == p2.group && p1.kind + == p2.kind && p1.name == p2.name && (((!has(p1.__namespace__) + || p1.__namespace__ == '') && (!has(p2.__namespace__) || p2.__namespace__ + == '')) || (has(p1.__namespace__) && has(p2.__namespace__) && + p1.__namespace__ == p2.__namespace__ )) && (((!has(p1.sectionName) + || p1.sectionName == '') && (!has(p2.sectionName) || p2.sectionName + == '')) || (has(p1.sectionName) && has(p2.sectionName) && p1.sectionName + == p2.sectionName)))) + rules: + description: Rules are a list of GRPC matchers, filters and actions. + items: + description: |- + GRPCRouteRule defines the semantics for matching a gRPC request based on + conditions (matches), processing it (filters), and forwarding the request to + an API object (backendRefs). + properties: + backendRefs: + description: |- + BackendRefs defines the backend(s) where matching requests should be + sent. + + + Failure behavior here depends on how many BackendRefs are specified and + how many are invalid. + + + If *all* entries in BackendRefs are invalid, and there are also no filters + specified in this route rule, *all* traffic which matches this rule MUST + receive an `UNAVAILABLE` status. + + + See the GRPCBackendRef definition for the rules about what makes a single + GRPCBackendRef invalid. + + + When a GRPCBackendRef is invalid, `UNAVAILABLE` statuses MUST be returned for + requests that would have otherwise been routed to an invalid backend. If + multiple backends are specified, and some are invalid, the proportion of + requests that would otherwise have been routed to an invalid backend + MUST receive an `UNAVAILABLE` status. + + + For example, if two backends are specified with equal weights, and one is + invalid, 50 percent of traffic MUST receive an `UNAVAILABLE` status. + Implementations may choose how that 50 percent is determined. + + + Support: Core for Kubernetes Service + + + Support: Implementation-specific for any other resource + + + Support for weight: Core + items: + description: |- + GRPCBackendRef defines how a GRPCRoute forwards a gRPC request. + + + Note that when a namespace different than the local namespace is specified, a + ReferenceGrant object is required in the referent namespace to allow that + namespace's owner to accept the reference. See the ReferenceGrant + documentation for details. + + + + + + When the BackendRef points to a Kubernetes Service, implementations SHOULD + honor the appProtocol field if it is set for the target Service Port. + + + Implementations supporting appProtocol SHOULD recognize the Kubernetes + Standard Application Protocols defined in KEP-3726. + + + If a Service appProtocol isn't specified, an implementation MAY infer the + backend protocol through its own means. Implementations MAY infer the + protocol from the Route type referring to the backend Service. + + + If a Route is not able to send traffic to the backend using the specified + protocol then the backend is considered invalid. Implementations MUST set the + "ResolvedRefs" condition to "False" with the "UnsupportedProtocol" reason. + + + + properties: + filters: + description: |- + Filters defined at this level MUST be executed if and only if the + request is being forwarded to the backend defined here. + + + Support: Implementation-specific (For broader support of filters, use the + Filters field in GRPCRouteRule.) + items: + description: |- + GRPCRouteFilter defines processing steps that must be completed during the + request or response lifecycle. GRPCRouteFilters are meant as an extension + point to express processing that may be done in Gateway implementations. Some + examples include request or response modification, implementing + authentication strategies, rate-limiting, and traffic shaping. API + guarantee/conformance is defined based on the type of the filter. + properties: + extensionRef: + description: |- + ExtensionRef is an optional, implementation-specific extension to the + "filter" behavior. For example, resource "myroutefilter" in group + "networking.example.net"). ExtensionRef MUST NOT be used for core and + extended filters. + + + Support: Implementation-specific + + + This filter can be used multiple times within the same rule. + properties: + group: + description: |- + Group is the group of the referent. For example, "gateway.networking.k8s.io". + When unspecified or empty string, core API group is inferred. + maxLength: 253 + pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + kind: + description: Kind is kind of the referent. For + example "HTTPRoute" or "Service". + maxLength: 63 + minLength: 1 + pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ + type: string + name: + description: Name is the name of the referent. + maxLength: 253 + minLength: 1 + type: string + required: + - group + - kind + - name + type: object + requestHeaderModifier: + description: |- + RequestHeaderModifier defines a schema for a filter that modifies request + headers. + + + Support: Core + properties: + add: + description: |- + Add adds the given header(s) (name, value) to the request + before the action. It appends to any existing values associated + with the header name. + + + Input: + GET /foo HTTP/1.1 + my-header: foo + + + Config: + add: + - name: "my-header" + value: "bar,baz" + + + Output: + GET /foo HTTP/1.1 + my-header: foo,bar,baz + items: + description: HTTPHeader represents an HTTP + Header name and value as defined by RFC + 7230. + properties: + name: + description: |- + Name is the name of the HTTP Header to be matched. Name matching MUST be + case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + + + If multiple entries specify equivalent header names, the first entry with + an equivalent name MUST be considered for a match. Subsequent entries + with an equivalent header name MUST be ignored. Due to the + case-insensitivity of header names, "foo" and "Foo" are considered + equivalent. + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP + Header to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + remove: + description: |- + Remove the given header(s) from the HTTP request before the action. The + value of Remove is a list of HTTP header names. Note that the header + names are case-insensitive (see + https://datatracker.ietf.org/doc/html/rfc2616#section-4.2). + + + Input: + GET /foo HTTP/1.1 + my-header1: foo + my-header2: bar + my-header3: baz + + + Config: + remove: ["my-header1", "my-header3"] + + + Output: + GET /foo HTTP/1.1 + my-header2: bar + items: + type: string + maxItems: 16 + type: array + x-kubernetes-list-type: set + set: + description: |- + Set overwrites the request with the given header (name, value) + before the action. + + + Input: + GET /foo HTTP/1.1 + my-header: foo + + + Config: + set: + - name: "my-header" + value: "bar" + + + Output: + GET /foo HTTP/1.1 + my-header: bar + items: + description: HTTPHeader represents an HTTP + Header name and value as defined by RFC + 7230. + properties: + name: + description: |- + Name is the name of the HTTP Header to be matched. Name matching MUST be + case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + + + If multiple entries specify equivalent header names, the first entry with + an equivalent name MUST be considered for a match. Subsequent entries + with an equivalent header name MUST be ignored. Due to the + case-insensitivity of header names, "foo" and "Foo" are considered + equivalent. + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP + Header to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + type: object + requestMirror: + description: |- + RequestMirror defines a schema for a filter that mirrors requests. + Requests are sent to the specified destination, but responses from + that destination are ignored. + + + This filter can be used multiple times within the same rule. Note that + not all implementations will be able to support mirroring to multiple + backends. + + + Support: Extended + properties: + backendRef: + description: |- + BackendRef references a resource where mirrored requests are sent. + + + Mirrored requests must be sent only to a single destination endpoint + within this BackendRef, irrespective of how many endpoints are present + within this BackendRef. + + + If the referent cannot be found, this BackendRef is invalid and must be + dropped from the Gateway. The controller must ensure the "ResolvedRefs" + condition on the Route status is set to `status: False` and not configure + this backend in the underlying implementation. + + + If there is a cross-namespace reference to an *existing* object + that is not allowed by a ReferenceGrant, the controller must ensure the + "ResolvedRefs" condition on the Route is set to `status: False`, + with the "RefNotPermitted" reason and not configure this backend in the + underlying implementation. + + + In either error case, the Message of the `ResolvedRefs` Condition + should be used to provide more detail about the problem. + + + Support: Extended for Kubernetes Service + + + Support: Implementation-specific for any other resource + properties: + group: + default: "" + description: |- + Group is the group of the referent. For example, "gateway.networking.k8s.io". + When unspecified or empty string, core API group is inferred. + maxLength: 253 + pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + kind: + default: Service + description: |- + Kind is the Kubernetes resource kind of the referent. For example + "Service". + + + Defaults to "Service" when not specified. + + + ExternalName services can refer to CNAME DNS records that may live + outside of the cluster and as such are difficult to reason about in + terms of conformance. They also may not be safe to forward to (see + CVE-2021-25740 for more information). Implementations SHOULD NOT + support ExternalName Services. + + + Support: Core (Services with a type other than ExternalName) + + + Support: Implementation-specific (Services with type ExternalName) + maxLength: 63 + minLength: 1 + pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ + type: string + name: + description: Name is the name of the referent. + maxLength: 253 + minLength: 1 + type: string + namespace: + description: |- + Namespace is the namespace of the backend. When unspecified, the local + namespace is inferred. + + + Note that when a namespace different than the local namespace is specified, + a ReferenceGrant object is required in the referent namespace to allow that + namespace's owner to accept the reference. See the ReferenceGrant + documentation for details. + + + Support: Core + maxLength: 63 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ + type: string + port: + description: |- + Port specifies the destination port number to use for this resource. + Port is required when the referent is a Kubernetes Service. In this + case, the port number is the service port number, not the target port. + For other resources, destination port might be derived from the referent + resource or this field. + format: int32 + maximum: 65535 + minimum: 1 + type: integer + required: + - name + type: object + x-kubernetes-validations: + - message: Must have port for Service reference + rule: '(size(self.group) == 0 && self.kind + == ''Service'') ? has(self.port) : true' + required: + - backendRef + type: object + responseHeaderModifier: + description: |- + ResponseHeaderModifier defines a schema for a filter that modifies response + headers. + + + Support: Extended + properties: + add: + description: |- + Add adds the given header(s) (name, value) to the request + before the action. It appends to any existing values associated + with the header name. + + + Input: + GET /foo HTTP/1.1 + my-header: foo + + + Config: + add: + - name: "my-header" + value: "bar,baz" + + + Output: + GET /foo HTTP/1.1 + my-header: foo,bar,baz + items: + description: HTTPHeader represents an HTTP + Header name and value as defined by RFC + 7230. + properties: + name: + description: |- + Name is the name of the HTTP Header to be matched. Name matching MUST be + case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + + + If multiple entries specify equivalent header names, the first entry with + an equivalent name MUST be considered for a match. Subsequent entries + with an equivalent header name MUST be ignored. Due to the + case-insensitivity of header names, "foo" and "Foo" are considered + equivalent. + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP + Header to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + remove: + description: |- + Remove the given header(s) from the HTTP request before the action. The + value of Remove is a list of HTTP header names. Note that the header + names are case-insensitive (see + https://datatracker.ietf.org/doc/html/rfc2616#section-4.2). + + + Input: + GET /foo HTTP/1.1 + my-header1: foo + my-header2: bar + my-header3: baz + + + Config: + remove: ["my-header1", "my-header3"] + + + Output: + GET /foo HTTP/1.1 + my-header2: bar + items: + type: string + maxItems: 16 + type: array + x-kubernetes-list-type: set + set: + description: |- + Set overwrites the request with the given header (name, value) + before the action. + + + Input: + GET /foo HTTP/1.1 + my-header: foo + + + Config: + set: + - name: "my-header" + value: "bar" + + + Output: + GET /foo HTTP/1.1 + my-header: bar + items: + description: HTTPHeader represents an HTTP + Header name and value as defined by RFC + 7230. + properties: + name: + description: |- + Name is the name of the HTTP Header to be matched. Name matching MUST be + case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + + + If multiple entries specify equivalent header names, the first entry with + an equivalent name MUST be considered for a match. Subsequent entries + with an equivalent header name MUST be ignored. Due to the + case-insensitivity of header names, "foo" and "Foo" are considered + equivalent. + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP + Header to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + type: object + type: + description: |+ + Type identifies the type of filter to apply. As with other API fields, + types are classified into three conformance levels: + + + - Core: Filter types and their corresponding configuration defined by + "Support: Core" in this package, e.g. "RequestHeaderModifier". All + implementations supporting GRPCRoute MUST support core filters. + + + - Extended: Filter types and their corresponding configuration defined by + "Support: Extended" in this package, e.g. "RequestMirror". Implementers + are encouraged to support extended filters. + + + - Implementation-specific: Filters that are defined and supported by specific vendors. + In the future, filters showing convergence in behavior across multiple + implementations will be considered for inclusion in extended or core + conformance levels. Filter-specific configuration for such filters + is specified using the ExtensionRef field. `Type` MUST be set to + "ExtensionRef" for custom filters. + + + Implementers are encouraged to define custom implementation types to + extend the core API with implementation-specific behavior. + + + If a reference to a custom filter type cannot be resolved, the filter + MUST NOT be skipped. Instead, requests that would have been processed by + that filter MUST receive a HTTP error response. + + + enum: + - ResponseHeaderModifier + - RequestHeaderModifier + - RequestMirror + - ExtensionRef + type: string + required: + - type + type: object + x-kubernetes-validations: + - message: filter.requestHeaderModifier must be nil + if the filter.type is not RequestHeaderModifier + rule: '!(has(self.requestHeaderModifier) && self.type + != ''RequestHeaderModifier'')' + - message: filter.requestHeaderModifier must be specified + for RequestHeaderModifier filter.type + rule: '!(!has(self.requestHeaderModifier) && self.type + == ''RequestHeaderModifier'')' + - message: filter.responseHeaderModifier must be nil + if the filter.type is not ResponseHeaderModifier + rule: '!(has(self.responseHeaderModifier) && self.type + != ''ResponseHeaderModifier'')' + - message: filter.responseHeaderModifier must be specified + for ResponseHeaderModifier filter.type + rule: '!(!has(self.responseHeaderModifier) && self.type + == ''ResponseHeaderModifier'')' + - message: filter.requestMirror must be nil if the filter.type + is not RequestMirror + rule: '!(has(self.requestMirror) && self.type != ''RequestMirror'')' + - message: filter.requestMirror must be specified for + RequestMirror filter.type + rule: '!(!has(self.requestMirror) && self.type == + ''RequestMirror'')' + - message: filter.extensionRef must be nil if the filter.type + is not ExtensionRef + rule: '!(has(self.extensionRef) && self.type != ''ExtensionRef'')' + - message: filter.extensionRef must be specified for + ExtensionRef filter.type + rule: '!(!has(self.extensionRef) && self.type == ''ExtensionRef'')' + maxItems: 16 + type: array + x-kubernetes-validations: + - message: RequestHeaderModifier filter cannot be repeated + rule: self.filter(f, f.type == 'RequestHeaderModifier').size() + <= 1 + - message: ResponseHeaderModifier filter cannot be repeated + rule: self.filter(f, f.type == 'ResponseHeaderModifier').size() + <= 1 + group: + default: "" + description: |- + Group is the group of the referent. For example, "gateway.networking.k8s.io". + When unspecified or empty string, core API group is inferred. + maxLength: 253 + pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + kind: + default: Service + description: |- + Kind is the Kubernetes resource kind of the referent. For example + "Service". + + + Defaults to "Service" when not specified. + + + ExternalName services can refer to CNAME DNS records that may live + outside of the cluster and as such are difficult to reason about in + terms of conformance. They also may not be safe to forward to (see + CVE-2021-25740 for more information). Implementations SHOULD NOT + support ExternalName Services. + + + Support: Core (Services with a type other than ExternalName) + + + Support: Implementation-specific (Services with type ExternalName) + maxLength: 63 + minLength: 1 + pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ + type: string + name: + description: Name is the name of the referent. + maxLength: 253 + minLength: 1 + type: string + namespace: + description: |- + Namespace is the namespace of the backend. When unspecified, the local + namespace is inferred. + + + Note that when a namespace different than the local namespace is specified, + a ReferenceGrant object is required in the referent namespace to allow that + namespace's owner to accept the reference. See the ReferenceGrant + documentation for details. + + + Support: Core + maxLength: 63 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ + type: string + port: + description: |- + Port specifies the destination port number to use for this resource. + Port is required when the referent is a Kubernetes Service. In this + case, the port number is the service port number, not the target port. + For other resources, destination port might be derived from the referent + resource or this field. + format: int32 + maximum: 65535 + minimum: 1 + type: integer + weight: + default: 1 + description: |- + Weight specifies the proportion of requests forwarded to the referenced + backend. This is computed as weight/(sum of all weights in this + BackendRefs list). For non-zero values, there may be some epsilon from + the exact proportion defined here depending on the precision an + implementation supports. Weight is not a percentage and the sum of + weights does not need to equal 100. + + + If only one backend is specified and it has a weight greater than 0, 100% + of the traffic is forwarded to that backend. If weight is set to 0, no + traffic should be forwarded for this entry. If unspecified, weight + defaults to 1. + + + Support for this field varies based on the context where used. + format: int32 + maximum: 1000000 + minimum: 0 + type: integer + required: + - name + type: object + x-kubernetes-validations: + - message: Must have port for Service reference + rule: '(size(self.group) == 0 && self.kind == ''Service'') + ? has(self.port) : true' + maxItems: 16 + type: array + filters: + description: |- + Filters define the filters that are applied to requests that match + this rule. + + + The effects of ordering of multiple behaviors are currently unspecified. + This can change in the future based on feedback during the alpha stage. + + + Conformance-levels at this level are defined based on the type of filter: + + + - ALL core filters MUST be supported by all implementations that support + GRPCRoute. + - Implementers are encouraged to support extended filters. + - Implementation-specific custom filters have no API guarantees across + implementations. + + + Specifying the same filter multiple times is not supported unless explicitly + indicated in the filter. + + + If an implementation can not support a combination of filters, it must clearly + document that limitation. In cases where incompatible or unsupported + filters are specified and cause the `Accepted` condition to be set to status + `False`, implementations may use the `IncompatibleFilters` reason to specify + this configuration error. + + + Support: Core + items: + description: |- + GRPCRouteFilter defines processing steps that must be completed during the + request or response lifecycle. GRPCRouteFilters are meant as an extension + point to express processing that may be done in Gateway implementations. Some + examples include request or response modification, implementing + authentication strategies, rate-limiting, and traffic shaping. API + guarantee/conformance is defined based on the type of the filter. + properties: + extensionRef: + description: |- + ExtensionRef is an optional, implementation-specific extension to the + "filter" behavior. For example, resource "myroutefilter" in group + "networking.example.net"). ExtensionRef MUST NOT be used for core and + extended filters. + + + Support: Implementation-specific + + + This filter can be used multiple times within the same rule. + properties: + group: + description: |- + Group is the group of the referent. For example, "gateway.networking.k8s.io". + When unspecified or empty string, core API group is inferred. + maxLength: 253 + pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + kind: + description: Kind is kind of the referent. For example + "HTTPRoute" or "Service". + maxLength: 63 + minLength: 1 + pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ + type: string + name: + description: Name is the name of the referent. + maxLength: 253 + minLength: 1 + type: string + required: + - group + - kind + - name + type: object + requestHeaderModifier: + description: |- + RequestHeaderModifier defines a schema for a filter that modifies request + headers. + + + Support: Core + properties: + add: + description: |- + Add adds the given header(s) (name, value) to the request + before the action. It appends to any existing values associated + with the header name. + + + Input: + GET /foo HTTP/1.1 + my-header: foo + + + Config: + add: + - name: "my-header" + value: "bar,baz" + + + Output: + GET /foo HTTP/1.1 + my-header: foo,bar,baz + items: + description: HTTPHeader represents an HTTP Header + name and value as defined by RFC 7230. + properties: + name: + description: |- + Name is the name of the HTTP Header to be matched. Name matching MUST be + case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + + + If multiple entries specify equivalent header names, the first entry with + an equivalent name MUST be considered for a match. Subsequent entries + with an equivalent header name MUST be ignored. Due to the + case-insensitivity of header names, "foo" and "Foo" are considered + equivalent. + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP Header + to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + remove: + description: |- + Remove the given header(s) from the HTTP request before the action. The + value of Remove is a list of HTTP header names. Note that the header + names are case-insensitive (see + https://datatracker.ietf.org/doc/html/rfc2616#section-4.2). + + + Input: + GET /foo HTTP/1.1 + my-header1: foo + my-header2: bar + my-header3: baz + + + Config: + remove: ["my-header1", "my-header3"] + + + Output: + GET /foo HTTP/1.1 + my-header2: bar + items: + type: string + maxItems: 16 + type: array + x-kubernetes-list-type: set + set: + description: |- + Set overwrites the request with the given header (name, value) + before the action. + + + Input: + GET /foo HTTP/1.1 + my-header: foo + + + Config: + set: + - name: "my-header" + value: "bar" + + + Output: + GET /foo HTTP/1.1 + my-header: bar + items: + description: HTTPHeader represents an HTTP Header + name and value as defined by RFC 7230. + properties: + name: + description: |- + Name is the name of the HTTP Header to be matched. Name matching MUST be + case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + + + If multiple entries specify equivalent header names, the first entry with + an equivalent name MUST be considered for a match. Subsequent entries + with an equivalent header name MUST be ignored. Due to the + case-insensitivity of header names, "foo" and "Foo" are considered + equivalent. + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP Header + to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + type: object + requestMirror: + description: |- + RequestMirror defines a schema for a filter that mirrors requests. + Requests are sent to the specified destination, but responses from + that destination are ignored. + + + This filter can be used multiple times within the same rule. Note that + not all implementations will be able to support mirroring to multiple + backends. + + + Support: Extended + properties: + backendRef: + description: |- + BackendRef references a resource where mirrored requests are sent. + + + Mirrored requests must be sent only to a single destination endpoint + within this BackendRef, irrespective of how many endpoints are present + within this BackendRef. + + + If the referent cannot be found, this BackendRef is invalid and must be + dropped from the Gateway. The controller must ensure the "ResolvedRefs" + condition on the Route status is set to `status: False` and not configure + this backend in the underlying implementation. + + + If there is a cross-namespace reference to an *existing* object + that is not allowed by a ReferenceGrant, the controller must ensure the + "ResolvedRefs" condition on the Route is set to `status: False`, + with the "RefNotPermitted" reason and not configure this backend in the + underlying implementation. + + + In either error case, the Message of the `ResolvedRefs` Condition + should be used to provide more detail about the problem. + + + Support: Extended for Kubernetes Service + + + Support: Implementation-specific for any other resource + properties: + group: + default: "" + description: |- + Group is the group of the referent. For example, "gateway.networking.k8s.io". + When unspecified or empty string, core API group is inferred. + maxLength: 253 + pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + kind: + default: Service + description: |- + Kind is the Kubernetes resource kind of the referent. For example + "Service". + + + Defaults to "Service" when not specified. + + + ExternalName services can refer to CNAME DNS records that may live + outside of the cluster and as such are difficult to reason about in + terms of conformance. They also may not be safe to forward to (see + CVE-2021-25740 for more information). Implementations SHOULD NOT + support ExternalName Services. + + + Support: Core (Services with a type other than ExternalName) + + + Support: Implementation-specific (Services with type ExternalName) + maxLength: 63 + minLength: 1 + pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ + type: string + name: + description: Name is the name of the referent. + maxLength: 253 + minLength: 1 + type: string + namespace: + description: |- + Namespace is the namespace of the backend. When unspecified, the local + namespace is inferred. + + + Note that when a namespace different than the local namespace is specified, + a ReferenceGrant object is required in the referent namespace to allow that + namespace's owner to accept the reference. See the ReferenceGrant + documentation for details. + + + Support: Core + maxLength: 63 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ + type: string + port: + description: |- + Port specifies the destination port number to use for this resource. + Port is required when the referent is a Kubernetes Service. In this + case, the port number is the service port number, not the target port. + For other resources, destination port might be derived from the referent + resource or this field. + format: int32 + maximum: 65535 + minimum: 1 + type: integer + required: + - name + type: object + x-kubernetes-validations: + - message: Must have port for Service reference + rule: '(size(self.group) == 0 && self.kind == ''Service'') + ? has(self.port) : true' + required: + - backendRef + type: object + responseHeaderModifier: + description: |- + ResponseHeaderModifier defines a schema for a filter that modifies response + headers. + + + Support: Extended + properties: + add: + description: |- + Add adds the given header(s) (name, value) to the request + before the action. It appends to any existing values associated + with the header name. + + + Input: + GET /foo HTTP/1.1 + my-header: foo + + + Config: + add: + - name: "my-header" + value: "bar,baz" + + + Output: + GET /foo HTTP/1.1 + my-header: foo,bar,baz + items: + description: HTTPHeader represents an HTTP Header + name and value as defined by RFC 7230. + properties: + name: + description: |- + Name is the name of the HTTP Header to be matched. Name matching MUST be + case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + + + If multiple entries specify equivalent header names, the first entry with + an equivalent name MUST be considered for a match. Subsequent entries + with an equivalent header name MUST be ignored. Due to the + case-insensitivity of header names, "foo" and "Foo" are considered + equivalent. + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP Header + to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + remove: + description: |- + Remove the given header(s) from the HTTP request before the action. The + value of Remove is a list of HTTP header names. Note that the header + names are case-insensitive (see + https://datatracker.ietf.org/doc/html/rfc2616#section-4.2). + + + Input: + GET /foo HTTP/1.1 + my-header1: foo + my-header2: bar + my-header3: baz + + + Config: + remove: ["my-header1", "my-header3"] + + + Output: + GET /foo HTTP/1.1 + my-header2: bar + items: + type: string + maxItems: 16 + type: array + x-kubernetes-list-type: set + set: + description: |- + Set overwrites the request with the given header (name, value) + before the action. + + + Input: + GET /foo HTTP/1.1 + my-header: foo + + + Config: + set: + - name: "my-header" + value: "bar" + + + Output: + GET /foo HTTP/1.1 + my-header: bar + items: + description: HTTPHeader represents an HTTP Header + name and value as defined by RFC 7230. + properties: + name: + description: |- + Name is the name of the HTTP Header to be matched. Name matching MUST be + case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + + + If multiple entries specify equivalent header names, the first entry with + an equivalent name MUST be considered for a match. Subsequent entries + with an equivalent header name MUST be ignored. Due to the + case-insensitivity of header names, "foo" and "Foo" are considered + equivalent. + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP Header + to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + type: object + type: + description: |+ + Type identifies the type of filter to apply. As with other API fields, + types are classified into three conformance levels: + + + - Core: Filter types and their corresponding configuration defined by + "Support: Core" in this package, e.g. "RequestHeaderModifier". All + implementations supporting GRPCRoute MUST support core filters. + + + - Extended: Filter types and their corresponding configuration defined by + "Support: Extended" in this package, e.g. "RequestMirror". Implementers + are encouraged to support extended filters. + + + - Implementation-specific: Filters that are defined and supported by specific vendors. + In the future, filters showing convergence in behavior across multiple + implementations will be considered for inclusion in extended or core + conformance levels. Filter-specific configuration for such filters + is specified using the ExtensionRef field. `Type` MUST be set to + "ExtensionRef" for custom filters. + + + Implementers are encouraged to define custom implementation types to + extend the core API with implementation-specific behavior. + + + If a reference to a custom filter type cannot be resolved, the filter + MUST NOT be skipped. Instead, requests that would have been processed by + that filter MUST receive a HTTP error response. + + + enum: + - ResponseHeaderModifier + - RequestHeaderModifier + - RequestMirror + - ExtensionRef + type: string + required: + - type + type: object + x-kubernetes-validations: + - message: filter.requestHeaderModifier must be nil if the + filter.type is not RequestHeaderModifier + rule: '!(has(self.requestHeaderModifier) && self.type != + ''RequestHeaderModifier'')' + - message: filter.requestHeaderModifier must be specified + for RequestHeaderModifier filter.type + rule: '!(!has(self.requestHeaderModifier) && self.type == + ''RequestHeaderModifier'')' + - message: filter.responseHeaderModifier must be nil if the + filter.type is not ResponseHeaderModifier + rule: '!(has(self.responseHeaderModifier) && self.type != + ''ResponseHeaderModifier'')' + - message: filter.responseHeaderModifier must be specified + for ResponseHeaderModifier filter.type + rule: '!(!has(self.responseHeaderModifier) && self.type + == ''ResponseHeaderModifier'')' + - message: filter.requestMirror must be nil if the filter.type + is not RequestMirror + rule: '!(has(self.requestMirror) && self.type != ''RequestMirror'')' + - message: filter.requestMirror must be specified for RequestMirror + filter.type + rule: '!(!has(self.requestMirror) && self.type == ''RequestMirror'')' + - message: filter.extensionRef must be nil if the filter.type + is not ExtensionRef + rule: '!(has(self.extensionRef) && self.type != ''ExtensionRef'')' + - message: filter.extensionRef must be specified for ExtensionRef + filter.type + rule: '!(!has(self.extensionRef) && self.type == ''ExtensionRef'')' + maxItems: 16 + type: array + x-kubernetes-validations: + - message: RequestHeaderModifier filter cannot be repeated + rule: self.filter(f, f.type == 'RequestHeaderModifier').size() + <= 1 + - message: ResponseHeaderModifier filter cannot be repeated + rule: self.filter(f, f.type == 'ResponseHeaderModifier').size() + <= 1 + matches: + description: |- + Matches define conditions used for matching the rule against incoming + gRPC requests. Each match is independent, i.e. this rule will be matched + if **any** one of the matches is satisfied. + + + For example, take the following matches configuration: + + + ``` + matches: + - method: + service: foo.bar + headers: + values: + version: 2 + - method: + service: foo.bar.v2 + ``` + + + For a request to match against this rule, it MUST satisfy + EITHER of the two conditions: + + + - service of foo.bar AND contains the header `version: 2` + - service of foo.bar.v2 + + + See the documentation for GRPCRouteMatch on how to specify multiple + match conditions to be ANDed together. + + + If no matches are specified, the implementation MUST match every gRPC request. + + + Proxy or Load Balancer routing configuration generated from GRPCRoutes + MUST prioritize rules based on the following criteria, continuing on + ties. Merging MUST not be done between GRPCRoutes and HTTPRoutes. + Precedence MUST be given to the rule with the largest number of: + + + * Characters in a matching non-wildcard hostname. + * Characters in a matching hostname. + * Characters in a matching service. + * Characters in a matching method. + * Header matches. + + + If ties still exist across multiple Routes, matching precedence MUST be + determined in order of the following criteria, continuing on ties: + + + * The oldest Route based on creation timestamp. + * The Route appearing first in alphabetical order by + "{namespace}/{name}". + + + If ties still exist within the Route that has been given precedence, + matching precedence MUST be granted to the first matching rule meeting + the above criteria. + items: + description: |- + GRPCRouteMatch defines the predicate used to match requests to a given + action. Multiple match types are ANDed together, i.e. the match will + evaluate to true only if all conditions are satisfied. + + + For example, the match below will match a gRPC request only if its service + is `foo` AND it contains the `version: v1` header: + + + ``` + matches: + - method: + type: Exact + service: "foo" + headers: + - name: "version" + value "v1" + + + ``` + properties: + headers: + description: |- + Headers specifies gRPC request header matchers. Multiple match values are + ANDed together, meaning, a request MUST match all the specified headers + to select the route. + items: + description: |- + GRPCHeaderMatch describes how to select a gRPC route by matching gRPC request + headers. + properties: + name: + description: |- + Name is the name of the gRPC Header to be matched. + + + If multiple entries specify equivalent header names, only the first + entry with an equivalent name MUST be considered for a match. Subsequent + entries with an equivalent header name MUST be ignored. Due to the + case-insensitivity of header names, "foo" and "Foo" are considered + equivalent. + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + type: + default: Exact + description: Type specifies how to match against + the value of the header. + enum: + - Exact + - RegularExpression + type: string + value: + description: Value is the value of the gRPC Header + to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + method: + description: |- + Method specifies a gRPC request service/method matcher. If this field is + not specified, all services and methods will match. + properties: + method: + description: |- + Value of the method to match against. If left empty or omitted, will + match all services. + + + At least one of Service and Method MUST be a non-empty string. + maxLength: 1024 + type: string + service: + description: |- + Value of the service to match against. If left empty or omitted, will + match any service. + + + At least one of Service and Method MUST be a non-empty string. + maxLength: 1024 + type: string + type: + default: Exact + description: |- + Type specifies how to match against the service and/or method. + Support: Core (Exact with service and method specified) + + + Support: Implementation-specific (Exact with method specified but no service specified) + + + Support: Implementation-specific (RegularExpression) + enum: + - Exact + - RegularExpression + type: string + type: object + x-kubernetes-validations: + - message: One or both of 'service' or 'method' must be + specified + rule: 'has(self.type) ? has(self.service) || has(self.method) + : true' + - message: service must only contain valid characters + (matching ^(?i)\.?[a-z_][a-z_0-9]*(\.[a-z_][a-z_0-9]*)*$) + rule: '(!has(self.type) || self.type == ''Exact'') && + has(self.service) ? self.service.matches(r"""^(?i)\.?[a-z_][a-z_0-9]*(\.[a-z_][a-z_0-9]*)*$"""): + true' + - message: method must only contain valid characters (matching + ^[A-Za-z_][A-Za-z_0-9]*$) + rule: '(!has(self.type) || self.type == ''Exact'') && + has(self.method) ? self.method.matches(r"""^[A-Za-z_][A-Za-z_0-9]*$"""): + true' + type: object + maxItems: 8 + type: array + type: object + maxItems: 16 + type: array + type: object + status: + description: Status defines the current state of GRPCRoute. + properties: + parents: + description: |- + Parents is a list of parent resources (usually Gateways) that are + associated with the route, and the status of the route with respect to + each parent. When this route attaches to a parent, the controller that + manages the parent must add an entry to this list when the controller + first sees the route and should update the entry as appropriate when the + route or gateway is modified. + + + Note that parent references that cannot be resolved by an implementation + of this API will not be added to this list. Implementations of this API + can only populate Route status for the Gateways/parent resources they are + responsible for. + + + A maximum of 32 Gateways will be represented in this list. An empty list + means the route has not been attached to any Gateway. + items: + description: |- + RouteParentStatus describes the status of a route with respect to an + associated Parent. + properties: + conditions: + description: |- + Conditions describes the status of the route with respect to the Gateway. + Note that the route's availability is also subject to the Gateway's own + status conditions and listener status. + + + If the Route's ParentRef specifies an existing Gateway that supports + Routes of this kind AND that Gateway's controller has sufficient access, + then that Gateway's controller MUST set the "Accepted" condition on the + Route, to indicate whether the route has been accepted or rejected by the + Gateway, and why. + + + A Route MUST be considered "Accepted" if at least one of the Route's + rules is implemented by the Gateway. + + + There are a number of cases where the "Accepted" condition may not be set + due to lack of controller visibility, that includes when: + + + * The Route refers to a non-existent parent. + * The Route is of a type that the controller does not support. + * The Route is in a namespace the controller does not have access to. + 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 + maxItems: 8 + minItems: 1 + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + controllerName: + description: |- + ControllerName is a domain/path string that indicates the name of the + controller that wrote this status. This corresponds with the + controllerName field on GatewayClass. + + + Example: "example.net/gateway-controller". + + + The format of this field is DOMAIN "/" PATH, where DOMAIN and PATH are + valid Kubernetes names + (https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names). + + + Controllers MUST populate this field when writing status. Controllers should ensure that + entries to status populated with their ControllerName are cleaned up when they are no + longer necessary. + maxLength: 253 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*\/[A-Za-z0-9\/\-._~%!$&'()*+,;=:]+$ + type: string + parentRef: + description: |- + ParentRef corresponds with a ParentRef in the spec that this + RouteParentStatus struct describes the status of. + properties: + group: + default: gateway.networking.k8s.io + description: |- + Group is the group of the referent. + When unspecified, "gateway.networking.k8s.io" is inferred. + To set the core API group (such as for a "Service" kind referent), + Group must be explicitly set to "" (empty string). + + + Support: Core + maxLength: 253 + pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + kind: + default: Gateway + description: |- + Kind is kind of the referent. + + + There are two kinds of parent resources with "Core" support: + + + * Gateway (Gateway conformance profile) + * Service (Mesh conformance profile, ClusterIP Services only) + + + Support for other resources is Implementation-Specific. + maxLength: 63 + minLength: 1 + pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ + type: string + name: + description: |- + Name is the name of the referent. + + + Support: Core + maxLength: 253 + minLength: 1 + type: string + namespace: + description: |- + Namespace is the namespace of the referent. When unspecified, this refers + to the local namespace of the Route. + + + Note that there are specific rules for ParentRefs which cross namespace + boundaries. Cross-namespace references are only valid if they are explicitly + allowed by something in the namespace they are referring to. For example: + Gateway has the AllowedRoutes field, and ReferenceGrant provides a + generic way to enable any other kind of cross-namespace reference. + + + + + + Support: Core + maxLength: 63 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ + type: string + port: + description: |- + Port is the network port this Route targets. It can be interpreted + differently based on the type of parent resource. + + + When the parent resource is a Gateway, this targets all listeners + listening on the specified port that also support this kind of Route(and + select this Route). It's not recommended to set `Port` unless the + networking behaviors specified in a Route must apply to a specific port + as opposed to a listener(s) whose port(s) may be changed. When both Port + and SectionName are specified, the name and port of the selected listener + must match both specified values. + + + + + + Implementations MAY choose to support other parent resources. + Implementations supporting other types of parent resources MUST clearly + document how/if Port is interpreted. + + + For the purpose of status, an attachment is considered successful as + long as the parent resource accepts it partially. For example, Gateway + listeners can restrict which Routes can attach to them by Route kind, + namespace, or hostname. If 1 of 2 Gateway listeners accept attachment + from the referencing Route, the Route MUST be considered successfully + attached. If no Gateway listeners accept attachment from this Route, + the Route MUST be considered detached from the Gateway. + + + Support: Extended + format: int32 + maximum: 65535 + minimum: 1 + type: integer + sectionName: + description: |- + SectionName is the name of a section within the target resource. In the + following resources, SectionName is interpreted as the following: + + + * Gateway: Listener name. When both Port (experimental) and SectionName + are specified, the name and port of the selected listener must match + both specified values. + * Service: Port name. When both Port (experimental) and SectionName + are specified, the name and port of the selected listener must match + both specified values. + + + Implementations MAY choose to support attaching Routes to other resources. + If that is the case, they MUST clearly document how SectionName is + interpreted. + + + When unspecified (empty string), this will reference the entire resource. + For the purpose of status, an attachment is considered successful if at + least one section in the parent resource accepts it. For example, Gateway + listeners can restrict which Routes can attach to them by Route kind, + namespace, or hostname. If 1 of 2 Gateway listeners accept attachment from + the referencing Route, the Route MUST be considered successfully + attached. If no Gateway listeners accept attachment from this Route, the + Route MUST be considered detached from the Gateway. + + + Support: Core + maxLength: 253 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + required: + - name + type: object + required: + - controllerName + - parentRef + type: object + maxItems: 32 + type: array + required: + - parents + type: object + type: object + served: false + storage: false +status: + acceptedNames: + kind: "" + plural: "" + conditions: null + storedVersions: null +--- +# +# config/crd/standard/gateway.networking.k8s.io_httproutes.yaml +# +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + api-approved.kubernetes.io: https://github.com/kubernetes-sigs/gateway-api/pull/2997 + gateway.networking.k8s.io/bundle-version: v1.1.0 + gateway.networking.k8s.io/channel: standard + creationTimestamp: null + name: httproutes.gateway.networking.k8s.io +spec: + group: gateway.networking.k8s.io + names: + categories: + - gateway-api + kind: HTTPRoute + listKind: HTTPRouteList + plural: httproutes + singular: httproute + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .spec.hostnames + name: Hostnames + type: string + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v1 + schema: + openAPIV3Schema: + description: |- + HTTPRoute provides a way to route HTTP requests. This includes the capability + to match requests by hostname, path, header, or query param. Filters can be + used to specify additional processing steps. Backends specify where matching + requests should be routed. + 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: Spec defines the desired state of HTTPRoute. + properties: + hostnames: + description: |- + Hostnames defines a set of hostnames that should match against the HTTP Host + header to select a HTTPRoute used to process the request. Implementations + MUST ignore any port value specified in the HTTP Host header while + performing a match and (absent of any applicable header modification + configuration) MUST forward this header unmodified to the backend. + + + Valid values for Hostnames are determined by RFC 1123 definition of a + hostname with 2 notable exceptions: + + + 1. IPs are not allowed. + 2. A hostname may be prefixed with a wildcard label (`*.`). The wildcard + label must appear by itself as the first label. + + + If a hostname is specified by both the Listener and HTTPRoute, there + must be at least one intersecting hostname for the HTTPRoute to be + attached to the Listener. For example: + + + * A Listener with `test.example.com` as the hostname matches HTTPRoutes + that have either not specified any hostnames, or have specified at + least one of `test.example.com` or `*.example.com`. + * A Listener with `*.example.com` as the hostname matches HTTPRoutes + that have either not specified any hostnames or have specified at least + one hostname that matches the Listener hostname. For example, + `*.example.com`, `test.example.com`, and `foo.test.example.com` would + all match. On the other hand, `example.com` and `test.example.net` would + not match. + + + Hostnames that are prefixed with a wildcard label (`*.`) are interpreted + as a suffix match. That means that a match for `*.example.com` would match + both `test.example.com`, and `foo.test.example.com`, but not `example.com`. + + + If both the Listener and HTTPRoute have specified hostnames, any + HTTPRoute hostnames that do not match the Listener hostname MUST be + ignored. For example, if a Listener specified `*.example.com`, and the + HTTPRoute specified `test.example.com` and `test.example.net`, + `test.example.net` must not be considered for a match. + + + If both the Listener and HTTPRoute have specified hostnames, and none + match with the criteria above, then the HTTPRoute is not accepted. The + implementation must raise an 'Accepted' Condition with a status of + `False` in the corresponding RouteParentStatus. + + + In the event that multiple HTTPRoutes specify intersecting hostnames (e.g. + overlapping wildcard matching and exact matching hostnames), precedence must + be given to rules from the HTTPRoute with the largest number of: + + + * Characters in a matching non-wildcard hostname. + * Characters in a matching hostname. + + + If ties exist across multiple Routes, the matching precedence rules for + HTTPRouteMatches takes over. + + + Support: Core + items: + description: |- + Hostname is the fully qualified domain name of a network host. This matches + the RFC 1123 definition of a hostname with 2 notable exceptions: + + + 1. IPs are not allowed. + 2. A hostname may be prefixed with a wildcard label (`*.`). The wildcard + label must appear by itself as the first label. + + + Hostname can be "precise" which is a domain name without the terminating + dot of a network host (e.g. "foo.example.com") or "wildcard", which is a + domain name prefixed with a single wildcard label (e.g. `*.example.com`). + + + Note that as per RFC1035 and RFC1123, a *label* must consist of lower case + alphanumeric characters or '-', and must start and end with an alphanumeric + character. No other punctuation is allowed. + maxLength: 253 + minLength: 1 + pattern: ^(\*\.)?[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + maxItems: 16 + type: array + parentRefs: + description: |+ + ParentRefs references the resources (usually Gateways) that a Route wants + to be attached to. Note that the referenced parent resource needs to + allow this for the attachment to be complete. For Gateways, that means + the Gateway needs to allow attachment from Routes of this kind and + namespace. For Services, that means the Service must either be in the same + namespace for a "producer" route, or the mesh implementation must support + and allow "consumer" routes for the referenced Service. ReferenceGrant is + not applicable for governing ParentRefs to Services - it is not possible to + create a "producer" route for a Service in a different namespace from the + Route. + + + There are two kinds of parent resources with "Core" support: + + + * Gateway (Gateway conformance profile) + * Service (Mesh conformance profile, ClusterIP Services only) + + + This API may be extended in the future to support additional kinds of parent + resources. + + + ParentRefs must be _distinct_. This means either that: + + + * They select different objects. If this is the case, then parentRef + entries are distinct. In terms of fields, this means that the + multi-part key defined by `group`, `kind`, `namespace`, and `name` must + be unique across all parentRef entries in the Route. + * They do not select different objects, but for each optional field used, + each ParentRef that selects the same object must set the same set of + optional fields to different values. If one ParentRef sets a + combination of optional fields, all must set the same combination. + + + Some examples: + + + * If one ParentRef sets `sectionName`, all ParentRefs referencing the + same object must also set `sectionName`. + * If one ParentRef sets `port`, all ParentRefs referencing the same + object must also set `port`. + * If one ParentRef sets `sectionName` and `port`, all ParentRefs + referencing the same object must also set `sectionName` and `port`. + + + It is possible to separately reference multiple distinct objects that may + be collapsed by an implementation. For example, some implementations may + choose to merge compatible Gateway Listeners together. If that is the + case, the list of routes attached to those resources should also be + merged. + + + Note that for ParentRefs that cross namespace boundaries, there are specific + rules. Cross-namespace references are only valid if they are explicitly + allowed by something in the namespace they are referring to. For example, + Gateway has the AllowedRoutes field, and ReferenceGrant provides a + generic way to enable other kinds of cross-namespace reference. + + + + + + + + + items: + description: |- + ParentReference identifies an API object (usually a Gateway) that can be considered + a parent of this resource (usually a route). There are two kinds of parent resources + with "Core" support: + + + * Gateway (Gateway conformance profile) + * Service (Mesh conformance profile, ClusterIP Services only) + + + This API may be extended in the future to support additional kinds of parent + resources. + + + The API object must be valid in the cluster; the Group and Kind must + be registered in the cluster for this reference to be valid. + properties: + group: + default: gateway.networking.k8s.io + description: |- + Group is the group of the referent. + When unspecified, "gateway.networking.k8s.io" is inferred. + To set the core API group (such as for a "Service" kind referent), + Group must be explicitly set to "" (empty string). + + + Support: Core + maxLength: 253 + pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + kind: + default: Gateway + description: |- + Kind is kind of the referent. + + + There are two kinds of parent resources with "Core" support: + + + * Gateway (Gateway conformance profile) + * Service (Mesh conformance profile, ClusterIP Services only) + + + Support for other resources is Implementation-Specific. + maxLength: 63 + minLength: 1 + pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ + type: string + name: + description: |- + Name is the name of the referent. + + + Support: Core + maxLength: 253 + minLength: 1 + type: string + namespace: + description: |- + Namespace is the namespace of the referent. When unspecified, this refers + to the local namespace of the Route. + + + Note that there are specific rules for ParentRefs which cross namespace + boundaries. Cross-namespace references are only valid if they are explicitly + allowed by something in the namespace they are referring to. For example: + Gateway has the AllowedRoutes field, and ReferenceGrant provides a + generic way to enable any other kind of cross-namespace reference. + + + + + + Support: Core + maxLength: 63 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ + type: string + port: + description: |- + Port is the network port this Route targets. It can be interpreted + differently based on the type of parent resource. + + + When the parent resource is a Gateway, this targets all listeners + listening on the specified port that also support this kind of Route(and + select this Route). It's not recommended to set `Port` unless the + networking behaviors specified in a Route must apply to a specific port + as opposed to a listener(s) whose port(s) may be changed. When both Port + and SectionName are specified, the name and port of the selected listener + must match both specified values. + + + + + + Implementations MAY choose to support other parent resources. + Implementations supporting other types of parent resources MUST clearly + document how/if Port is interpreted. + + + For the purpose of status, an attachment is considered successful as + long as the parent resource accepts it partially. For example, Gateway + listeners can restrict which Routes can attach to them by Route kind, + namespace, or hostname. If 1 of 2 Gateway listeners accept attachment + from the referencing Route, the Route MUST be considered successfully + attached. If no Gateway listeners accept attachment from this Route, + the Route MUST be considered detached from the Gateway. + + + Support: Extended + format: int32 + maximum: 65535 + minimum: 1 + type: integer + sectionName: + description: |- + SectionName is the name of a section within the target resource. In the + following resources, SectionName is interpreted as the following: + + + * Gateway: Listener name. When both Port (experimental) and SectionName + are specified, the name and port of the selected listener must match + both specified values. + * Service: Port name. When both Port (experimental) and SectionName + are specified, the name and port of the selected listener must match + both specified values. + + + Implementations MAY choose to support attaching Routes to other resources. + If that is the case, they MUST clearly document how SectionName is + interpreted. + + + When unspecified (empty string), this will reference the entire resource. + For the purpose of status, an attachment is considered successful if at + least one section in the parent resource accepts it. For example, Gateway + listeners can restrict which Routes can attach to them by Route kind, + namespace, or hostname. If 1 of 2 Gateway listeners accept attachment from + the referencing Route, the Route MUST be considered successfully + attached. If no Gateway listeners accept attachment from this Route, the + Route MUST be considered detached from the Gateway. + + + Support: Core + maxLength: 253 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + required: + - name + type: object + maxItems: 32 + type: array + x-kubernetes-validations: + - message: sectionName must be specified when parentRefs includes + 2 or more references to the same parent + rule: 'self.all(p1, self.all(p2, p1.group == p2.group && p1.kind + == p2.kind && p1.name == p2.name && (((!has(p1.__namespace__) + || p1.__namespace__ == '''') && (!has(p2.__namespace__) || p2.__namespace__ + == '''')) || (has(p1.__namespace__) && has(p2.__namespace__) && + p1.__namespace__ == p2.__namespace__ )) ? ((!has(p1.sectionName) + || p1.sectionName == '''') == (!has(p2.sectionName) || p2.sectionName + == '''')) : true))' + - message: sectionName must be unique when parentRefs includes 2 or + more references to the same parent + rule: self.all(p1, self.exists_one(p2, p1.group == p2.group && p1.kind + == p2.kind && p1.name == p2.name && (((!has(p1.__namespace__) + || p1.__namespace__ == '') && (!has(p2.__namespace__) || p2.__namespace__ + == '')) || (has(p1.__namespace__) && has(p2.__namespace__) && + p1.__namespace__ == p2.__namespace__ )) && (((!has(p1.sectionName) + || p1.sectionName == '') && (!has(p2.sectionName) || p2.sectionName + == '')) || (has(p1.sectionName) && has(p2.sectionName) && p1.sectionName + == p2.sectionName)))) + rules: + default: + - matches: + - path: + type: PathPrefix + value: / + description: Rules are a list of HTTP matchers, filters and actions. + items: + description: |- + HTTPRouteRule defines semantics for matching an HTTP request based on + conditions (matches), processing it (filters), and forwarding the request to + an API object (backendRefs). + properties: + backendRefs: + description: |- + BackendRefs defines the backend(s) where matching requests should be + sent. + + + Failure behavior here depends on how many BackendRefs are specified and + how many are invalid. + + + If *all* entries in BackendRefs are invalid, and there are also no filters + specified in this route rule, *all* traffic which matches this rule MUST + receive a 500 status code. + + + See the HTTPBackendRef definition for the rules about what makes a single + HTTPBackendRef invalid. + + + When a HTTPBackendRef is invalid, 500 status codes MUST be returned for + requests that would have otherwise been routed to an invalid backend. If + multiple backends are specified, and some are invalid, the proportion of + requests that would otherwise have been routed to an invalid backend + MUST receive a 500 status code. + + + For example, if two backends are specified with equal weights, and one is + invalid, 50 percent of traffic must receive a 500. Implementations may + choose how that 50 percent is determined. + + + Support: Core for Kubernetes Service + + + Support: Extended for Kubernetes ServiceImport + + + Support: Implementation-specific for any other resource + + + Support for weight: Core + items: + description: |- + HTTPBackendRef defines how a HTTPRoute forwards a HTTP request. + + + Note that when a namespace different than the local namespace is specified, a + ReferenceGrant object is required in the referent namespace to allow that + namespace's owner to accept the reference. See the ReferenceGrant + documentation for details. + + + + + + When the BackendRef points to a Kubernetes Service, implementations SHOULD + honor the appProtocol field if it is set for the target Service Port. + + + Implementations supporting appProtocol SHOULD recognize the Kubernetes + Standard Application Protocols defined in KEP-3726. + + + If a Service appProtocol isn't specified, an implementation MAY infer the + backend protocol through its own means. Implementations MAY infer the + protocol from the Route type referring to the backend Service. + + + If a Route is not able to send traffic to the backend using the specified + protocol then the backend is considered invalid. Implementations MUST set the + "ResolvedRefs" condition to "False" with the "UnsupportedProtocol" reason. + + + + properties: + filters: + description: |- + Filters defined at this level should be executed if and only if the + request is being forwarded to the backend defined here. + + + Support: Implementation-specific (For broader support of filters, use the + Filters field in HTTPRouteRule.) + items: + description: |- + HTTPRouteFilter defines processing steps that must be completed during the + request or response lifecycle. HTTPRouteFilters are meant as an extension + point to express processing that may be done in Gateway implementations. Some + examples include request or response modification, implementing + authentication strategies, rate-limiting, and traffic shaping. API + guarantee/conformance is defined based on the type of the filter. + properties: + extensionRef: + description: |- + ExtensionRef is an optional, implementation-specific extension to the + "filter" behavior. For example, resource "myroutefilter" in group + "networking.example.net"). ExtensionRef MUST NOT be used for core and + extended filters. + + + This filter can be used multiple times within the same rule. + + + Support: Implementation-specific + properties: + group: + description: |- + Group is the group of the referent. For example, "gateway.networking.k8s.io". + When unspecified or empty string, core API group is inferred. + maxLength: 253 + pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + kind: + description: Kind is kind of the referent. For + example "HTTPRoute" or "Service". + maxLength: 63 + minLength: 1 + pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ + type: string + name: + description: Name is the name of the referent. + maxLength: 253 + minLength: 1 + type: string + required: + - group + - kind + - name + type: object + requestHeaderModifier: + description: |- + RequestHeaderModifier defines a schema for a filter that modifies request + headers. + + + Support: Core + properties: + add: + description: |- + Add adds the given header(s) (name, value) to the request + before the action. It appends to any existing values associated + with the header name. + + + Input: + GET /foo HTTP/1.1 + my-header: foo + + + Config: + add: + - name: "my-header" + value: "bar,baz" + + + Output: + GET /foo HTTP/1.1 + my-header: foo,bar,baz + items: + description: HTTPHeader represents an HTTP + Header name and value as defined by RFC + 7230. + properties: + name: + description: |- + Name is the name of the HTTP Header to be matched. Name matching MUST be + case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + + + If multiple entries specify equivalent header names, the first entry with + an equivalent name MUST be considered for a match. Subsequent entries + with an equivalent header name MUST be ignored. Due to the + case-insensitivity of header names, "foo" and "Foo" are considered + equivalent. + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP + Header to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + remove: + description: |- + Remove the given header(s) from the HTTP request before the action. The + value of Remove is a list of HTTP header names. Note that the header + names are case-insensitive (see + https://datatracker.ietf.org/doc/html/rfc2616#section-4.2). + + + Input: + GET /foo HTTP/1.1 + my-header1: foo + my-header2: bar + my-header3: baz + + + Config: + remove: ["my-header1", "my-header3"] + + + Output: + GET /foo HTTP/1.1 + my-header2: bar + items: + type: string + maxItems: 16 + type: array + x-kubernetes-list-type: set + set: + description: |- + Set overwrites the request with the given header (name, value) + before the action. + + + Input: + GET /foo HTTP/1.1 + my-header: foo + + + Config: + set: + - name: "my-header" + value: "bar" + + + Output: + GET /foo HTTP/1.1 + my-header: bar + items: + description: HTTPHeader represents an HTTP + Header name and value as defined by RFC + 7230. + properties: + name: + description: |- + Name is the name of the HTTP Header to be matched. Name matching MUST be + case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + + + If multiple entries specify equivalent header names, the first entry with + an equivalent name MUST be considered for a match. Subsequent entries + with an equivalent header name MUST be ignored. Due to the + case-insensitivity of header names, "foo" and "Foo" are considered + equivalent. + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP + Header to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + type: object + requestMirror: + description: |- + RequestMirror defines a schema for a filter that mirrors requests. + Requests are sent to the specified destination, but responses from + that destination are ignored. + + + This filter can be used multiple times within the same rule. Note that + not all implementations will be able to support mirroring to multiple + backends. + + + Support: Extended + properties: + backendRef: + description: |- + BackendRef references a resource where mirrored requests are sent. + + + Mirrored requests must be sent only to a single destination endpoint + within this BackendRef, irrespective of how many endpoints are present + within this BackendRef. + + + If the referent cannot be found, this BackendRef is invalid and must be + dropped from the Gateway. The controller must ensure the "ResolvedRefs" + condition on the Route status is set to `status: False` and not configure + this backend in the underlying implementation. + + + If there is a cross-namespace reference to an *existing* object + that is not allowed by a ReferenceGrant, the controller must ensure the + "ResolvedRefs" condition on the Route is set to `status: False`, + with the "RefNotPermitted" reason and not configure this backend in the + underlying implementation. + + + In either error case, the Message of the `ResolvedRefs` Condition + should be used to provide more detail about the problem. + + + Support: Extended for Kubernetes Service + + + Support: Implementation-specific for any other resource + properties: + group: + default: "" + description: |- + Group is the group of the referent. For example, "gateway.networking.k8s.io". + When unspecified or empty string, core API group is inferred. + maxLength: 253 + pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + kind: + default: Service + description: |- + Kind is the Kubernetes resource kind of the referent. For example + "Service". + + + Defaults to "Service" when not specified. + + + ExternalName services can refer to CNAME DNS records that may live + outside of the cluster and as such are difficult to reason about in + terms of conformance. They also may not be safe to forward to (see + CVE-2021-25740 for more information). Implementations SHOULD NOT + support ExternalName Services. + + + Support: Core (Services with a type other than ExternalName) + + + Support: Implementation-specific (Services with type ExternalName) + maxLength: 63 + minLength: 1 + pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ + type: string + name: + description: Name is the name of the referent. + maxLength: 253 + minLength: 1 + type: string + namespace: + description: |- + Namespace is the namespace of the backend. When unspecified, the local + namespace is inferred. + + + Note that when a namespace different than the local namespace is specified, + a ReferenceGrant object is required in the referent namespace to allow that + namespace's owner to accept the reference. See the ReferenceGrant + documentation for details. + + + Support: Core + maxLength: 63 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ + type: string + port: + description: |- + Port specifies the destination port number to use for this resource. + Port is required when the referent is a Kubernetes Service. In this + case, the port number is the service port number, not the target port. + For other resources, destination port might be derived from the referent + resource or this field. + format: int32 + maximum: 65535 + minimum: 1 + type: integer + required: + - name + type: object + x-kubernetes-validations: + - message: Must have port for Service reference + rule: '(size(self.group) == 0 && self.kind + == ''Service'') ? has(self.port) : true' + required: + - backendRef + type: object + requestRedirect: + description: |- + RequestRedirect defines a schema for a filter that responds to the + request with an HTTP redirection. + + + Support: Core + properties: + hostname: + description: |- + Hostname is the hostname to be used in the value of the `Location` + header in the response. + When empty, the hostname in the `Host` header of the request is used. + + + Support: Core + maxLength: 253 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + path: + description: |- + Path defines parameters used to modify the path of the incoming request. + The modified path is then used to construct the `Location` header. When + empty, the request path is used as-is. + + + Support: Extended + properties: + replaceFullPath: + description: |- + ReplaceFullPath specifies the value with which to replace the full path + of a request during a rewrite or redirect. + maxLength: 1024 + type: string + replacePrefixMatch: + description: |- + ReplacePrefixMatch specifies the value with which to replace the prefix + match of a request during a rewrite or redirect. For example, a request + to "/foo/bar" with a prefix match of "/foo" and a ReplacePrefixMatch + of "/xyz" would be modified to "/xyz/bar". + + + Note that this matches the behavior of the PathPrefix match type. This + matches full path elements. A path element refers to the list of labels + in the path split by the `/` separator. When specified, a trailing `/` is + ignored. For example, the paths `/abc`, `/abc/`, and `/abc/def` would all + match the prefix `/abc`, but the path `/abcd` would not. + + + ReplacePrefixMatch is only compatible with a `PathPrefix` HTTPRouteMatch. + Using any other HTTPRouteMatch type on the same HTTPRouteRule will result in + the implementation setting the Accepted Condition for the Route to `status: False`. + + + Request Path | Prefix Match | Replace Prefix | Modified Path + -------------|--------------|----------------|---------- + /foo/bar | /foo | /xyz | /xyz/bar + /foo/bar | /foo | /xyz/ | /xyz/bar + /foo/bar | /foo/ | /xyz | /xyz/bar + /foo/bar | /foo/ | /xyz/ | /xyz/bar + /foo | /foo | /xyz | /xyz + /foo/ | /foo | /xyz | /xyz/ + /foo/bar | /foo | | /bar + /foo/ | /foo | | / + /foo | /foo | | / + /foo/ | /foo | / | / + /foo | /foo | / | / + maxLength: 1024 + type: string + type: + description: |- + Type defines the type of path modifier. Additional types may be + added in a future release of the API. + + + Note that values may be added to this enum, implementations + must ensure that unknown values will not cause a crash. + + + Unknown values here must result in the implementation setting the + Accepted Condition for the Route to `status: False`, with a + Reason of `UnsupportedValue`. + enum: + - ReplaceFullPath + - ReplacePrefixMatch + type: string + required: + - type + type: object + x-kubernetes-validations: + - message: replaceFullPath must be specified + when type is set to 'ReplaceFullPath' + rule: 'self.type == ''ReplaceFullPath'' ? + has(self.replaceFullPath) : true' + - message: type must be 'ReplaceFullPath' when + replaceFullPath is set + rule: 'has(self.replaceFullPath) ? self.type + == ''ReplaceFullPath'' : true' + - message: replacePrefixMatch must be specified + when type is set to 'ReplacePrefixMatch' + rule: 'self.type == ''ReplacePrefixMatch'' + ? has(self.replacePrefixMatch) : true' + - message: type must be 'ReplacePrefixMatch' + when replacePrefixMatch is set + rule: 'has(self.replacePrefixMatch) ? self.type + == ''ReplacePrefixMatch'' : true' + port: + description: |- + Port is the port to be used in the value of the `Location` + header in the response. + + + If no port is specified, the redirect port MUST be derived using the + following rules: + + + * If redirect scheme is not-empty, the redirect port MUST be the well-known + port associated with the redirect scheme. Specifically "http" to port 80 + and "https" to port 443. If the redirect scheme does not have a + well-known port, the listener port of the Gateway SHOULD be used. + * If redirect scheme is empty, the redirect port MUST be the Gateway + Listener port. + + + Implementations SHOULD NOT add the port number in the 'Location' + header in the following cases: + + + * A Location header that will use HTTP (whether that is determined via + the Listener protocol or the Scheme field) _and_ use port 80. + * A Location header that will use HTTPS (whether that is determined via + the Listener protocol or the Scheme field) _and_ use port 443. + + + Support: Extended + format: int32 + maximum: 65535 + minimum: 1 + type: integer + scheme: + description: |- + Scheme is the scheme to be used in the value of the `Location` header in + the response. When empty, the scheme of the request is used. + + + Scheme redirects can affect the port of the redirect, for more information, + refer to the documentation for the port field of this filter. + + + Note that values may be added to this enum, implementations + must ensure that unknown values will not cause a crash. + + + Unknown values here must result in the implementation setting the + Accepted Condition for the Route to `status: False`, with a + Reason of `UnsupportedValue`. + + + Support: Extended + enum: + - http + - https + type: string + statusCode: + default: 302 + description: |- + StatusCode is the HTTP status code to be used in response. + + + Note that values may be added to this enum, implementations + must ensure that unknown values will not cause a crash. + + + Unknown values here must result in the implementation setting the + Accepted Condition for the Route to `status: False`, with a + Reason of `UnsupportedValue`. + + + Support: Core + enum: + - 301 + - 302 + type: integer + type: object + responseHeaderModifier: + description: |- + ResponseHeaderModifier defines a schema for a filter that modifies response + headers. + + + Support: Extended + properties: + add: + description: |- + Add adds the given header(s) (name, value) to the request + before the action. It appends to any existing values associated + with the header name. + + + Input: + GET /foo HTTP/1.1 + my-header: foo + + + Config: + add: + - name: "my-header" + value: "bar,baz" + + + Output: + GET /foo HTTP/1.1 + my-header: foo,bar,baz + items: + description: HTTPHeader represents an HTTP + Header name and value as defined by RFC + 7230. + properties: + name: + description: |- + Name is the name of the HTTP Header to be matched. Name matching MUST be + case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + + + If multiple entries specify equivalent header names, the first entry with + an equivalent name MUST be considered for a match. Subsequent entries + with an equivalent header name MUST be ignored. Due to the + case-insensitivity of header names, "foo" and "Foo" are considered + equivalent. + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP + Header to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + remove: + description: |- + Remove the given header(s) from the HTTP request before the action. The + value of Remove is a list of HTTP header names. Note that the header + names are case-insensitive (see + https://datatracker.ietf.org/doc/html/rfc2616#section-4.2). + + + Input: + GET /foo HTTP/1.1 + my-header1: foo + my-header2: bar + my-header3: baz + + + Config: + remove: ["my-header1", "my-header3"] + + + Output: + GET /foo HTTP/1.1 + my-header2: bar + items: + type: string + maxItems: 16 + type: array + x-kubernetes-list-type: set + set: + description: |- + Set overwrites the request with the given header (name, value) + before the action. + + + Input: + GET /foo HTTP/1.1 + my-header: foo + + + Config: + set: + - name: "my-header" + value: "bar" + + + Output: + GET /foo HTTP/1.1 + my-header: bar + items: + description: HTTPHeader represents an HTTP + Header name and value as defined by RFC + 7230. + properties: + name: + description: |- + Name is the name of the HTTP Header to be matched. Name matching MUST be + case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + + + If multiple entries specify equivalent header names, the first entry with + an equivalent name MUST be considered for a match. Subsequent entries + with an equivalent header name MUST be ignored. Due to the + case-insensitivity of header names, "foo" and "Foo" are considered + equivalent. + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP + Header to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + type: object + type: + description: |- + Type identifies the type of filter to apply. As with other API fields, + types are classified into three conformance levels: + + + - Core: Filter types and their corresponding configuration defined by + "Support: Core" in this package, e.g. "RequestHeaderModifier". All + implementations must support core filters. + + + - Extended: Filter types and their corresponding configuration defined by + "Support: Extended" in this package, e.g. "RequestMirror". Implementers + are encouraged to support extended filters. + + + - Implementation-specific: Filters that are defined and supported by + specific vendors. + In the future, filters showing convergence in behavior across multiple + implementations will be considered for inclusion in extended or core + conformance levels. Filter-specific configuration for such filters + is specified using the ExtensionRef field. `Type` should be set to + "ExtensionRef" for custom filters. + + + Implementers are encouraged to define custom implementation types to + extend the core API with implementation-specific behavior. + + + If a reference to a custom filter type cannot be resolved, the filter + MUST NOT be skipped. Instead, requests that would have been processed by + that filter MUST receive a HTTP error response. + + + Note that values may be added to this enum, implementations + must ensure that unknown values will not cause a crash. + + + Unknown values here must result in the implementation setting the + Accepted Condition for the Route to `status: False`, with a + Reason of `UnsupportedValue`. + enum: + - RequestHeaderModifier + - ResponseHeaderModifier + - RequestMirror + - RequestRedirect + - URLRewrite + - ExtensionRef + type: string + urlRewrite: + description: |- + URLRewrite defines a schema for a filter that modifies a request during forwarding. + + + Support: Extended + properties: + hostname: + description: |- + Hostname is the value to be used to replace the Host header value during + forwarding. + + + Support: Extended + maxLength: 253 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + path: + description: |- + Path defines a path rewrite. + + + Support: Extended + properties: + replaceFullPath: + description: |- + ReplaceFullPath specifies the value with which to replace the full path + of a request during a rewrite or redirect. + maxLength: 1024 + type: string + replacePrefixMatch: + description: |- + ReplacePrefixMatch specifies the value with which to replace the prefix + match of a request during a rewrite or redirect. For example, a request + to "/foo/bar" with a prefix match of "/foo" and a ReplacePrefixMatch + of "/xyz" would be modified to "/xyz/bar". + + + Note that this matches the behavior of the PathPrefix match type. This + matches full path elements. A path element refers to the list of labels + in the path split by the `/` separator. When specified, a trailing `/` is + ignored. For example, the paths `/abc`, `/abc/`, and `/abc/def` would all + match the prefix `/abc`, but the path `/abcd` would not. + + + ReplacePrefixMatch is only compatible with a `PathPrefix` HTTPRouteMatch. + Using any other HTTPRouteMatch type on the same HTTPRouteRule will result in + the implementation setting the Accepted Condition for the Route to `status: False`. + + + Request Path | Prefix Match | Replace Prefix | Modified Path + -------------|--------------|----------------|---------- + /foo/bar | /foo | /xyz | /xyz/bar + /foo/bar | /foo | /xyz/ | /xyz/bar + /foo/bar | /foo/ | /xyz | /xyz/bar + /foo/bar | /foo/ | /xyz/ | /xyz/bar + /foo | /foo | /xyz | /xyz + /foo/ | /foo | /xyz | /xyz/ + /foo/bar | /foo | | /bar + /foo/ | /foo | | / + /foo | /foo | | / + /foo/ | /foo | / | / + /foo | /foo | / | / + maxLength: 1024 + type: string + type: + description: |- + Type defines the type of path modifier. Additional types may be + added in a future release of the API. + + + Note that values may be added to this enum, implementations + must ensure that unknown values will not cause a crash. + + + Unknown values here must result in the implementation setting the + Accepted Condition for the Route to `status: False`, with a + Reason of `UnsupportedValue`. + enum: + - ReplaceFullPath + - ReplacePrefixMatch + type: string + required: + - type + type: object + x-kubernetes-validations: + - message: replaceFullPath must be specified + when type is set to 'ReplaceFullPath' + rule: 'self.type == ''ReplaceFullPath'' ? + has(self.replaceFullPath) : true' + - message: type must be 'ReplaceFullPath' when + replaceFullPath is set + rule: 'has(self.replaceFullPath) ? self.type + == ''ReplaceFullPath'' : true' + - message: replacePrefixMatch must be specified + when type is set to 'ReplacePrefixMatch' + rule: 'self.type == ''ReplacePrefixMatch'' + ? has(self.replacePrefixMatch) : true' + - message: type must be 'ReplacePrefixMatch' + when replacePrefixMatch is set + rule: 'has(self.replacePrefixMatch) ? self.type + == ''ReplacePrefixMatch'' : true' + type: object + required: + - type + type: object + x-kubernetes-validations: + - message: filter.requestHeaderModifier must be nil + if the filter.type is not RequestHeaderModifier + rule: '!(has(self.requestHeaderModifier) && self.type + != ''RequestHeaderModifier'')' + - message: filter.requestHeaderModifier must be specified + for RequestHeaderModifier filter.type + rule: '!(!has(self.requestHeaderModifier) && self.type + == ''RequestHeaderModifier'')' + - message: filter.responseHeaderModifier must be nil + if the filter.type is not ResponseHeaderModifier + rule: '!(has(self.responseHeaderModifier) && self.type + != ''ResponseHeaderModifier'')' + - message: filter.responseHeaderModifier must be specified + for ResponseHeaderModifier filter.type + rule: '!(!has(self.responseHeaderModifier) && self.type + == ''ResponseHeaderModifier'')' + - message: filter.requestMirror must be nil if the filter.type + is not RequestMirror + rule: '!(has(self.requestMirror) && self.type != ''RequestMirror'')' + - message: filter.requestMirror must be specified for + RequestMirror filter.type + rule: '!(!has(self.requestMirror) && self.type == + ''RequestMirror'')' + - message: filter.requestRedirect must be nil if the + filter.type is not RequestRedirect + rule: '!(has(self.requestRedirect) && self.type != + ''RequestRedirect'')' + - message: filter.requestRedirect must be specified + for RequestRedirect filter.type + rule: '!(!has(self.requestRedirect) && self.type == + ''RequestRedirect'')' + - message: filter.urlRewrite must be nil if the filter.type + is not URLRewrite + rule: '!(has(self.urlRewrite) && self.type != ''URLRewrite'')' + - message: filter.urlRewrite must be specified for URLRewrite + filter.type + rule: '!(!has(self.urlRewrite) && self.type == ''URLRewrite'')' + - message: filter.extensionRef must be nil if the filter.type + is not ExtensionRef + rule: '!(has(self.extensionRef) && self.type != ''ExtensionRef'')' + - message: filter.extensionRef must be specified for + ExtensionRef filter.type + rule: '!(!has(self.extensionRef) && self.type == ''ExtensionRef'')' + maxItems: 16 + type: array + x-kubernetes-validations: + - message: May specify either httpRouteFilterRequestRedirect + or httpRouteFilterRequestRewrite, but not both + rule: '!(self.exists(f, f.type == ''RequestRedirect'') + && self.exists(f, f.type == ''URLRewrite''))' + - message: May specify either httpRouteFilterRequestRedirect + or httpRouteFilterRequestRewrite, but not both + rule: '!(self.exists(f, f.type == ''RequestRedirect'') + && self.exists(f, f.type == ''URLRewrite''))' + - message: RequestHeaderModifier filter cannot be repeated + rule: self.filter(f, f.type == 'RequestHeaderModifier').size() + <= 1 + - message: ResponseHeaderModifier filter cannot be repeated + rule: self.filter(f, f.type == 'ResponseHeaderModifier').size() + <= 1 + - message: RequestRedirect filter cannot be repeated + rule: self.filter(f, f.type == 'RequestRedirect').size() + <= 1 + - message: URLRewrite filter cannot be repeated + rule: self.filter(f, f.type == 'URLRewrite').size() + <= 1 + group: + default: "" + description: |- + Group is the group of the referent. For example, "gateway.networking.k8s.io". + When unspecified or empty string, core API group is inferred. + maxLength: 253 + pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + kind: + default: Service + description: |- + Kind is the Kubernetes resource kind of the referent. For example + "Service". + + + Defaults to "Service" when not specified. + + + ExternalName services can refer to CNAME DNS records that may live + outside of the cluster and as such are difficult to reason about in + terms of conformance. They also may not be safe to forward to (see + CVE-2021-25740 for more information). Implementations SHOULD NOT + support ExternalName Services. + + + Support: Core (Services with a type other than ExternalName) + + + Support: Implementation-specific (Services with type ExternalName) + maxLength: 63 + minLength: 1 + pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ + type: string + name: + description: Name is the name of the referent. + maxLength: 253 + minLength: 1 + type: string + namespace: + description: |- + Namespace is the namespace of the backend. When unspecified, the local + namespace is inferred. + + + Note that when a namespace different than the local namespace is specified, + a ReferenceGrant object is required in the referent namespace to allow that + namespace's owner to accept the reference. See the ReferenceGrant + documentation for details. + + + Support: Core + maxLength: 63 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ + type: string + port: + description: |- + Port specifies the destination port number to use for this resource. + Port is required when the referent is a Kubernetes Service. In this + case, the port number is the service port number, not the target port. + For other resources, destination port might be derived from the referent + resource or this field. + format: int32 + maximum: 65535 + minimum: 1 + type: integer + weight: + default: 1 + description: |- + Weight specifies the proportion of requests forwarded to the referenced + backend. This is computed as weight/(sum of all weights in this + BackendRefs list). For non-zero values, there may be some epsilon from + the exact proportion defined here depending on the precision an + implementation supports. Weight is not a percentage and the sum of + weights does not need to equal 100. + + + If only one backend is specified and it has a weight greater than 0, 100% + of the traffic is forwarded to that backend. If weight is set to 0, no + traffic should be forwarded for this entry. If unspecified, weight + defaults to 1. + + + Support for this field varies based on the context where used. + format: int32 + maximum: 1000000 + minimum: 0 + type: integer + required: + - name + type: object + x-kubernetes-validations: + - message: Must have port for Service reference + rule: '(size(self.group) == 0 && self.kind == ''Service'') + ? has(self.port) : true' + maxItems: 16 + type: array + filters: + description: |- + Filters define the filters that are applied to requests that match + this rule. + + + Wherever possible, implementations SHOULD implement filters in the order + they are specified. + + + Implementations MAY choose to implement this ordering strictly, rejecting + any combination or order of filters that can not be supported. If implementations + choose a strict interpretation of filter ordering, they MUST clearly document + that behavior. + + + To reject an invalid combination or order of filters, implementations SHOULD + consider the Route Rules with this configuration invalid. If all Route Rules + in a Route are invalid, the entire Route would be considered invalid. If only + a portion of Route Rules are invalid, implementations MUST set the + "PartiallyInvalid" condition for the Route. + + + Conformance-levels at this level are defined based on the type of filter: + + + - ALL core filters MUST be supported by all implementations. + - Implementers are encouraged to support extended filters. + - Implementation-specific custom filters have no API guarantees across + implementations. + + + Specifying the same filter multiple times is not supported unless explicitly + indicated in the filter. + + + All filters are expected to be compatible with each other except for the + URLRewrite and RequestRedirect filters, which may not be combined. If an + implementation can not support other combinations of filters, they must clearly + document that limitation. In cases where incompatible or unsupported + filters are specified and cause the `Accepted` condition to be set to status + `False`, implementations may use the `IncompatibleFilters` reason to specify + this configuration error. + + + Support: Core + items: + description: |- + HTTPRouteFilter defines processing steps that must be completed during the + request or response lifecycle. HTTPRouteFilters are meant as an extension + point to express processing that may be done in Gateway implementations. Some + examples include request or response modification, implementing + authentication strategies, rate-limiting, and traffic shaping. API + guarantee/conformance is defined based on the type of the filter. + properties: + extensionRef: + description: |- + ExtensionRef is an optional, implementation-specific extension to the + "filter" behavior. For example, resource "myroutefilter" in group + "networking.example.net"). ExtensionRef MUST NOT be used for core and + extended filters. + + + This filter can be used multiple times within the same rule. + + + Support: Implementation-specific + properties: + group: + description: |- + Group is the group of the referent. For example, "gateway.networking.k8s.io". + When unspecified or empty string, core API group is inferred. + maxLength: 253 + pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + kind: + description: Kind is kind of the referent. For example + "HTTPRoute" or "Service". + maxLength: 63 + minLength: 1 + pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ + type: string + name: + description: Name is the name of the referent. + maxLength: 253 + minLength: 1 + type: string + required: + - group + - kind + - name + type: object + requestHeaderModifier: + description: |- + RequestHeaderModifier defines a schema for a filter that modifies request + headers. + + + Support: Core + properties: + add: + description: |- + Add adds the given header(s) (name, value) to the request + before the action. It appends to any existing values associated + with the header name. + + + Input: + GET /foo HTTP/1.1 + my-header: foo + + + Config: + add: + - name: "my-header" + value: "bar,baz" + + + Output: + GET /foo HTTP/1.1 + my-header: foo,bar,baz + items: + description: HTTPHeader represents an HTTP Header + name and value as defined by RFC 7230. + properties: + name: + description: |- + Name is the name of the HTTP Header to be matched. Name matching MUST be + case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + + + If multiple entries specify equivalent header names, the first entry with + an equivalent name MUST be considered for a match. Subsequent entries + with an equivalent header name MUST be ignored. Due to the + case-insensitivity of header names, "foo" and "Foo" are considered + equivalent. + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP Header + to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + remove: + description: |- + Remove the given header(s) from the HTTP request before the action. The + value of Remove is a list of HTTP header names. Note that the header + names are case-insensitive (see + https://datatracker.ietf.org/doc/html/rfc2616#section-4.2). + + + Input: + GET /foo HTTP/1.1 + my-header1: foo + my-header2: bar + my-header3: baz + + + Config: + remove: ["my-header1", "my-header3"] + + + Output: + GET /foo HTTP/1.1 + my-header2: bar + items: + type: string + maxItems: 16 + type: array + x-kubernetes-list-type: set + set: + description: |- + Set overwrites the request with the given header (name, value) + before the action. + + + Input: + GET /foo HTTP/1.1 + my-header: foo + + + Config: + set: + - name: "my-header" + value: "bar" + + + Output: + GET /foo HTTP/1.1 + my-header: bar + items: + description: HTTPHeader represents an HTTP Header + name and value as defined by RFC 7230. + properties: + name: + description: |- + Name is the name of the HTTP Header to be matched. Name matching MUST be + case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + + + If multiple entries specify equivalent header names, the first entry with + an equivalent name MUST be considered for a match. Subsequent entries + with an equivalent header name MUST be ignored. Due to the + case-insensitivity of header names, "foo" and "Foo" are considered + equivalent. + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP Header + to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + type: object + requestMirror: + description: |- + RequestMirror defines a schema for a filter that mirrors requests. + Requests are sent to the specified destination, but responses from + that destination are ignored. + + + This filter can be used multiple times within the same rule. Note that + not all implementations will be able to support mirroring to multiple + backends. + + + Support: Extended + properties: + backendRef: + description: |- + BackendRef references a resource where mirrored requests are sent. + + + Mirrored requests must be sent only to a single destination endpoint + within this BackendRef, irrespective of how many endpoints are present + within this BackendRef. + + + If the referent cannot be found, this BackendRef is invalid and must be + dropped from the Gateway. The controller must ensure the "ResolvedRefs" + condition on the Route status is set to `status: False` and not configure + this backend in the underlying implementation. + + + If there is a cross-namespace reference to an *existing* object + that is not allowed by a ReferenceGrant, the controller must ensure the + "ResolvedRefs" condition on the Route is set to `status: False`, + with the "RefNotPermitted" reason and not configure this backend in the + underlying implementation. + + + In either error case, the Message of the `ResolvedRefs` Condition + should be used to provide more detail about the problem. + + + Support: Extended for Kubernetes Service + + + Support: Implementation-specific for any other resource + properties: + group: + default: "" + description: |- + Group is the group of the referent. For example, "gateway.networking.k8s.io". + When unspecified or empty string, core API group is inferred. + maxLength: 253 + pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + kind: + default: Service + description: |- + Kind is the Kubernetes resource kind of the referent. For example + "Service". + + + Defaults to "Service" when not specified. + + + ExternalName services can refer to CNAME DNS records that may live + outside of the cluster and as such are difficult to reason about in + terms of conformance. They also may not be safe to forward to (see + CVE-2021-25740 for more information). Implementations SHOULD NOT + support ExternalName Services. + + + Support: Core (Services with a type other than ExternalName) + + + Support: Implementation-specific (Services with type ExternalName) + maxLength: 63 + minLength: 1 + pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ + type: string + name: + description: Name is the name of the referent. + maxLength: 253 + minLength: 1 + type: string + namespace: + description: |- + Namespace is the namespace of the backend. When unspecified, the local + namespace is inferred. + + + Note that when a namespace different than the local namespace is specified, + a ReferenceGrant object is required in the referent namespace to allow that + namespace's owner to accept the reference. See the ReferenceGrant + documentation for details. + + + Support: Core + maxLength: 63 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ + type: string + port: + description: |- + Port specifies the destination port number to use for this resource. + Port is required when the referent is a Kubernetes Service. In this + case, the port number is the service port number, not the target port. + For other resources, destination port might be derived from the referent + resource or this field. + format: int32 + maximum: 65535 + minimum: 1 + type: integer + required: + - name + type: object + x-kubernetes-validations: + - message: Must have port for Service reference + rule: '(size(self.group) == 0 && self.kind == ''Service'') + ? has(self.port) : true' + required: + - backendRef + type: object + requestRedirect: + description: |- + RequestRedirect defines a schema for a filter that responds to the + request with an HTTP redirection. + + + Support: Core + properties: + hostname: + description: |- + Hostname is the hostname to be used in the value of the `Location` + header in the response. + When empty, the hostname in the `Host` header of the request is used. + + + Support: Core + maxLength: 253 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + path: + description: |- + Path defines parameters used to modify the path of the incoming request. + The modified path is then used to construct the `Location` header. When + empty, the request path is used as-is. + + + Support: Extended + properties: + replaceFullPath: + description: |- + ReplaceFullPath specifies the value with which to replace the full path + of a request during a rewrite or redirect. + maxLength: 1024 + type: string + replacePrefixMatch: + description: |- + ReplacePrefixMatch specifies the value with which to replace the prefix + match of a request during a rewrite or redirect. For example, a request + to "/foo/bar" with a prefix match of "/foo" and a ReplacePrefixMatch + of "/xyz" would be modified to "/xyz/bar". + + + Note that this matches the behavior of the PathPrefix match type. This + matches full path elements. A path element refers to the list of labels + in the path split by the `/` separator. When specified, a trailing `/` is + ignored. For example, the paths `/abc`, `/abc/`, and `/abc/def` would all + match the prefix `/abc`, but the path `/abcd` would not. + + + ReplacePrefixMatch is only compatible with a `PathPrefix` HTTPRouteMatch. + Using any other HTTPRouteMatch type on the same HTTPRouteRule will result in + the implementation setting the Accepted Condition for the Route to `status: False`. + + + Request Path | Prefix Match | Replace Prefix | Modified Path + -------------|--------------|----------------|---------- + /foo/bar | /foo | /xyz | /xyz/bar + /foo/bar | /foo | /xyz/ | /xyz/bar + /foo/bar | /foo/ | /xyz | /xyz/bar + /foo/bar | /foo/ | /xyz/ | /xyz/bar + /foo | /foo | /xyz | /xyz + /foo/ | /foo | /xyz | /xyz/ + /foo/bar | /foo | | /bar + /foo/ | /foo | | / + /foo | /foo | | / + /foo/ | /foo | / | / + /foo | /foo | / | / + maxLength: 1024 + type: string + type: + description: |- + Type defines the type of path modifier. Additional types may be + added in a future release of the API. + + + Note that values may be added to this enum, implementations + must ensure that unknown values will not cause a crash. + + + Unknown values here must result in the implementation setting the + Accepted Condition for the Route to `status: False`, with a + Reason of `UnsupportedValue`. + enum: + - ReplaceFullPath + - ReplacePrefixMatch + type: string + required: + - type + type: object + x-kubernetes-validations: + - message: replaceFullPath must be specified when + type is set to 'ReplaceFullPath' + rule: 'self.type == ''ReplaceFullPath'' ? has(self.replaceFullPath) + : true' + - message: type must be 'ReplaceFullPath' when replaceFullPath + is set + rule: 'has(self.replaceFullPath) ? self.type == + ''ReplaceFullPath'' : true' + - message: replacePrefixMatch must be specified when + type is set to 'ReplacePrefixMatch' + rule: 'self.type == ''ReplacePrefixMatch'' ? has(self.replacePrefixMatch) + : true' + - message: type must be 'ReplacePrefixMatch' when + replacePrefixMatch is set + rule: 'has(self.replacePrefixMatch) ? self.type + == ''ReplacePrefixMatch'' : true' + port: + description: |- + Port is the port to be used in the value of the `Location` + header in the response. + + + If no port is specified, the redirect port MUST be derived using the + following rules: + + + * If redirect scheme is not-empty, the redirect port MUST be the well-known + port associated with the redirect scheme. Specifically "http" to port 80 + and "https" to port 443. If the redirect scheme does not have a + well-known port, the listener port of the Gateway SHOULD be used. + * If redirect scheme is empty, the redirect port MUST be the Gateway + Listener port. + + + Implementations SHOULD NOT add the port number in the 'Location' + header in the following cases: + + + * A Location header that will use HTTP (whether that is determined via + the Listener protocol or the Scheme field) _and_ use port 80. + * A Location header that will use HTTPS (whether that is determined via + the Listener protocol or the Scheme field) _and_ use port 443. + + + Support: Extended + format: int32 + maximum: 65535 + minimum: 1 + type: integer + scheme: + description: |- + Scheme is the scheme to be used in the value of the `Location` header in + the response. When empty, the scheme of the request is used. + + + Scheme redirects can affect the port of the redirect, for more information, + refer to the documentation for the port field of this filter. + + + Note that values may be added to this enum, implementations + must ensure that unknown values will not cause a crash. + + + Unknown values here must result in the implementation setting the + Accepted Condition for the Route to `status: False`, with a + Reason of `UnsupportedValue`. + + + Support: Extended + enum: + - http + - https + type: string + statusCode: + default: 302 + description: |- + StatusCode is the HTTP status code to be used in response. + + + Note that values may be added to this enum, implementations + must ensure that unknown values will not cause a crash. + + + Unknown values here must result in the implementation setting the + Accepted Condition for the Route to `status: False`, with a + Reason of `UnsupportedValue`. + + + Support: Core + enum: + - 301 + - 302 + type: integer + type: object + responseHeaderModifier: + description: |- + ResponseHeaderModifier defines a schema for a filter that modifies response + headers. + + + Support: Extended + properties: + add: + description: |- + Add adds the given header(s) (name, value) to the request + before the action. It appends to any existing values associated + with the header name. + + + Input: + GET /foo HTTP/1.1 + my-header: foo + + + Config: + add: + - name: "my-header" + value: "bar,baz" + + + Output: + GET /foo HTTP/1.1 + my-header: foo,bar,baz + items: + description: HTTPHeader represents an HTTP Header + name and value as defined by RFC 7230. + properties: + name: + description: |- + Name is the name of the HTTP Header to be matched. Name matching MUST be + case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + + + If multiple entries specify equivalent header names, the first entry with + an equivalent name MUST be considered for a match. Subsequent entries + with an equivalent header name MUST be ignored. Due to the + case-insensitivity of header names, "foo" and "Foo" are considered + equivalent. + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP Header + to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + remove: + description: |- + Remove the given header(s) from the HTTP request before the action. The + value of Remove is a list of HTTP header names. Note that the header + names are case-insensitive (see + https://datatracker.ietf.org/doc/html/rfc2616#section-4.2). + + + Input: + GET /foo HTTP/1.1 + my-header1: foo + my-header2: bar + my-header3: baz + + + Config: + remove: ["my-header1", "my-header3"] + + + Output: + GET /foo HTTP/1.1 + my-header2: bar + items: + type: string + maxItems: 16 + type: array + x-kubernetes-list-type: set + set: + description: |- + Set overwrites the request with the given header (name, value) + before the action. + + + Input: + GET /foo HTTP/1.1 + my-header: foo + + + Config: + set: + - name: "my-header" + value: "bar" + + + Output: + GET /foo HTTP/1.1 + my-header: bar + items: + description: HTTPHeader represents an HTTP Header + name and value as defined by RFC 7230. + properties: + name: + description: |- + Name is the name of the HTTP Header to be matched. Name matching MUST be + case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + + + If multiple entries specify equivalent header names, the first entry with + an equivalent name MUST be considered for a match. Subsequent entries + with an equivalent header name MUST be ignored. Due to the + case-insensitivity of header names, "foo" and "Foo" are considered + equivalent. + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP Header + to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + type: object + type: + description: |- + Type identifies the type of filter to apply. As with other API fields, + types are classified into three conformance levels: + + + - Core: Filter types and their corresponding configuration defined by + "Support: Core" in this package, e.g. "RequestHeaderModifier". All + implementations must support core filters. + + + - Extended: Filter types and their corresponding configuration defined by + "Support: Extended" in this package, e.g. "RequestMirror". Implementers + are encouraged to support extended filters. + + + - Implementation-specific: Filters that are defined and supported by + specific vendors. + In the future, filters showing convergence in behavior across multiple + implementations will be considered for inclusion in extended or core + conformance levels. Filter-specific configuration for such filters + is specified using the ExtensionRef field. `Type` should be set to + "ExtensionRef" for custom filters. + + + Implementers are encouraged to define custom implementation types to + extend the core API with implementation-specific behavior. + + + If a reference to a custom filter type cannot be resolved, the filter + MUST NOT be skipped. Instead, requests that would have been processed by + that filter MUST receive a HTTP error response. + + + Note that values may be added to this enum, implementations + must ensure that unknown values will not cause a crash. + + + Unknown values here must result in the implementation setting the + Accepted Condition for the Route to `status: False`, with a + Reason of `UnsupportedValue`. + enum: + - RequestHeaderModifier + - ResponseHeaderModifier + - RequestMirror + - RequestRedirect + - URLRewrite + - ExtensionRef + type: string + urlRewrite: + description: |- + URLRewrite defines a schema for a filter that modifies a request during forwarding. + + + Support: Extended + properties: + hostname: + description: |- + Hostname is the value to be used to replace the Host header value during + forwarding. + + + Support: Extended + maxLength: 253 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + path: + description: |- + Path defines a path rewrite. + + + Support: Extended + properties: + replaceFullPath: + description: |- + ReplaceFullPath specifies the value with which to replace the full path + of a request during a rewrite or redirect. + maxLength: 1024 + type: string + replacePrefixMatch: + description: |- + ReplacePrefixMatch specifies the value with which to replace the prefix + match of a request during a rewrite or redirect. For example, a request + to "/foo/bar" with a prefix match of "/foo" and a ReplacePrefixMatch + of "/xyz" would be modified to "/xyz/bar". + + + Note that this matches the behavior of the PathPrefix match type. This + matches full path elements. A path element refers to the list of labels + in the path split by the `/` separator. When specified, a trailing `/` is + ignored. For example, the paths `/abc`, `/abc/`, and `/abc/def` would all + match the prefix `/abc`, but the path `/abcd` would not. + + + ReplacePrefixMatch is only compatible with a `PathPrefix` HTTPRouteMatch. + Using any other HTTPRouteMatch type on the same HTTPRouteRule will result in + the implementation setting the Accepted Condition for the Route to `status: False`. + + + Request Path | Prefix Match | Replace Prefix | Modified Path + -------------|--------------|----------------|---------- + /foo/bar | /foo | /xyz | /xyz/bar + /foo/bar | /foo | /xyz/ | /xyz/bar + /foo/bar | /foo/ | /xyz | /xyz/bar + /foo/bar | /foo/ | /xyz/ | /xyz/bar + /foo | /foo | /xyz | /xyz + /foo/ | /foo | /xyz | /xyz/ + /foo/bar | /foo | | /bar + /foo/ | /foo | | / + /foo | /foo | | / + /foo/ | /foo | / | / + /foo | /foo | / | / + maxLength: 1024 + type: string + type: + description: |- + Type defines the type of path modifier. Additional types may be + added in a future release of the API. + + + Note that values may be added to this enum, implementations + must ensure that unknown values will not cause a crash. + + + Unknown values here must result in the implementation setting the + Accepted Condition for the Route to `status: False`, with a + Reason of `UnsupportedValue`. + enum: + - ReplaceFullPath + - ReplacePrefixMatch + type: string + required: + - type + type: object + x-kubernetes-validations: + - message: replaceFullPath must be specified when + type is set to 'ReplaceFullPath' + rule: 'self.type == ''ReplaceFullPath'' ? has(self.replaceFullPath) + : true' + - message: type must be 'ReplaceFullPath' when replaceFullPath + is set + rule: 'has(self.replaceFullPath) ? self.type == + ''ReplaceFullPath'' : true' + - message: replacePrefixMatch must be specified when + type is set to 'ReplacePrefixMatch' + rule: 'self.type == ''ReplacePrefixMatch'' ? has(self.replacePrefixMatch) + : true' + - message: type must be 'ReplacePrefixMatch' when + replacePrefixMatch is set + rule: 'has(self.replacePrefixMatch) ? self.type + == ''ReplacePrefixMatch'' : true' + type: object + required: + - type + type: object + x-kubernetes-validations: + - message: filter.requestHeaderModifier must be nil if the + filter.type is not RequestHeaderModifier + rule: '!(has(self.requestHeaderModifier) && self.type != + ''RequestHeaderModifier'')' + - message: filter.requestHeaderModifier must be specified + for RequestHeaderModifier filter.type + rule: '!(!has(self.requestHeaderModifier) && self.type == + ''RequestHeaderModifier'')' + - message: filter.responseHeaderModifier must be nil if the + filter.type is not ResponseHeaderModifier + rule: '!(has(self.responseHeaderModifier) && self.type != + ''ResponseHeaderModifier'')' + - message: filter.responseHeaderModifier must be specified + for ResponseHeaderModifier filter.type + rule: '!(!has(self.responseHeaderModifier) && self.type + == ''ResponseHeaderModifier'')' + - message: filter.requestMirror must be nil if the filter.type + is not RequestMirror + rule: '!(has(self.requestMirror) && self.type != ''RequestMirror'')' + - message: filter.requestMirror must be specified for RequestMirror + filter.type + rule: '!(!has(self.requestMirror) && self.type == ''RequestMirror'')' + - message: filter.requestRedirect must be nil if the filter.type + is not RequestRedirect + rule: '!(has(self.requestRedirect) && self.type != ''RequestRedirect'')' + - message: filter.requestRedirect must be specified for RequestRedirect + filter.type + rule: '!(!has(self.requestRedirect) && self.type == ''RequestRedirect'')' + - message: filter.urlRewrite must be nil if the filter.type + is not URLRewrite + rule: '!(has(self.urlRewrite) && self.type != ''URLRewrite'')' + - message: filter.urlRewrite must be specified for URLRewrite + filter.type + rule: '!(!has(self.urlRewrite) && self.type == ''URLRewrite'')' + - message: filter.extensionRef must be nil if the filter.type + is not ExtensionRef + rule: '!(has(self.extensionRef) && self.type != ''ExtensionRef'')' + - message: filter.extensionRef must be specified for ExtensionRef + filter.type + rule: '!(!has(self.extensionRef) && self.type == ''ExtensionRef'')' + maxItems: 16 + type: array + x-kubernetes-validations: + - message: May specify either httpRouteFilterRequestRedirect + or httpRouteFilterRequestRewrite, but not both + rule: '!(self.exists(f, f.type == ''RequestRedirect'') && + self.exists(f, f.type == ''URLRewrite''))' + - message: RequestHeaderModifier filter cannot be repeated + rule: self.filter(f, f.type == 'RequestHeaderModifier').size() + <= 1 + - message: ResponseHeaderModifier filter cannot be repeated + rule: self.filter(f, f.type == 'ResponseHeaderModifier').size() + <= 1 + - message: RequestRedirect filter cannot be repeated + rule: self.filter(f, f.type == 'RequestRedirect').size() <= + 1 + - message: URLRewrite filter cannot be repeated + rule: self.filter(f, f.type == 'URLRewrite').size() <= 1 + matches: + default: + - path: + type: PathPrefix + value: / + description: |- + Matches define conditions used for matching the rule against incoming + HTTP requests. Each match is independent, i.e. this rule will be matched + if **any** one of the matches is satisfied. + + + For example, take the following matches configuration: + + + ``` + matches: + - path: + value: "/foo" + headers: + - name: "version" + value: "v2" + - path: + value: "/v2/foo" + ``` + + + For a request to match against this rule, a request must satisfy + EITHER of the two conditions: + + + - path prefixed with `/foo` AND contains the header `version: v2` + - path prefix of `/v2/foo` + + + See the documentation for HTTPRouteMatch on how to specify multiple + match conditions that should be ANDed together. + + + If no matches are specified, the default is a prefix + path match on "/", which has the effect of matching every + HTTP request. + + + Proxy or Load Balancer routing configuration generated from HTTPRoutes + MUST prioritize matches based on the following criteria, continuing on + ties. Across all rules specified on applicable Routes, precedence must be + given to the match having: + + + * "Exact" path match. + * "Prefix" path match with largest number of characters. + * Method match. + * Largest number of header matches. + * Largest number of query param matches. + + + Note: The precedence of RegularExpression path matches are implementation-specific. + + + If ties still exist across multiple Routes, matching precedence MUST be + determined in order of the following criteria, continuing on ties: + + + * The oldest Route based on creation timestamp. + * The Route appearing first in alphabetical order by + "{namespace}/{name}". + + + If ties still exist within an HTTPRoute, matching precedence MUST be granted + to the FIRST matching rule (in list order) with a match meeting the above + criteria. + + + When no rules matching a request have been successfully attached to the + parent a request is coming from, a HTTP 404 status code MUST be returned. + items: + description: "HTTPRouteMatch defines the predicate used to + match requests to a given\naction. Multiple match types + are ANDed together, i.e. the match will\nevaluate to true + only if all conditions are satisfied.\n\n\nFor example, + the match below will match a HTTP request only if its path\nstarts + with `/foo` AND it contains the `version: v1` header:\n\n\n```\nmatch:\n\n\n\tpath:\n\t + \ value: \"/foo\"\n\theaders:\n\t- name: \"version\"\n\t + \ value \"v1\"\n\n\n```" + properties: + headers: + description: |- + Headers specifies HTTP request header matchers. Multiple match values are + ANDed together, meaning, a request must match all the specified headers + to select the route. + items: + description: |- + HTTPHeaderMatch describes how to select a HTTP route by matching HTTP request + headers. + properties: + name: + description: |- + Name is the name of the HTTP Header to be matched. Name matching MUST be + case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + + + If multiple entries specify equivalent header names, only the first + entry with an equivalent name MUST be considered for a match. Subsequent + entries with an equivalent header name MUST be ignored. Due to the + case-insensitivity of header names, "foo" and "Foo" are considered + equivalent. + + + When a header is repeated in an HTTP request, it is + implementation-specific behavior as to how this is represented. + Generally, proxies should follow the guidance from the RFC: + https://www.rfc-editor.org/rfc/rfc7230.html#section-3.2.2 regarding + processing a repeated header, with special handling for "Set-Cookie". + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + type: + default: Exact + description: |- + Type specifies how to match against the value of the header. + + + Support: Core (Exact) + + + Support: Implementation-specific (RegularExpression) + + + Since RegularExpression HeaderMatchType has implementation-specific + conformance, implementations can support POSIX, PCRE or any other dialects + of regular expressions. Please read the implementation's documentation to + determine the supported dialect. + enum: + - Exact + - RegularExpression + type: string + value: + description: Value is the value of HTTP Header to + be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + method: + description: |- + Method specifies HTTP method matcher. + When specified, this route will be matched only if the request has the + specified method. + + + Support: Extended + enum: + - GET + - HEAD + - POST + - PUT + - DELETE + - CONNECT + - OPTIONS + - TRACE + - PATCH + type: string + path: + default: + type: PathPrefix + value: / + description: |- + Path specifies a HTTP request path matcher. If this field is not + specified, a default prefix match on the "/" path is provided. + properties: + type: + default: PathPrefix + description: |- + Type specifies how to match against the path Value. + + + Support: Core (Exact, PathPrefix) + + + Support: Implementation-specific (RegularExpression) + enum: + - Exact + - PathPrefix + - RegularExpression + type: string + value: + default: / + description: Value of the HTTP path to match against. + maxLength: 1024 + type: string + type: object + x-kubernetes-validations: + - message: value must be an absolute path and start with + '/' when type one of ['Exact', 'PathPrefix'] + rule: '(self.type in [''Exact'',''PathPrefix'']) ? self.value.startsWith(''/'') + : true' + - message: must not contain '//' when type one of ['Exact', + 'PathPrefix'] + rule: '(self.type in [''Exact'',''PathPrefix'']) ? !self.value.contains(''//'') + : true' + - message: must not contain '/./' when type one of ['Exact', + 'PathPrefix'] + rule: '(self.type in [''Exact'',''PathPrefix'']) ? !self.value.contains(''/./'') + : true' + - message: must not contain '/../' when type one of ['Exact', + 'PathPrefix'] + rule: '(self.type in [''Exact'',''PathPrefix'']) ? !self.value.contains(''/../'') + : true' + - message: must not contain '%2f' when type one of ['Exact', + 'PathPrefix'] + rule: '(self.type in [''Exact'',''PathPrefix'']) ? !self.value.contains(''%2f'') + : true' + - message: must not contain '%2F' when type one of ['Exact', + 'PathPrefix'] + rule: '(self.type in [''Exact'',''PathPrefix'']) ? !self.value.contains(''%2F'') + : true' + - message: must not contain '#' when type one of ['Exact', + 'PathPrefix'] + rule: '(self.type in [''Exact'',''PathPrefix'']) ? !self.value.contains(''#'') + : true' + - message: must not end with '/..' when type one of ['Exact', + 'PathPrefix'] + rule: '(self.type in [''Exact'',''PathPrefix'']) ? !self.value.endsWith(''/..'') + : true' + - message: must not end with '/.' when type one of ['Exact', + 'PathPrefix'] + rule: '(self.type in [''Exact'',''PathPrefix'']) ? !self.value.endsWith(''/.'') + : true' + - message: type must be one of ['Exact', 'PathPrefix', + 'RegularExpression'] + rule: self.type in ['Exact','PathPrefix'] || self.type + == 'RegularExpression' + - message: must only contain valid characters (matching + ^(?:[-A-Za-z0-9/._~!$&'()*+,;=:@]|[%][0-9a-fA-F]{2})+$) + for types ['Exact', 'PathPrefix'] + rule: '(self.type in [''Exact'',''PathPrefix'']) ? self.value.matches(r"""^(?:[-A-Za-z0-9/._~!$&''()*+,;=:@]|[%][0-9a-fA-F]{2})+$""") + : true' + queryParams: + description: |- + QueryParams specifies HTTP query parameter matchers. Multiple match + values are ANDed together, meaning, a request must match all the + specified query parameters to select the route. + + + Support: Extended + items: + description: |- + HTTPQueryParamMatch describes how to select a HTTP route by matching HTTP + query parameters. + properties: + name: + description: |- + Name is the name of the HTTP query param to be matched. This must be an + exact string match. (See + https://tools.ietf.org/html/rfc7230#section-2.7.3). + + + If multiple entries specify equivalent query param names, only the first + entry with an equivalent name MUST be considered for a match. Subsequent + entries with an equivalent query param name MUST be ignored. + + + If a query param is repeated in an HTTP request, the behavior is + purposely left undefined, since different data planes have different + capabilities. However, it is *recommended* that implementations should + match against the first value of the param if the data plane supports it, + as this behavior is expected in other load balancing contexts outside of + the Gateway API. + + + Users SHOULD NOT route traffic based on repeated query params to guard + themselves against potential differences in the implementations. + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + type: + default: Exact + description: |- + Type specifies how to match against the value of the query parameter. + + + Support: Extended (Exact) + + + Support: Implementation-specific (RegularExpression) + + + Since RegularExpression QueryParamMatchType has Implementation-specific + conformance, implementations can support POSIX, PCRE or any other + dialects of regular expressions. Please read the implementation's + documentation to determine the supported dialect. + enum: + - Exact + - RegularExpression + type: string + value: + description: Value is the value of HTTP query param + to be matched. + maxLength: 1024 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + type: object + maxItems: 8 + type: array + type: object + x-kubernetes-validations: + - message: RequestRedirect filter must not be used together with + backendRefs + rule: '(has(self.backendRefs) && size(self.backendRefs) > 0) ? + (!has(self.filters) || self.filters.all(f, !has(f.requestRedirect))): + true' + - message: When using RequestRedirect filter with path.replacePrefixMatch, + exactly one PathPrefix match must be specified + rule: '(has(self.filters) && self.filters.exists_one(f, has(f.requestRedirect) + && has(f.requestRedirect.path) && f.requestRedirect.path.type + == ''ReplacePrefixMatch'' && has(f.requestRedirect.path.replacePrefixMatch))) + ? ((size(self.matches) != 1 || !has(self.matches[0].path) || + self.matches[0].path.type != ''PathPrefix'') ? false : true) + : true' + - message: When using URLRewrite filter with path.replacePrefixMatch, + exactly one PathPrefix match must be specified + rule: '(has(self.filters) && self.filters.exists_one(f, has(f.urlRewrite) + && has(f.urlRewrite.path) && f.urlRewrite.path.type == ''ReplacePrefixMatch'' + && has(f.urlRewrite.path.replacePrefixMatch))) ? ((size(self.matches) + != 1 || !has(self.matches[0].path) || self.matches[0].path.type + != ''PathPrefix'') ? false : true) : true' + - message: Within backendRefs, when using RequestRedirect filter + with path.replacePrefixMatch, exactly one PathPrefix match must + be specified + rule: '(has(self.backendRefs) && self.backendRefs.exists_one(b, + (has(b.filters) && b.filters.exists_one(f, has(f.requestRedirect) + && has(f.requestRedirect.path) && f.requestRedirect.path.type + == ''ReplacePrefixMatch'' && has(f.requestRedirect.path.replacePrefixMatch))) + )) ? ((size(self.matches) != 1 || !has(self.matches[0].path) + || self.matches[0].path.type != ''PathPrefix'') ? false : true) + : true' + - message: Within backendRefs, When using URLRewrite filter with + path.replacePrefixMatch, exactly one PathPrefix match must be + specified + rule: '(has(self.backendRefs) && self.backendRefs.exists_one(b, + (has(b.filters) && b.filters.exists_one(f, has(f.urlRewrite) + && has(f.urlRewrite.path) && f.urlRewrite.path.type == ''ReplacePrefixMatch'' + && has(f.urlRewrite.path.replacePrefixMatch))) )) ? ((size(self.matches) + != 1 || !has(self.matches[0].path) || self.matches[0].path.type + != ''PathPrefix'') ? false : true) : true' + maxItems: 16 + type: array + type: object + status: + description: Status defines the current state of HTTPRoute. + properties: + parents: + description: |- + Parents is a list of parent resources (usually Gateways) that are + associated with the route, and the status of the route with respect to + each parent. When this route attaches to a parent, the controller that + manages the parent must add an entry to this list when the controller + first sees the route and should update the entry as appropriate when the + route or gateway is modified. + + + Note that parent references that cannot be resolved by an implementation + of this API will not be added to this list. Implementations of this API + can only populate Route status for the Gateways/parent resources they are + responsible for. + + + A maximum of 32 Gateways will be represented in this list. An empty list + means the route has not been attached to any Gateway. + items: + description: |- + RouteParentStatus describes the status of a route with respect to an + associated Parent. + properties: + conditions: + description: |- + Conditions describes the status of the route with respect to the Gateway. + Note that the route's availability is also subject to the Gateway's own + status conditions and listener status. + + + If the Route's ParentRef specifies an existing Gateway that supports + Routes of this kind AND that Gateway's controller has sufficient access, + then that Gateway's controller MUST set the "Accepted" condition on the + Route, to indicate whether the route has been accepted or rejected by the + Gateway, and why. + + + A Route MUST be considered "Accepted" if at least one of the Route's + rules is implemented by the Gateway. + + + There are a number of cases where the "Accepted" condition may not be set + due to lack of controller visibility, that includes when: + + + * The Route refers to a non-existent parent. + * The Route is of a type that the controller does not support. + * The Route is in a namespace the controller does not have access to. + 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 + maxItems: 8 + minItems: 1 + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + controllerName: + description: |- + ControllerName is a domain/path string that indicates the name of the + controller that wrote this status. This corresponds with the + controllerName field on GatewayClass. + + + Example: "example.net/gateway-controller". + + + The format of this field is DOMAIN "/" PATH, where DOMAIN and PATH are + valid Kubernetes names + (https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names). + + + Controllers MUST populate this field when writing status. Controllers should ensure that + entries to status populated with their ControllerName are cleaned up when they are no + longer necessary. + maxLength: 253 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*\/[A-Za-z0-9\/\-._~%!$&'()*+,;=:]+$ + type: string + parentRef: + description: |- + ParentRef corresponds with a ParentRef in the spec that this + RouteParentStatus struct describes the status of. + properties: + group: + default: gateway.networking.k8s.io + description: |- + Group is the group of the referent. + When unspecified, "gateway.networking.k8s.io" is inferred. + To set the core API group (such as for a "Service" kind referent), + Group must be explicitly set to "" (empty string). + + + Support: Core + maxLength: 253 + pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + kind: + default: Gateway + description: |- + Kind is kind of the referent. + + + There are two kinds of parent resources with "Core" support: + + + * Gateway (Gateway conformance profile) + * Service (Mesh conformance profile, ClusterIP Services only) + + + Support for other resources is Implementation-Specific. + maxLength: 63 + minLength: 1 + pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ + type: string + name: + description: |- + Name is the name of the referent. + + + Support: Core + maxLength: 253 + minLength: 1 + type: string + namespace: + description: |- + Namespace is the namespace of the referent. When unspecified, this refers + to the local namespace of the Route. + + + Note that there are specific rules for ParentRefs which cross namespace + boundaries. Cross-namespace references are only valid if they are explicitly + allowed by something in the namespace they are referring to. For example: + Gateway has the AllowedRoutes field, and ReferenceGrant provides a + generic way to enable any other kind of cross-namespace reference. + + + + + + Support: Core + maxLength: 63 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ + type: string + port: + description: |- + Port is the network port this Route targets. It can be interpreted + differently based on the type of parent resource. + + + When the parent resource is a Gateway, this targets all listeners + listening on the specified port that also support this kind of Route(and + select this Route). It's not recommended to set `Port` unless the + networking behaviors specified in a Route must apply to a specific port + as opposed to a listener(s) whose port(s) may be changed. When both Port + and SectionName are specified, the name and port of the selected listener + must match both specified values. + + + + + + Implementations MAY choose to support other parent resources. + Implementations supporting other types of parent resources MUST clearly + document how/if Port is interpreted. + + + For the purpose of status, an attachment is considered successful as + long as the parent resource accepts it partially. For example, Gateway + listeners can restrict which Routes can attach to them by Route kind, + namespace, or hostname. If 1 of 2 Gateway listeners accept attachment + from the referencing Route, the Route MUST be considered successfully + attached. If no Gateway listeners accept attachment from this Route, + the Route MUST be considered detached from the Gateway. + + + Support: Extended + format: int32 + maximum: 65535 + minimum: 1 + type: integer + sectionName: + description: |- + SectionName is the name of a section within the target resource. In the + following resources, SectionName is interpreted as the following: + + + * Gateway: Listener name. When both Port (experimental) and SectionName + are specified, the name and port of the selected listener must match + both specified values. + * Service: Port name. When both Port (experimental) and SectionName + are specified, the name and port of the selected listener must match + both specified values. + + + Implementations MAY choose to support attaching Routes to other resources. + If that is the case, they MUST clearly document how SectionName is + interpreted. + + + When unspecified (empty string), this will reference the entire resource. + For the purpose of status, an attachment is considered successful if at + least one section in the parent resource accepts it. For example, Gateway + listeners can restrict which Routes can attach to them by Route kind, + namespace, or hostname. If 1 of 2 Gateway listeners accept attachment from + the referencing Route, the Route MUST be considered successfully + attached. If no Gateway listeners accept attachment from this Route, the + Route MUST be considered detached from the Gateway. + + + Support: Core + maxLength: 253 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + required: + - name + type: object + required: + - controllerName + - parentRef + type: object + maxItems: 32 + type: array + required: + - parents + type: object + required: + - spec + type: object + served: true + storage: true + subresources: + status: {} + - additionalPrinterColumns: + - jsonPath: .spec.hostnames + name: Hostnames + type: string + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v1beta1 + schema: + openAPIV3Schema: + description: |- + HTTPRoute provides a way to route HTTP requests. This includes the capability + to match requests by hostname, path, header, or query param. Filters can be + used to specify additional processing steps. Backends specify where matching + requests should be routed. + 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: Spec defines the desired state of HTTPRoute. + properties: + hostnames: + description: |- + Hostnames defines a set of hostnames that should match against the HTTP Host + header to select a HTTPRoute used to process the request. Implementations + MUST ignore any port value specified in the HTTP Host header while + performing a match and (absent of any applicable header modification + configuration) MUST forward this header unmodified to the backend. + + + Valid values for Hostnames are determined by RFC 1123 definition of a + hostname with 2 notable exceptions: + + + 1. IPs are not allowed. + 2. A hostname may be prefixed with a wildcard label (`*.`). The wildcard + label must appear by itself as the first label. + + + If a hostname is specified by both the Listener and HTTPRoute, there + must be at least one intersecting hostname for the HTTPRoute to be + attached to the Listener. For example: + + + * A Listener with `test.example.com` as the hostname matches HTTPRoutes + that have either not specified any hostnames, or have specified at + least one of `test.example.com` or `*.example.com`. + * A Listener with `*.example.com` as the hostname matches HTTPRoutes + that have either not specified any hostnames or have specified at least + one hostname that matches the Listener hostname. For example, + `*.example.com`, `test.example.com`, and `foo.test.example.com` would + all match. On the other hand, `example.com` and `test.example.net` would + not match. + + + Hostnames that are prefixed with a wildcard label (`*.`) are interpreted + as a suffix match. That means that a match for `*.example.com` would match + both `test.example.com`, and `foo.test.example.com`, but not `example.com`. + + + If both the Listener and HTTPRoute have specified hostnames, any + HTTPRoute hostnames that do not match the Listener hostname MUST be + ignored. For example, if a Listener specified `*.example.com`, and the + HTTPRoute specified `test.example.com` and `test.example.net`, + `test.example.net` must not be considered for a match. + + + If both the Listener and HTTPRoute have specified hostnames, and none + match with the criteria above, then the HTTPRoute is not accepted. The + implementation must raise an 'Accepted' Condition with a status of + `False` in the corresponding RouteParentStatus. + + + In the event that multiple HTTPRoutes specify intersecting hostnames (e.g. + overlapping wildcard matching and exact matching hostnames), precedence must + be given to rules from the HTTPRoute with the largest number of: + + + * Characters in a matching non-wildcard hostname. + * Characters in a matching hostname. + + + If ties exist across multiple Routes, the matching precedence rules for + HTTPRouteMatches takes over. + + + Support: Core + items: + description: |- + Hostname is the fully qualified domain name of a network host. This matches + the RFC 1123 definition of a hostname with 2 notable exceptions: + + + 1. IPs are not allowed. + 2. A hostname may be prefixed with a wildcard label (`*.`). The wildcard + label must appear by itself as the first label. + + + Hostname can be "precise" which is a domain name without the terminating + dot of a network host (e.g. "foo.example.com") or "wildcard", which is a + domain name prefixed with a single wildcard label (e.g. `*.example.com`). + + + Note that as per RFC1035 and RFC1123, a *label* must consist of lower case + alphanumeric characters or '-', and must start and end with an alphanumeric + character. No other punctuation is allowed. + maxLength: 253 + minLength: 1 + pattern: ^(\*\.)?[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + maxItems: 16 + type: array + parentRefs: + description: |+ + ParentRefs references the resources (usually Gateways) that a Route wants + to be attached to. Note that the referenced parent resource needs to + allow this for the attachment to be complete. For Gateways, that means + the Gateway needs to allow attachment from Routes of this kind and + namespace. For Services, that means the Service must either be in the same + namespace for a "producer" route, or the mesh implementation must support + and allow "consumer" routes for the referenced Service. ReferenceGrant is + not applicable for governing ParentRefs to Services - it is not possible to + create a "producer" route for a Service in a different namespace from the + Route. + + + There are two kinds of parent resources with "Core" support: + + + * Gateway (Gateway conformance profile) + * Service (Mesh conformance profile, ClusterIP Services only) + + + This API may be extended in the future to support additional kinds of parent + resources. + + + ParentRefs must be _distinct_. This means either that: + + + * They select different objects. If this is the case, then parentRef + entries are distinct. In terms of fields, this means that the + multi-part key defined by `group`, `kind`, `namespace`, and `name` must + be unique across all parentRef entries in the Route. + * They do not select different objects, but for each optional field used, + each ParentRef that selects the same object must set the same set of + optional fields to different values. If one ParentRef sets a + combination of optional fields, all must set the same combination. + + + Some examples: + + + * If one ParentRef sets `sectionName`, all ParentRefs referencing the + same object must also set `sectionName`. + * If one ParentRef sets `port`, all ParentRefs referencing the same + object must also set `port`. + * If one ParentRef sets `sectionName` and `port`, all ParentRefs + referencing the same object must also set `sectionName` and `port`. + + + It is possible to separately reference multiple distinct objects that may + be collapsed by an implementation. For example, some implementations may + choose to merge compatible Gateway Listeners together. If that is the + case, the list of routes attached to those resources should also be + merged. + + + Note that for ParentRefs that cross namespace boundaries, there are specific + rules. Cross-namespace references are only valid if they are explicitly + allowed by something in the namespace they are referring to. For example, + Gateway has the AllowedRoutes field, and ReferenceGrant provides a + generic way to enable other kinds of cross-namespace reference. + + + + + + + + + items: + description: |- + ParentReference identifies an API object (usually a Gateway) that can be considered + a parent of this resource (usually a route). There are two kinds of parent resources + with "Core" support: + + + * Gateway (Gateway conformance profile) + * Service (Mesh conformance profile, ClusterIP Services only) + + + This API may be extended in the future to support additional kinds of parent + resources. + + + The API object must be valid in the cluster; the Group and Kind must + be registered in the cluster for this reference to be valid. + properties: + group: + default: gateway.networking.k8s.io + description: |- + Group is the group of the referent. + When unspecified, "gateway.networking.k8s.io" is inferred. + To set the core API group (such as for a "Service" kind referent), + Group must be explicitly set to "" (empty string). + + + Support: Core + maxLength: 253 + pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + kind: + default: Gateway + description: |- + Kind is kind of the referent. + + + There are two kinds of parent resources with "Core" support: + + + * Gateway (Gateway conformance profile) + * Service (Mesh conformance profile, ClusterIP Services only) + + + Support for other resources is Implementation-Specific. + maxLength: 63 + minLength: 1 + pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ + type: string + name: + description: |- + Name is the name of the referent. + + + Support: Core + maxLength: 253 + minLength: 1 + type: string + namespace: + description: |- + Namespace is the namespace of the referent. When unspecified, this refers + to the local namespace of the Route. + + + Note that there are specific rules for ParentRefs which cross namespace + boundaries. Cross-namespace references are only valid if they are explicitly + allowed by something in the namespace they are referring to. For example: + Gateway has the AllowedRoutes field, and ReferenceGrant provides a + generic way to enable any other kind of cross-namespace reference. + + + + + + Support: Core + maxLength: 63 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ + type: string + port: + description: |- + Port is the network port this Route targets. It can be interpreted + differently based on the type of parent resource. + + + When the parent resource is a Gateway, this targets all listeners + listening on the specified port that also support this kind of Route(and + select this Route). It's not recommended to set `Port` unless the + networking behaviors specified in a Route must apply to a specific port + as opposed to a listener(s) whose port(s) may be changed. When both Port + and SectionName are specified, the name and port of the selected listener + must match both specified values. + + + + + + Implementations MAY choose to support other parent resources. + Implementations supporting other types of parent resources MUST clearly + document how/if Port is interpreted. + + + For the purpose of status, an attachment is considered successful as + long as the parent resource accepts it partially. For example, Gateway + listeners can restrict which Routes can attach to them by Route kind, + namespace, or hostname. If 1 of 2 Gateway listeners accept attachment + from the referencing Route, the Route MUST be considered successfully + attached. If no Gateway listeners accept attachment from this Route, + the Route MUST be considered detached from the Gateway. + + + Support: Extended + format: int32 + maximum: 65535 + minimum: 1 + type: integer + sectionName: + description: |- + SectionName is the name of a section within the target resource. In the + following resources, SectionName is interpreted as the following: + + + * Gateway: Listener name. When both Port (experimental) and SectionName + are specified, the name and port of the selected listener must match + both specified values. + * Service: Port name. When both Port (experimental) and SectionName + are specified, the name and port of the selected listener must match + both specified values. + + + Implementations MAY choose to support attaching Routes to other resources. + If that is the case, they MUST clearly document how SectionName is + interpreted. + + + When unspecified (empty string), this will reference the entire resource. + For the purpose of status, an attachment is considered successful if at + least one section in the parent resource accepts it. For example, Gateway + listeners can restrict which Routes can attach to them by Route kind, + namespace, or hostname. If 1 of 2 Gateway listeners accept attachment from + the referencing Route, the Route MUST be considered successfully + attached. If no Gateway listeners accept attachment from this Route, the + Route MUST be considered detached from the Gateway. + + + Support: Core + maxLength: 253 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + required: + - name + type: object + maxItems: 32 + type: array + x-kubernetes-validations: + - message: sectionName must be specified when parentRefs includes + 2 or more references to the same parent + rule: 'self.all(p1, self.all(p2, p1.group == p2.group && p1.kind + == p2.kind && p1.name == p2.name && (((!has(p1.__namespace__) + || p1.__namespace__ == '''') && (!has(p2.__namespace__) || p2.__namespace__ + == '''')) || (has(p1.__namespace__) && has(p2.__namespace__) && + p1.__namespace__ == p2.__namespace__ )) ? ((!has(p1.sectionName) + || p1.sectionName == '''') == (!has(p2.sectionName) || p2.sectionName + == '''')) : true))' + - message: sectionName must be unique when parentRefs includes 2 or + more references to the same parent + rule: self.all(p1, self.exists_one(p2, p1.group == p2.group && p1.kind + == p2.kind && p1.name == p2.name && (((!has(p1.__namespace__) + || p1.__namespace__ == '') && (!has(p2.__namespace__) || p2.__namespace__ + == '')) || (has(p1.__namespace__) && has(p2.__namespace__) && + p1.__namespace__ == p2.__namespace__ )) && (((!has(p1.sectionName) + || p1.sectionName == '') && (!has(p2.sectionName) || p2.sectionName + == '')) || (has(p1.sectionName) && has(p2.sectionName) && p1.sectionName + == p2.sectionName)))) + rules: + default: + - matches: + - path: + type: PathPrefix + value: / + description: Rules are a list of HTTP matchers, filters and actions. + items: + description: |- + HTTPRouteRule defines semantics for matching an HTTP request based on + conditions (matches), processing it (filters), and forwarding the request to + an API object (backendRefs). + properties: + backendRefs: + description: |- + BackendRefs defines the backend(s) where matching requests should be + sent. + + + Failure behavior here depends on how many BackendRefs are specified and + how many are invalid. + + + If *all* entries in BackendRefs are invalid, and there are also no filters + specified in this route rule, *all* traffic which matches this rule MUST + receive a 500 status code. + + + See the HTTPBackendRef definition for the rules about what makes a single + HTTPBackendRef invalid. + + + When a HTTPBackendRef is invalid, 500 status codes MUST be returned for + requests that would have otherwise been routed to an invalid backend. If + multiple backends are specified, and some are invalid, the proportion of + requests that would otherwise have been routed to an invalid backend + MUST receive a 500 status code. + + + For example, if two backends are specified with equal weights, and one is + invalid, 50 percent of traffic must receive a 500. Implementations may + choose how that 50 percent is determined. + + + Support: Core for Kubernetes Service + + + Support: Extended for Kubernetes ServiceImport + + + Support: Implementation-specific for any other resource + + + Support for weight: Core + items: + description: |- + HTTPBackendRef defines how a HTTPRoute forwards a HTTP request. + + + Note that when a namespace different than the local namespace is specified, a + ReferenceGrant object is required in the referent namespace to allow that + namespace's owner to accept the reference. See the ReferenceGrant + documentation for details. + + + + + + When the BackendRef points to a Kubernetes Service, implementations SHOULD + honor the appProtocol field if it is set for the target Service Port. + + + Implementations supporting appProtocol SHOULD recognize the Kubernetes + Standard Application Protocols defined in KEP-3726. + + + If a Service appProtocol isn't specified, an implementation MAY infer the + backend protocol through its own means. Implementations MAY infer the + protocol from the Route type referring to the backend Service. + + + If a Route is not able to send traffic to the backend using the specified + protocol then the backend is considered invalid. Implementations MUST set the + "ResolvedRefs" condition to "False" with the "UnsupportedProtocol" reason. + + + + properties: + filters: + description: |- + Filters defined at this level should be executed if and only if the + request is being forwarded to the backend defined here. + + + Support: Implementation-specific (For broader support of filters, use the + Filters field in HTTPRouteRule.) + items: + description: |- + HTTPRouteFilter defines processing steps that must be completed during the + request or response lifecycle. HTTPRouteFilters are meant as an extension + point to express processing that may be done in Gateway implementations. Some + examples include request or response modification, implementing + authentication strategies, rate-limiting, and traffic shaping. API + guarantee/conformance is defined based on the type of the filter. + properties: + extensionRef: + description: |- + ExtensionRef is an optional, implementation-specific extension to the + "filter" behavior. For example, resource "myroutefilter" in group + "networking.example.net"). ExtensionRef MUST NOT be used for core and + extended filters. + + + This filter can be used multiple times within the same rule. + + + Support: Implementation-specific + properties: + group: + description: |- + Group is the group of the referent. For example, "gateway.networking.k8s.io". + When unspecified or empty string, core API group is inferred. + maxLength: 253 + pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + kind: + description: Kind is kind of the referent. For + example "HTTPRoute" or "Service". + maxLength: 63 + minLength: 1 + pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ + type: string + name: + description: Name is the name of the referent. + maxLength: 253 + minLength: 1 + type: string + required: + - group + - kind + - name + type: object + requestHeaderModifier: + description: |- + RequestHeaderModifier defines a schema for a filter that modifies request + headers. + + + Support: Core + properties: + add: + description: |- + Add adds the given header(s) (name, value) to the request + before the action. It appends to any existing values associated + with the header name. + + + Input: + GET /foo HTTP/1.1 + my-header: foo + + + Config: + add: + - name: "my-header" + value: "bar,baz" + + + Output: + GET /foo HTTP/1.1 + my-header: foo,bar,baz + items: + description: HTTPHeader represents an HTTP + Header name and value as defined by RFC + 7230. + properties: + name: + description: |- + Name is the name of the HTTP Header to be matched. Name matching MUST be + case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + + + If multiple entries specify equivalent header names, the first entry with + an equivalent name MUST be considered for a match. Subsequent entries + with an equivalent header name MUST be ignored. Due to the + case-insensitivity of header names, "foo" and "Foo" are considered + equivalent. + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP + Header to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + remove: + description: |- + Remove the given header(s) from the HTTP request before the action. The + value of Remove is a list of HTTP header names. Note that the header + names are case-insensitive (see + https://datatracker.ietf.org/doc/html/rfc2616#section-4.2). + + + Input: + GET /foo HTTP/1.1 + my-header1: foo + my-header2: bar + my-header3: baz + + + Config: + remove: ["my-header1", "my-header3"] + + + Output: + GET /foo HTTP/1.1 + my-header2: bar + items: + type: string + maxItems: 16 + type: array + x-kubernetes-list-type: set + set: + description: |- + Set overwrites the request with the given header (name, value) + before the action. + + + Input: + GET /foo HTTP/1.1 + my-header: foo + + + Config: + set: + - name: "my-header" + value: "bar" + + + Output: + GET /foo HTTP/1.1 + my-header: bar + items: + description: HTTPHeader represents an HTTP + Header name and value as defined by RFC + 7230. + properties: + name: + description: |- + Name is the name of the HTTP Header to be matched. Name matching MUST be + case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + + + If multiple entries specify equivalent header names, the first entry with + an equivalent name MUST be considered for a match. Subsequent entries + with an equivalent header name MUST be ignored. Due to the + case-insensitivity of header names, "foo" and "Foo" are considered + equivalent. + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP + Header to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + type: object + requestMirror: + description: |- + RequestMirror defines a schema for a filter that mirrors requests. + Requests are sent to the specified destination, but responses from + that destination are ignored. + + + This filter can be used multiple times within the same rule. Note that + not all implementations will be able to support mirroring to multiple + backends. + + + Support: Extended + properties: + backendRef: + description: |- + BackendRef references a resource where mirrored requests are sent. + + + Mirrored requests must be sent only to a single destination endpoint + within this BackendRef, irrespective of how many endpoints are present + within this BackendRef. + + + If the referent cannot be found, this BackendRef is invalid and must be + dropped from the Gateway. The controller must ensure the "ResolvedRefs" + condition on the Route status is set to `status: False` and not configure + this backend in the underlying implementation. + + + If there is a cross-namespace reference to an *existing* object + that is not allowed by a ReferenceGrant, the controller must ensure the + "ResolvedRefs" condition on the Route is set to `status: False`, + with the "RefNotPermitted" reason and not configure this backend in the + underlying implementation. + + + In either error case, the Message of the `ResolvedRefs` Condition + should be used to provide more detail about the problem. + + + Support: Extended for Kubernetes Service + + + Support: Implementation-specific for any other resource + properties: + group: + default: "" + description: |- + Group is the group of the referent. For example, "gateway.networking.k8s.io". + When unspecified or empty string, core API group is inferred. + maxLength: 253 + pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + kind: + default: Service + description: |- + Kind is the Kubernetes resource kind of the referent. For example + "Service". + + + Defaults to "Service" when not specified. + + + ExternalName services can refer to CNAME DNS records that may live + outside of the cluster and as such are difficult to reason about in + terms of conformance. They also may not be safe to forward to (see + CVE-2021-25740 for more information). Implementations SHOULD NOT + support ExternalName Services. + + + Support: Core (Services with a type other than ExternalName) + + + Support: Implementation-specific (Services with type ExternalName) + maxLength: 63 + minLength: 1 + pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ + type: string + name: + description: Name is the name of the referent. + maxLength: 253 + minLength: 1 + type: string + namespace: + description: |- + Namespace is the namespace of the backend. When unspecified, the local + namespace is inferred. + + + Note that when a namespace different than the local namespace is specified, + a ReferenceGrant object is required in the referent namespace to allow that + namespace's owner to accept the reference. See the ReferenceGrant + documentation for details. + + + Support: Core + maxLength: 63 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ + type: string + port: + description: |- + Port specifies the destination port number to use for this resource. + Port is required when the referent is a Kubernetes Service. In this + case, the port number is the service port number, not the target port. + For other resources, destination port might be derived from the referent + resource or this field. + format: int32 + maximum: 65535 + minimum: 1 + type: integer + required: + - name + type: object + x-kubernetes-validations: + - message: Must have port for Service reference + rule: '(size(self.group) == 0 && self.kind + == ''Service'') ? has(self.port) : true' + required: + - backendRef + type: object + requestRedirect: + description: |- + RequestRedirect defines a schema for a filter that responds to the + request with an HTTP redirection. + + + Support: Core + properties: + hostname: + description: |- + Hostname is the hostname to be used in the value of the `Location` + header in the response. + When empty, the hostname in the `Host` header of the request is used. + + + Support: Core + maxLength: 253 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + path: + description: |- + Path defines parameters used to modify the path of the incoming request. + The modified path is then used to construct the `Location` header. When + empty, the request path is used as-is. + + + Support: Extended + properties: + replaceFullPath: + description: |- + ReplaceFullPath specifies the value with which to replace the full path + of a request during a rewrite or redirect. + maxLength: 1024 + type: string + replacePrefixMatch: + description: |- + ReplacePrefixMatch specifies the value with which to replace the prefix + match of a request during a rewrite or redirect. For example, a request + to "/foo/bar" with a prefix match of "/foo" and a ReplacePrefixMatch + of "/xyz" would be modified to "/xyz/bar". + + + Note that this matches the behavior of the PathPrefix match type. This + matches full path elements. A path element refers to the list of labels + in the path split by the `/` separator. When specified, a trailing `/` is + ignored. For example, the paths `/abc`, `/abc/`, and `/abc/def` would all + match the prefix `/abc`, but the path `/abcd` would not. + + + ReplacePrefixMatch is only compatible with a `PathPrefix` HTTPRouteMatch. + Using any other HTTPRouteMatch type on the same HTTPRouteRule will result in + the implementation setting the Accepted Condition for the Route to `status: False`. + + + Request Path | Prefix Match | Replace Prefix | Modified Path + -------------|--------------|----------------|---------- + /foo/bar | /foo | /xyz | /xyz/bar + /foo/bar | /foo | /xyz/ | /xyz/bar + /foo/bar | /foo/ | /xyz | /xyz/bar + /foo/bar | /foo/ | /xyz/ | /xyz/bar + /foo | /foo | /xyz | /xyz + /foo/ | /foo | /xyz | /xyz/ + /foo/bar | /foo | | /bar + /foo/ | /foo | | / + /foo | /foo | | / + /foo/ | /foo | / | / + /foo | /foo | / | / + maxLength: 1024 + type: string + type: + description: |- + Type defines the type of path modifier. Additional types may be + added in a future release of the API. + + + Note that values may be added to this enum, implementations + must ensure that unknown values will not cause a crash. + + + Unknown values here must result in the implementation setting the + Accepted Condition for the Route to `status: False`, with a + Reason of `UnsupportedValue`. + enum: + - ReplaceFullPath + - ReplacePrefixMatch + type: string + required: + - type + type: object + x-kubernetes-validations: + - message: replaceFullPath must be specified + when type is set to 'ReplaceFullPath' + rule: 'self.type == ''ReplaceFullPath'' ? + has(self.replaceFullPath) : true' + - message: type must be 'ReplaceFullPath' when + replaceFullPath is set + rule: 'has(self.replaceFullPath) ? self.type + == ''ReplaceFullPath'' : true' + - message: replacePrefixMatch must be specified + when type is set to 'ReplacePrefixMatch' + rule: 'self.type == ''ReplacePrefixMatch'' + ? has(self.replacePrefixMatch) : true' + - message: type must be 'ReplacePrefixMatch' + when replacePrefixMatch is set + rule: 'has(self.replacePrefixMatch) ? self.type + == ''ReplacePrefixMatch'' : true' + port: + description: |- + Port is the port to be used in the value of the `Location` + header in the response. + + + If no port is specified, the redirect port MUST be derived using the + following rules: + + + * If redirect scheme is not-empty, the redirect port MUST be the well-known + port associated with the redirect scheme. Specifically "http" to port 80 + and "https" to port 443. If the redirect scheme does not have a + well-known port, the listener port of the Gateway SHOULD be used. + * If redirect scheme is empty, the redirect port MUST be the Gateway + Listener port. + + + Implementations SHOULD NOT add the port number in the 'Location' + header in the following cases: + + + * A Location header that will use HTTP (whether that is determined via + the Listener protocol or the Scheme field) _and_ use port 80. + * A Location header that will use HTTPS (whether that is determined via + the Listener protocol or the Scheme field) _and_ use port 443. + + + Support: Extended + format: int32 + maximum: 65535 + minimum: 1 + type: integer + scheme: + description: |- + Scheme is the scheme to be used in the value of the `Location` header in + the response. When empty, the scheme of the request is used. + + + Scheme redirects can affect the port of the redirect, for more information, + refer to the documentation for the port field of this filter. + + + Note that values may be added to this enum, implementations + must ensure that unknown values will not cause a crash. + + + Unknown values here must result in the implementation setting the + Accepted Condition for the Route to `status: False`, with a + Reason of `UnsupportedValue`. + + + Support: Extended + enum: + - http + - https + type: string + statusCode: + default: 302 + description: |- + StatusCode is the HTTP status code to be used in response. + + + Note that values may be added to this enum, implementations + must ensure that unknown values will not cause a crash. + + + Unknown values here must result in the implementation setting the + Accepted Condition for the Route to `status: False`, with a + Reason of `UnsupportedValue`. + + + Support: Core + enum: + - 301 + - 302 + type: integer + type: object + responseHeaderModifier: + description: |- + ResponseHeaderModifier defines a schema for a filter that modifies response + headers. + + + Support: Extended + properties: + add: + description: |- + Add adds the given header(s) (name, value) to the request + before the action. It appends to any existing values associated + with the header name. + + + Input: + GET /foo HTTP/1.1 + my-header: foo + + + Config: + add: + - name: "my-header" + value: "bar,baz" + + + Output: + GET /foo HTTP/1.1 + my-header: foo,bar,baz + items: + description: HTTPHeader represents an HTTP + Header name and value as defined by RFC + 7230. + properties: + name: + description: |- + Name is the name of the HTTP Header to be matched. Name matching MUST be + case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + + + If multiple entries specify equivalent header names, the first entry with + an equivalent name MUST be considered for a match. Subsequent entries + with an equivalent header name MUST be ignored. Due to the + case-insensitivity of header names, "foo" and "Foo" are considered + equivalent. + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP + Header to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + remove: + description: |- + Remove the given header(s) from the HTTP request before the action. The + value of Remove is a list of HTTP header names. Note that the header + names are case-insensitive (see + https://datatracker.ietf.org/doc/html/rfc2616#section-4.2). + + + Input: + GET /foo HTTP/1.1 + my-header1: foo + my-header2: bar + my-header3: baz + + + Config: + remove: ["my-header1", "my-header3"] + + + Output: + GET /foo HTTP/1.1 + my-header2: bar + items: + type: string + maxItems: 16 + type: array + x-kubernetes-list-type: set + set: + description: |- + Set overwrites the request with the given header (name, value) + before the action. + + + Input: + GET /foo HTTP/1.1 + my-header: foo + + + Config: + set: + - name: "my-header" + value: "bar" + + + Output: + GET /foo HTTP/1.1 + my-header: bar + items: + description: HTTPHeader represents an HTTP + Header name and value as defined by RFC + 7230. + properties: + name: + description: |- + Name is the name of the HTTP Header to be matched. Name matching MUST be + case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + + + If multiple entries specify equivalent header names, the first entry with + an equivalent name MUST be considered for a match. Subsequent entries + with an equivalent header name MUST be ignored. Due to the + case-insensitivity of header names, "foo" and "Foo" are considered + equivalent. + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP + Header to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + type: object + type: + description: |- + Type identifies the type of filter to apply. As with other API fields, + types are classified into three conformance levels: + + + - Core: Filter types and their corresponding configuration defined by + "Support: Core" in this package, e.g. "RequestHeaderModifier". All + implementations must support core filters. + + + - Extended: Filter types and their corresponding configuration defined by + "Support: Extended" in this package, e.g. "RequestMirror". Implementers + are encouraged to support extended filters. + + + - Implementation-specific: Filters that are defined and supported by + specific vendors. + In the future, filters showing convergence in behavior across multiple + implementations will be considered for inclusion in extended or core + conformance levels. Filter-specific configuration for such filters + is specified using the ExtensionRef field. `Type` should be set to + "ExtensionRef" for custom filters. + + + Implementers are encouraged to define custom implementation types to + extend the core API with implementation-specific behavior. + + + If a reference to a custom filter type cannot be resolved, the filter + MUST NOT be skipped. Instead, requests that would have been processed by + that filter MUST receive a HTTP error response. + + + Note that values may be added to this enum, implementations + must ensure that unknown values will not cause a crash. + + + Unknown values here must result in the implementation setting the + Accepted Condition for the Route to `status: False`, with a + Reason of `UnsupportedValue`. + enum: + - RequestHeaderModifier + - ResponseHeaderModifier + - RequestMirror + - RequestRedirect + - URLRewrite + - ExtensionRef + type: string + urlRewrite: + description: |- + URLRewrite defines a schema for a filter that modifies a request during forwarding. + + + Support: Extended + properties: + hostname: + description: |- + Hostname is the value to be used to replace the Host header value during + forwarding. + + + Support: Extended + maxLength: 253 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + path: + description: |- + Path defines a path rewrite. + + + Support: Extended + properties: + replaceFullPath: + description: |- + ReplaceFullPath specifies the value with which to replace the full path + of a request during a rewrite or redirect. + maxLength: 1024 + type: string + replacePrefixMatch: + description: |- + ReplacePrefixMatch specifies the value with which to replace the prefix + match of a request during a rewrite or redirect. For example, a request + to "/foo/bar" with a prefix match of "/foo" and a ReplacePrefixMatch + of "/xyz" would be modified to "/xyz/bar". + + + Note that this matches the behavior of the PathPrefix match type. This + matches full path elements. A path element refers to the list of labels + in the path split by the `/` separator. When specified, a trailing `/` is + ignored. For example, the paths `/abc`, `/abc/`, and `/abc/def` would all + match the prefix `/abc`, but the path `/abcd` would not. + + + ReplacePrefixMatch is only compatible with a `PathPrefix` HTTPRouteMatch. + Using any other HTTPRouteMatch type on the same HTTPRouteRule will result in + the implementation setting the Accepted Condition for the Route to `status: False`. + + + Request Path | Prefix Match | Replace Prefix | Modified Path + -------------|--------------|----------------|---------- + /foo/bar | /foo | /xyz | /xyz/bar + /foo/bar | /foo | /xyz/ | /xyz/bar + /foo/bar | /foo/ | /xyz | /xyz/bar + /foo/bar | /foo/ | /xyz/ | /xyz/bar + /foo | /foo | /xyz | /xyz + /foo/ | /foo | /xyz | /xyz/ + /foo/bar | /foo | | /bar + /foo/ | /foo | | / + /foo | /foo | | / + /foo/ | /foo | / | / + /foo | /foo | / | / + maxLength: 1024 + type: string + type: + description: |- + Type defines the type of path modifier. Additional types may be + added in a future release of the API. + + + Note that values may be added to this enum, implementations + must ensure that unknown values will not cause a crash. + + + Unknown values here must result in the implementation setting the + Accepted Condition for the Route to `status: False`, with a + Reason of `UnsupportedValue`. + enum: + - ReplaceFullPath + - ReplacePrefixMatch + type: string + required: + - type + type: object + x-kubernetes-validations: + - message: replaceFullPath must be specified + when type is set to 'ReplaceFullPath' + rule: 'self.type == ''ReplaceFullPath'' ? + has(self.replaceFullPath) : true' + - message: type must be 'ReplaceFullPath' when + replaceFullPath is set + rule: 'has(self.replaceFullPath) ? self.type + == ''ReplaceFullPath'' : true' + - message: replacePrefixMatch must be specified + when type is set to 'ReplacePrefixMatch' + rule: 'self.type == ''ReplacePrefixMatch'' + ? has(self.replacePrefixMatch) : true' + - message: type must be 'ReplacePrefixMatch' + when replacePrefixMatch is set + rule: 'has(self.replacePrefixMatch) ? self.type + == ''ReplacePrefixMatch'' : true' + type: object + required: + - type + type: object + x-kubernetes-validations: + - message: filter.requestHeaderModifier must be nil + if the filter.type is not RequestHeaderModifier + rule: '!(has(self.requestHeaderModifier) && self.type + != ''RequestHeaderModifier'')' + - message: filter.requestHeaderModifier must be specified + for RequestHeaderModifier filter.type + rule: '!(!has(self.requestHeaderModifier) && self.type + == ''RequestHeaderModifier'')' + - message: filter.responseHeaderModifier must be nil + if the filter.type is not ResponseHeaderModifier + rule: '!(has(self.responseHeaderModifier) && self.type + != ''ResponseHeaderModifier'')' + - message: filter.responseHeaderModifier must be specified + for ResponseHeaderModifier filter.type + rule: '!(!has(self.responseHeaderModifier) && self.type + == ''ResponseHeaderModifier'')' + - message: filter.requestMirror must be nil if the filter.type + is not RequestMirror + rule: '!(has(self.requestMirror) && self.type != ''RequestMirror'')' + - message: filter.requestMirror must be specified for + RequestMirror filter.type + rule: '!(!has(self.requestMirror) && self.type == + ''RequestMirror'')' + - message: filter.requestRedirect must be nil if the + filter.type is not RequestRedirect + rule: '!(has(self.requestRedirect) && self.type != + ''RequestRedirect'')' + - message: filter.requestRedirect must be specified + for RequestRedirect filter.type + rule: '!(!has(self.requestRedirect) && self.type == + ''RequestRedirect'')' + - message: filter.urlRewrite must be nil if the filter.type + is not URLRewrite + rule: '!(has(self.urlRewrite) && self.type != ''URLRewrite'')' + - message: filter.urlRewrite must be specified for URLRewrite + filter.type + rule: '!(!has(self.urlRewrite) && self.type == ''URLRewrite'')' + - message: filter.extensionRef must be nil if the filter.type + is not ExtensionRef + rule: '!(has(self.extensionRef) && self.type != ''ExtensionRef'')' + - message: filter.extensionRef must be specified for + ExtensionRef filter.type + rule: '!(!has(self.extensionRef) && self.type == ''ExtensionRef'')' + maxItems: 16 + type: array + x-kubernetes-validations: + - message: May specify either httpRouteFilterRequestRedirect + or httpRouteFilterRequestRewrite, but not both + rule: '!(self.exists(f, f.type == ''RequestRedirect'') + && self.exists(f, f.type == ''URLRewrite''))' + - message: May specify either httpRouteFilterRequestRedirect + or httpRouteFilterRequestRewrite, but not both + rule: '!(self.exists(f, f.type == ''RequestRedirect'') + && self.exists(f, f.type == ''URLRewrite''))' + - message: RequestHeaderModifier filter cannot be repeated + rule: self.filter(f, f.type == 'RequestHeaderModifier').size() + <= 1 + - message: ResponseHeaderModifier filter cannot be repeated + rule: self.filter(f, f.type == 'ResponseHeaderModifier').size() + <= 1 + - message: RequestRedirect filter cannot be repeated + rule: self.filter(f, f.type == 'RequestRedirect').size() + <= 1 + - message: URLRewrite filter cannot be repeated + rule: self.filter(f, f.type == 'URLRewrite').size() + <= 1 + group: + default: "" + description: |- + Group is the group of the referent. For example, "gateway.networking.k8s.io". + When unspecified or empty string, core API group is inferred. + maxLength: 253 + pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + kind: + default: Service + description: |- + Kind is the Kubernetes resource kind of the referent. For example + "Service". + + + Defaults to "Service" when not specified. + + + ExternalName services can refer to CNAME DNS records that may live + outside of the cluster and as such are difficult to reason about in + terms of conformance. They also may not be safe to forward to (see + CVE-2021-25740 for more information). Implementations SHOULD NOT + support ExternalName Services. + + + Support: Core (Services with a type other than ExternalName) + + + Support: Implementation-specific (Services with type ExternalName) + maxLength: 63 + minLength: 1 + pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ + type: string + name: + description: Name is the name of the referent. + maxLength: 253 + minLength: 1 + type: string + namespace: + description: |- + Namespace is the namespace of the backend. When unspecified, the local + namespace is inferred. + + + Note that when a namespace different than the local namespace is specified, + a ReferenceGrant object is required in the referent namespace to allow that + namespace's owner to accept the reference. See the ReferenceGrant + documentation for details. + + + Support: Core + maxLength: 63 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ + type: string + port: + description: |- + Port specifies the destination port number to use for this resource. + Port is required when the referent is a Kubernetes Service. In this + case, the port number is the service port number, not the target port. + For other resources, destination port might be derived from the referent + resource or this field. + format: int32 + maximum: 65535 + minimum: 1 + type: integer + weight: + default: 1 + description: |- + Weight specifies the proportion of requests forwarded to the referenced + backend. This is computed as weight/(sum of all weights in this + BackendRefs list). For non-zero values, there may be some epsilon from + the exact proportion defined here depending on the precision an + implementation supports. Weight is not a percentage and the sum of + weights does not need to equal 100. + + + If only one backend is specified and it has a weight greater than 0, 100% + of the traffic is forwarded to that backend. If weight is set to 0, no + traffic should be forwarded for this entry. If unspecified, weight + defaults to 1. + + + Support for this field varies based on the context where used. + format: int32 + maximum: 1000000 + minimum: 0 + type: integer + required: + - name + type: object + x-kubernetes-validations: + - message: Must have port for Service reference + rule: '(size(self.group) == 0 && self.kind == ''Service'') + ? has(self.port) : true' + maxItems: 16 + type: array + filters: + description: |- + Filters define the filters that are applied to requests that match + this rule. + + + Wherever possible, implementations SHOULD implement filters in the order + they are specified. + + + Implementations MAY choose to implement this ordering strictly, rejecting + any combination or order of filters that can not be supported. If implementations + choose a strict interpretation of filter ordering, they MUST clearly document + that behavior. + + + To reject an invalid combination or order of filters, implementations SHOULD + consider the Route Rules with this configuration invalid. If all Route Rules + in a Route are invalid, the entire Route would be considered invalid. If only + a portion of Route Rules are invalid, implementations MUST set the + "PartiallyInvalid" condition for the Route. + + + Conformance-levels at this level are defined based on the type of filter: + + + - ALL core filters MUST be supported by all implementations. + - Implementers are encouraged to support extended filters. + - Implementation-specific custom filters have no API guarantees across + implementations. + + + Specifying the same filter multiple times is not supported unless explicitly + indicated in the filter. + + + All filters are expected to be compatible with each other except for the + URLRewrite and RequestRedirect filters, which may not be combined. If an + implementation can not support other combinations of filters, they must clearly + document that limitation. In cases where incompatible or unsupported + filters are specified and cause the `Accepted` condition to be set to status + `False`, implementations may use the `IncompatibleFilters` reason to specify + this configuration error. + + + Support: Core + items: + description: |- + HTTPRouteFilter defines processing steps that must be completed during the + request or response lifecycle. HTTPRouteFilters are meant as an extension + point to express processing that may be done in Gateway implementations. Some + examples include request or response modification, implementing + authentication strategies, rate-limiting, and traffic shaping. API + guarantee/conformance is defined based on the type of the filter. + properties: + extensionRef: + description: |- + ExtensionRef is an optional, implementation-specific extension to the + "filter" behavior. For example, resource "myroutefilter" in group + "networking.example.net"). ExtensionRef MUST NOT be used for core and + extended filters. + + + This filter can be used multiple times within the same rule. + + + Support: Implementation-specific + properties: + group: + description: |- + Group is the group of the referent. For example, "gateway.networking.k8s.io". + When unspecified or empty string, core API group is inferred. + maxLength: 253 + pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + kind: + description: Kind is kind of the referent. For example + "HTTPRoute" or "Service". + maxLength: 63 + minLength: 1 + pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ + type: string + name: + description: Name is the name of the referent. + maxLength: 253 + minLength: 1 + type: string + required: + - group + - kind + - name + type: object + requestHeaderModifier: + description: |- + RequestHeaderModifier defines a schema for a filter that modifies request + headers. + + + Support: Core + properties: + add: + description: |- + Add adds the given header(s) (name, value) to the request + before the action. It appends to any existing values associated + with the header name. + + + Input: + GET /foo HTTP/1.1 + my-header: foo + + + Config: + add: + - name: "my-header" + value: "bar,baz" + + + Output: + GET /foo HTTP/1.1 + my-header: foo,bar,baz + items: + description: HTTPHeader represents an HTTP Header + name and value as defined by RFC 7230. + properties: + name: + description: |- + Name is the name of the HTTP Header to be matched. Name matching MUST be + case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + + + If multiple entries specify equivalent header names, the first entry with + an equivalent name MUST be considered for a match. Subsequent entries + with an equivalent header name MUST be ignored. Due to the + case-insensitivity of header names, "foo" and "Foo" are considered + equivalent. + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP Header + to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + remove: + description: |- + Remove the given header(s) from the HTTP request before the action. The + value of Remove is a list of HTTP header names. Note that the header + names are case-insensitive (see + https://datatracker.ietf.org/doc/html/rfc2616#section-4.2). + + + Input: + GET /foo HTTP/1.1 + my-header1: foo + my-header2: bar + my-header3: baz + + + Config: + remove: ["my-header1", "my-header3"] + + + Output: + GET /foo HTTP/1.1 + my-header2: bar + items: + type: string + maxItems: 16 + type: array + x-kubernetes-list-type: set + set: + description: |- + Set overwrites the request with the given header (name, value) + before the action. + + + Input: + GET /foo HTTP/1.1 + my-header: foo + + + Config: + set: + - name: "my-header" + value: "bar" + + + Output: + GET /foo HTTP/1.1 + my-header: bar + items: + description: HTTPHeader represents an HTTP Header + name and value as defined by RFC 7230. + properties: + name: + description: |- + Name is the name of the HTTP Header to be matched. Name matching MUST be + case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + + + If multiple entries specify equivalent header names, the first entry with + an equivalent name MUST be considered for a match. Subsequent entries + with an equivalent header name MUST be ignored. Due to the + case-insensitivity of header names, "foo" and "Foo" are considered + equivalent. + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP Header + to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + type: object + requestMirror: + description: |- + RequestMirror defines a schema for a filter that mirrors requests. + Requests are sent to the specified destination, but responses from + that destination are ignored. + + + This filter can be used multiple times within the same rule. Note that + not all implementations will be able to support mirroring to multiple + backends. + + + Support: Extended + properties: + backendRef: + description: |- + BackendRef references a resource where mirrored requests are sent. + + + Mirrored requests must be sent only to a single destination endpoint + within this BackendRef, irrespective of how many endpoints are present + within this BackendRef. + + + If the referent cannot be found, this BackendRef is invalid and must be + dropped from the Gateway. The controller must ensure the "ResolvedRefs" + condition on the Route status is set to `status: False` and not configure + this backend in the underlying implementation. + + + If there is a cross-namespace reference to an *existing* object + that is not allowed by a ReferenceGrant, the controller must ensure the + "ResolvedRefs" condition on the Route is set to `status: False`, + with the "RefNotPermitted" reason and not configure this backend in the + underlying implementation. + + + In either error case, the Message of the `ResolvedRefs` Condition + should be used to provide more detail about the problem. + + + Support: Extended for Kubernetes Service + + + Support: Implementation-specific for any other resource + properties: + group: + default: "" + description: |- + Group is the group of the referent. For example, "gateway.networking.k8s.io". + When unspecified or empty string, core API group is inferred. + maxLength: 253 + pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + kind: + default: Service + description: |- + Kind is the Kubernetes resource kind of the referent. For example + "Service". + + + Defaults to "Service" when not specified. + + + ExternalName services can refer to CNAME DNS records that may live + outside of the cluster and as such are difficult to reason about in + terms of conformance. They also may not be safe to forward to (see + CVE-2021-25740 for more information). Implementations SHOULD NOT + support ExternalName Services. + + + Support: Core (Services with a type other than ExternalName) + + + Support: Implementation-specific (Services with type ExternalName) + maxLength: 63 + minLength: 1 + pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ + type: string + name: + description: Name is the name of the referent. + maxLength: 253 + minLength: 1 + type: string + namespace: + description: |- + Namespace is the namespace of the backend. When unspecified, the local + namespace is inferred. + + + Note that when a namespace different than the local namespace is specified, + a ReferenceGrant object is required in the referent namespace to allow that + namespace's owner to accept the reference. See the ReferenceGrant + documentation for details. + + + Support: Core + maxLength: 63 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ + type: string + port: + description: |- + Port specifies the destination port number to use for this resource. + Port is required when the referent is a Kubernetes Service. In this + case, the port number is the service port number, not the target port. + For other resources, destination port might be derived from the referent + resource or this field. + format: int32 + maximum: 65535 + minimum: 1 + type: integer + required: + - name + type: object + x-kubernetes-validations: + - message: Must have port for Service reference + rule: '(size(self.group) == 0 && self.kind == ''Service'') + ? has(self.port) : true' + required: + - backendRef + type: object + requestRedirect: + description: |- + RequestRedirect defines a schema for a filter that responds to the + request with an HTTP redirection. + + + Support: Core + properties: + hostname: + description: |- + Hostname is the hostname to be used in the value of the `Location` + header in the response. + When empty, the hostname in the `Host` header of the request is used. + + + Support: Core + maxLength: 253 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + path: + description: |- + Path defines parameters used to modify the path of the incoming request. + The modified path is then used to construct the `Location` header. When + empty, the request path is used as-is. + + + Support: Extended + properties: + replaceFullPath: + description: |- + ReplaceFullPath specifies the value with which to replace the full path + of a request during a rewrite or redirect. + maxLength: 1024 + type: string + replacePrefixMatch: + description: |- + ReplacePrefixMatch specifies the value with which to replace the prefix + match of a request during a rewrite or redirect. For example, a request + to "/foo/bar" with a prefix match of "/foo" and a ReplacePrefixMatch + of "/xyz" would be modified to "/xyz/bar". + + + Note that this matches the behavior of the PathPrefix match type. This + matches full path elements. A path element refers to the list of labels + in the path split by the `/` separator. When specified, a trailing `/` is + ignored. For example, the paths `/abc`, `/abc/`, and `/abc/def` would all + match the prefix `/abc`, but the path `/abcd` would not. + + + ReplacePrefixMatch is only compatible with a `PathPrefix` HTTPRouteMatch. + Using any other HTTPRouteMatch type on the same HTTPRouteRule will result in + the implementation setting the Accepted Condition for the Route to `status: False`. + + + Request Path | Prefix Match | Replace Prefix | Modified Path + -------------|--------------|----------------|---------- + /foo/bar | /foo | /xyz | /xyz/bar + /foo/bar | /foo | /xyz/ | /xyz/bar + /foo/bar | /foo/ | /xyz | /xyz/bar + /foo/bar | /foo/ | /xyz/ | /xyz/bar + /foo | /foo | /xyz | /xyz + /foo/ | /foo | /xyz | /xyz/ + /foo/bar | /foo | | /bar + /foo/ | /foo | | / + /foo | /foo | | / + /foo/ | /foo | / | / + /foo | /foo | / | / + maxLength: 1024 + type: string + type: + description: |- + Type defines the type of path modifier. Additional types may be + added in a future release of the API. + + + Note that values may be added to this enum, implementations + must ensure that unknown values will not cause a crash. + + + Unknown values here must result in the implementation setting the + Accepted Condition for the Route to `status: False`, with a + Reason of `UnsupportedValue`. + enum: + - ReplaceFullPath + - ReplacePrefixMatch + type: string + required: + - type + type: object + x-kubernetes-validations: + - message: replaceFullPath must be specified when + type is set to 'ReplaceFullPath' + rule: 'self.type == ''ReplaceFullPath'' ? has(self.replaceFullPath) + : true' + - message: type must be 'ReplaceFullPath' when replaceFullPath + is set + rule: 'has(self.replaceFullPath) ? self.type == + ''ReplaceFullPath'' : true' + - message: replacePrefixMatch must be specified when + type is set to 'ReplacePrefixMatch' + rule: 'self.type == ''ReplacePrefixMatch'' ? has(self.replacePrefixMatch) + : true' + - message: type must be 'ReplacePrefixMatch' when + replacePrefixMatch is set + rule: 'has(self.replacePrefixMatch) ? self.type + == ''ReplacePrefixMatch'' : true' + port: + description: |- + Port is the port to be used in the value of the `Location` + header in the response. + + + If no port is specified, the redirect port MUST be derived using the + following rules: + + + * If redirect scheme is not-empty, the redirect port MUST be the well-known + port associated with the redirect scheme. Specifically "http" to port 80 + and "https" to port 443. If the redirect scheme does not have a + well-known port, the listener port of the Gateway SHOULD be used. + * If redirect scheme is empty, the redirect port MUST be the Gateway + Listener port. + + + Implementations SHOULD NOT add the port number in the 'Location' + header in the following cases: + + + * A Location header that will use HTTP (whether that is determined via + the Listener protocol or the Scheme field) _and_ use port 80. + * A Location header that will use HTTPS (whether that is determined via + the Listener protocol or the Scheme field) _and_ use port 443. + + + Support: Extended + format: int32 + maximum: 65535 + minimum: 1 + type: integer + scheme: + description: |- + Scheme is the scheme to be used in the value of the `Location` header in + the response. When empty, the scheme of the request is used. + + + Scheme redirects can affect the port of the redirect, for more information, + refer to the documentation for the port field of this filter. + + + Note that values may be added to this enum, implementations + must ensure that unknown values will not cause a crash. + + + Unknown values here must result in the implementation setting the + Accepted Condition for the Route to `status: False`, with a + Reason of `UnsupportedValue`. + + + Support: Extended + enum: + - http + - https + type: string + statusCode: + default: 302 + description: |- + StatusCode is the HTTP status code to be used in response. + + + Note that values may be added to this enum, implementations + must ensure that unknown values will not cause a crash. + + + Unknown values here must result in the implementation setting the + Accepted Condition for the Route to `status: False`, with a + Reason of `UnsupportedValue`. + + + Support: Core + enum: + - 301 + - 302 + type: integer + type: object + responseHeaderModifier: + description: |- + ResponseHeaderModifier defines a schema for a filter that modifies response + headers. + + + Support: Extended + properties: + add: + description: |- + Add adds the given header(s) (name, value) to the request + before the action. It appends to any existing values associated + with the header name. + + + Input: + GET /foo HTTP/1.1 + my-header: foo + + + Config: + add: + - name: "my-header" + value: "bar,baz" + + + Output: + GET /foo HTTP/1.1 + my-header: foo,bar,baz + items: + description: HTTPHeader represents an HTTP Header + name and value as defined by RFC 7230. + properties: + name: + description: |- + Name is the name of the HTTP Header to be matched. Name matching MUST be + case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + + + If multiple entries specify equivalent header names, the first entry with + an equivalent name MUST be considered for a match. Subsequent entries + with an equivalent header name MUST be ignored. Due to the + case-insensitivity of header names, "foo" and "Foo" are considered + equivalent. + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP Header + to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + remove: + description: |- + Remove the given header(s) from the HTTP request before the action. The + value of Remove is a list of HTTP header names. Note that the header + names are case-insensitive (see + https://datatracker.ietf.org/doc/html/rfc2616#section-4.2). + + + Input: + GET /foo HTTP/1.1 + my-header1: foo + my-header2: bar + my-header3: baz + + + Config: + remove: ["my-header1", "my-header3"] + + + Output: + GET /foo HTTP/1.1 + my-header2: bar + items: + type: string + maxItems: 16 + type: array + x-kubernetes-list-type: set + set: + description: |- + Set overwrites the request with the given header (name, value) + before the action. + + + Input: + GET /foo HTTP/1.1 + my-header: foo + + + Config: + set: + - name: "my-header" + value: "bar" + + + Output: + GET /foo HTTP/1.1 + my-header: bar + items: + description: HTTPHeader represents an HTTP Header + name and value as defined by RFC 7230. + properties: + name: + description: |- + Name is the name of the HTTP Header to be matched. Name matching MUST be + case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + + + If multiple entries specify equivalent header names, the first entry with + an equivalent name MUST be considered for a match. Subsequent entries + with an equivalent header name MUST be ignored. Due to the + case-insensitivity of header names, "foo" and "Foo" are considered + equivalent. + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP Header + to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + type: object + type: + description: |- + Type identifies the type of filter to apply. As with other API fields, + types are classified into three conformance levels: + + + - Core: Filter types and their corresponding configuration defined by + "Support: Core" in this package, e.g. "RequestHeaderModifier". All + implementations must support core filters. + + + - Extended: Filter types and their corresponding configuration defined by + "Support: Extended" in this package, e.g. "RequestMirror". Implementers + are encouraged to support extended filters. + + + - Implementation-specific: Filters that are defined and supported by + specific vendors. + In the future, filters showing convergence in behavior across multiple + implementations will be considered for inclusion in extended or core + conformance levels. Filter-specific configuration for such filters + is specified using the ExtensionRef field. `Type` should be set to + "ExtensionRef" for custom filters. + + + Implementers are encouraged to define custom implementation types to + extend the core API with implementation-specific behavior. + + + If a reference to a custom filter type cannot be resolved, the filter + MUST NOT be skipped. Instead, requests that would have been processed by + that filter MUST receive a HTTP error response. + + + Note that values may be added to this enum, implementations + must ensure that unknown values will not cause a crash. + + + Unknown values here must result in the implementation setting the + Accepted Condition for the Route to `status: False`, with a + Reason of `UnsupportedValue`. + enum: + - RequestHeaderModifier + - ResponseHeaderModifier + - RequestMirror + - RequestRedirect + - URLRewrite + - ExtensionRef + type: string + urlRewrite: + description: |- + URLRewrite defines a schema for a filter that modifies a request during forwarding. + + + Support: Extended + properties: + hostname: + description: |- + Hostname is the value to be used to replace the Host header value during + forwarding. + + + Support: Extended + maxLength: 253 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + path: + description: |- + Path defines a path rewrite. + + + Support: Extended + properties: + replaceFullPath: + description: |- + ReplaceFullPath specifies the value with which to replace the full path + of a request during a rewrite or redirect. + maxLength: 1024 + type: string + replacePrefixMatch: + description: |- + ReplacePrefixMatch specifies the value with which to replace the prefix + match of a request during a rewrite or redirect. For example, a request + to "/foo/bar" with a prefix match of "/foo" and a ReplacePrefixMatch + of "/xyz" would be modified to "/xyz/bar". + + + Note that this matches the behavior of the PathPrefix match type. This + matches full path elements. A path element refers to the list of labels + in the path split by the `/` separator. When specified, a trailing `/` is + ignored. For example, the paths `/abc`, `/abc/`, and `/abc/def` would all + match the prefix `/abc`, but the path `/abcd` would not. + + + ReplacePrefixMatch is only compatible with a `PathPrefix` HTTPRouteMatch. + Using any other HTTPRouteMatch type on the same HTTPRouteRule will result in + the implementation setting the Accepted Condition for the Route to `status: False`. + + + Request Path | Prefix Match | Replace Prefix | Modified Path + -------------|--------------|----------------|---------- + /foo/bar | /foo | /xyz | /xyz/bar + /foo/bar | /foo | /xyz/ | /xyz/bar + /foo/bar | /foo/ | /xyz | /xyz/bar + /foo/bar | /foo/ | /xyz/ | /xyz/bar + /foo | /foo | /xyz | /xyz + /foo/ | /foo | /xyz | /xyz/ + /foo/bar | /foo | | /bar + /foo/ | /foo | | / + /foo | /foo | | / + /foo/ | /foo | / | / + /foo | /foo | / | / + maxLength: 1024 + type: string + type: + description: |- + Type defines the type of path modifier. Additional types may be + added in a future release of the API. + + + Note that values may be added to this enum, implementations + must ensure that unknown values will not cause a crash. + + + Unknown values here must result in the implementation setting the + Accepted Condition for the Route to `status: False`, with a + Reason of `UnsupportedValue`. + enum: + - ReplaceFullPath + - ReplacePrefixMatch + type: string + required: + - type + type: object + x-kubernetes-validations: + - message: replaceFullPath must be specified when + type is set to 'ReplaceFullPath' + rule: 'self.type == ''ReplaceFullPath'' ? has(self.replaceFullPath) + : true' + - message: type must be 'ReplaceFullPath' when replaceFullPath + is set + rule: 'has(self.replaceFullPath) ? self.type == + ''ReplaceFullPath'' : true' + - message: replacePrefixMatch must be specified when + type is set to 'ReplacePrefixMatch' + rule: 'self.type == ''ReplacePrefixMatch'' ? has(self.replacePrefixMatch) + : true' + - message: type must be 'ReplacePrefixMatch' when + replacePrefixMatch is set + rule: 'has(self.replacePrefixMatch) ? self.type + == ''ReplacePrefixMatch'' : true' + type: object + required: + - type + type: object + x-kubernetes-validations: + - message: filter.requestHeaderModifier must be nil if the + filter.type is not RequestHeaderModifier + rule: '!(has(self.requestHeaderModifier) && self.type != + ''RequestHeaderModifier'')' + - message: filter.requestHeaderModifier must be specified + for RequestHeaderModifier filter.type + rule: '!(!has(self.requestHeaderModifier) && self.type == + ''RequestHeaderModifier'')' + - message: filter.responseHeaderModifier must be nil if the + filter.type is not ResponseHeaderModifier + rule: '!(has(self.responseHeaderModifier) && self.type != + ''ResponseHeaderModifier'')' + - message: filter.responseHeaderModifier must be specified + for ResponseHeaderModifier filter.type + rule: '!(!has(self.responseHeaderModifier) && self.type + == ''ResponseHeaderModifier'')' + - message: filter.requestMirror must be nil if the filter.type + is not RequestMirror + rule: '!(has(self.requestMirror) && self.type != ''RequestMirror'')' + - message: filter.requestMirror must be specified for RequestMirror + filter.type + rule: '!(!has(self.requestMirror) && self.type == ''RequestMirror'')' + - message: filter.requestRedirect must be nil if the filter.type + is not RequestRedirect + rule: '!(has(self.requestRedirect) && self.type != ''RequestRedirect'')' + - message: filter.requestRedirect must be specified for RequestRedirect + filter.type + rule: '!(!has(self.requestRedirect) && self.type == ''RequestRedirect'')' + - message: filter.urlRewrite must be nil if the filter.type + is not URLRewrite + rule: '!(has(self.urlRewrite) && self.type != ''URLRewrite'')' + - message: filter.urlRewrite must be specified for URLRewrite + filter.type + rule: '!(!has(self.urlRewrite) && self.type == ''URLRewrite'')' + - message: filter.extensionRef must be nil if the filter.type + is not ExtensionRef + rule: '!(has(self.extensionRef) && self.type != ''ExtensionRef'')' + - message: filter.extensionRef must be specified for ExtensionRef + filter.type + rule: '!(!has(self.extensionRef) && self.type == ''ExtensionRef'')' + maxItems: 16 + type: array + x-kubernetes-validations: + - message: May specify either httpRouteFilterRequestRedirect + or httpRouteFilterRequestRewrite, but not both + rule: '!(self.exists(f, f.type == ''RequestRedirect'') && + self.exists(f, f.type == ''URLRewrite''))' + - message: RequestHeaderModifier filter cannot be repeated + rule: self.filter(f, f.type == 'RequestHeaderModifier').size() + <= 1 + - message: ResponseHeaderModifier filter cannot be repeated + rule: self.filter(f, f.type == 'ResponseHeaderModifier').size() + <= 1 + - message: RequestRedirect filter cannot be repeated + rule: self.filter(f, f.type == 'RequestRedirect').size() <= + 1 + - message: URLRewrite filter cannot be repeated + rule: self.filter(f, f.type == 'URLRewrite').size() <= 1 + matches: + default: + - path: + type: PathPrefix + value: / + description: |- + Matches define conditions used for matching the rule against incoming + HTTP requests. Each match is independent, i.e. this rule will be matched + if **any** one of the matches is satisfied. + + + For example, take the following matches configuration: + + + ``` + matches: + - path: + value: "/foo" + headers: + - name: "version" + value: "v2" + - path: + value: "/v2/foo" + ``` + + + For a request to match against this rule, a request must satisfy + EITHER of the two conditions: + + + - path prefixed with `/foo` AND contains the header `version: v2` + - path prefix of `/v2/foo` + + + See the documentation for HTTPRouteMatch on how to specify multiple + match conditions that should be ANDed together. + + + If no matches are specified, the default is a prefix + path match on "/", which has the effect of matching every + HTTP request. + + + Proxy or Load Balancer routing configuration generated from HTTPRoutes + MUST prioritize matches based on the following criteria, continuing on + ties. Across all rules specified on applicable Routes, precedence must be + given to the match having: + + + * "Exact" path match. + * "Prefix" path match with largest number of characters. + * Method match. + * Largest number of header matches. + * Largest number of query param matches. + + + Note: The precedence of RegularExpression path matches are implementation-specific. + + + If ties still exist across multiple Routes, matching precedence MUST be + determined in order of the following criteria, continuing on ties: + + + * The oldest Route based on creation timestamp. + * The Route appearing first in alphabetical order by + "{namespace}/{name}". + + + If ties still exist within an HTTPRoute, matching precedence MUST be granted + to the FIRST matching rule (in list order) with a match meeting the above + criteria. + + + When no rules matching a request have been successfully attached to the + parent a request is coming from, a HTTP 404 status code MUST be returned. + items: + description: "HTTPRouteMatch defines the predicate used to + match requests to a given\naction. Multiple match types + are ANDed together, i.e. the match will\nevaluate to true + only if all conditions are satisfied.\n\n\nFor example, + the match below will match a HTTP request only if its path\nstarts + with `/foo` AND it contains the `version: v1` header:\n\n\n```\nmatch:\n\n\n\tpath:\n\t + \ value: \"/foo\"\n\theaders:\n\t- name: \"version\"\n\t + \ value \"v1\"\n\n\n```" + properties: + headers: + description: |- + Headers specifies HTTP request header matchers. Multiple match values are + ANDed together, meaning, a request must match all the specified headers + to select the route. + items: + description: |- + HTTPHeaderMatch describes how to select a HTTP route by matching HTTP request + headers. + properties: + name: + description: |- + Name is the name of the HTTP Header to be matched. Name matching MUST be + case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + + + If multiple entries specify equivalent header names, only the first + entry with an equivalent name MUST be considered for a match. Subsequent + entries with an equivalent header name MUST be ignored. Due to the + case-insensitivity of header names, "foo" and "Foo" are considered + equivalent. + + + When a header is repeated in an HTTP request, it is + implementation-specific behavior as to how this is represented. + Generally, proxies should follow the guidance from the RFC: + https://www.rfc-editor.org/rfc/rfc7230.html#section-3.2.2 regarding + processing a repeated header, with special handling for "Set-Cookie". + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + type: + default: Exact + description: |- + Type specifies how to match against the value of the header. + + + Support: Core (Exact) + + + Support: Implementation-specific (RegularExpression) + + + Since RegularExpression HeaderMatchType has implementation-specific + conformance, implementations can support POSIX, PCRE or any other dialects + of regular expressions. Please read the implementation's documentation to + determine the supported dialect. + enum: + - Exact + - RegularExpression + type: string + value: + description: Value is the value of HTTP Header to + be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + method: + description: |- + Method specifies HTTP method matcher. + When specified, this route will be matched only if the request has the + specified method. + + + Support: Extended + enum: + - GET + - HEAD + - POST + - PUT + - DELETE + - CONNECT + - OPTIONS + - TRACE + - PATCH + type: string + path: + default: + type: PathPrefix + value: / + description: |- + Path specifies a HTTP request path matcher. If this field is not + specified, a default prefix match on the "/" path is provided. + properties: + type: + default: PathPrefix + description: |- + Type specifies how to match against the path Value. + + + Support: Core (Exact, PathPrefix) + + + Support: Implementation-specific (RegularExpression) + enum: + - Exact + - PathPrefix + - RegularExpression + type: string + value: + default: / + description: Value of the HTTP path to match against. + maxLength: 1024 + type: string + type: object + x-kubernetes-validations: + - message: value must be an absolute path and start with + '/' when type one of ['Exact', 'PathPrefix'] + rule: '(self.type in [''Exact'',''PathPrefix'']) ? self.value.startsWith(''/'') + : true' + - message: must not contain '//' when type one of ['Exact', + 'PathPrefix'] + rule: '(self.type in [''Exact'',''PathPrefix'']) ? !self.value.contains(''//'') + : true' + - message: must not contain '/./' when type one of ['Exact', + 'PathPrefix'] + rule: '(self.type in [''Exact'',''PathPrefix'']) ? !self.value.contains(''/./'') + : true' + - message: must not contain '/../' when type one of ['Exact', + 'PathPrefix'] + rule: '(self.type in [''Exact'',''PathPrefix'']) ? !self.value.contains(''/../'') + : true' + - message: must not contain '%2f' when type one of ['Exact', + 'PathPrefix'] + rule: '(self.type in [''Exact'',''PathPrefix'']) ? !self.value.contains(''%2f'') + : true' + - message: must not contain '%2F' when type one of ['Exact', + 'PathPrefix'] + rule: '(self.type in [''Exact'',''PathPrefix'']) ? !self.value.contains(''%2F'') + : true' + - message: must not contain '#' when type one of ['Exact', + 'PathPrefix'] + rule: '(self.type in [''Exact'',''PathPrefix'']) ? !self.value.contains(''#'') + : true' + - message: must not end with '/..' when type one of ['Exact', + 'PathPrefix'] + rule: '(self.type in [''Exact'',''PathPrefix'']) ? !self.value.endsWith(''/..'') + : true' + - message: must not end with '/.' when type one of ['Exact', + 'PathPrefix'] + rule: '(self.type in [''Exact'',''PathPrefix'']) ? !self.value.endsWith(''/.'') + : true' + - message: type must be one of ['Exact', 'PathPrefix', + 'RegularExpression'] + rule: self.type in ['Exact','PathPrefix'] || self.type + == 'RegularExpression' + - message: must only contain valid characters (matching + ^(?:[-A-Za-z0-9/._~!$&'()*+,;=:@]|[%][0-9a-fA-F]{2})+$) + for types ['Exact', 'PathPrefix'] + rule: '(self.type in [''Exact'',''PathPrefix'']) ? self.value.matches(r"""^(?:[-A-Za-z0-9/._~!$&''()*+,;=:@]|[%][0-9a-fA-F]{2})+$""") + : true' + queryParams: + description: |- + QueryParams specifies HTTP query parameter matchers. Multiple match + values are ANDed together, meaning, a request must match all the + specified query parameters to select the route. + + + Support: Extended + items: + description: |- + HTTPQueryParamMatch describes how to select a HTTP route by matching HTTP + query parameters. + properties: + name: + description: |- + Name is the name of the HTTP query param to be matched. This must be an + exact string match. (See + https://tools.ietf.org/html/rfc7230#section-2.7.3). + + + If multiple entries specify equivalent query param names, only the first + entry with an equivalent name MUST be considered for a match. Subsequent + entries with an equivalent query param name MUST be ignored. + + + If a query param is repeated in an HTTP request, the behavior is + purposely left undefined, since different data planes have different + capabilities. However, it is *recommended* that implementations should + match against the first value of the param if the data plane supports it, + as this behavior is expected in other load balancing contexts outside of + the Gateway API. + + + Users SHOULD NOT route traffic based on repeated query params to guard + themselves against potential differences in the implementations. + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + type: + default: Exact + description: |- + Type specifies how to match against the value of the query parameter. + + + Support: Extended (Exact) + + + Support: Implementation-specific (RegularExpression) + + + Since RegularExpression QueryParamMatchType has Implementation-specific + conformance, implementations can support POSIX, PCRE or any other + dialects of regular expressions. Please read the implementation's + documentation to determine the supported dialect. + enum: + - Exact + - RegularExpression + type: string + value: + description: Value is the value of HTTP query param + to be matched. + maxLength: 1024 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + type: object + maxItems: 8 + type: array + type: object + x-kubernetes-validations: + - message: RequestRedirect filter must not be used together with + backendRefs + rule: '(has(self.backendRefs) && size(self.backendRefs) > 0) ? + (!has(self.filters) || self.filters.all(f, !has(f.requestRedirect))): + true' + - message: When using RequestRedirect filter with path.replacePrefixMatch, + exactly one PathPrefix match must be specified + rule: '(has(self.filters) && self.filters.exists_one(f, has(f.requestRedirect) + && has(f.requestRedirect.path) && f.requestRedirect.path.type + == ''ReplacePrefixMatch'' && has(f.requestRedirect.path.replacePrefixMatch))) + ? ((size(self.matches) != 1 || !has(self.matches[0].path) || + self.matches[0].path.type != ''PathPrefix'') ? false : true) + : true' + - message: When using URLRewrite filter with path.replacePrefixMatch, + exactly one PathPrefix match must be specified + rule: '(has(self.filters) && self.filters.exists_one(f, has(f.urlRewrite) + && has(f.urlRewrite.path) && f.urlRewrite.path.type == ''ReplacePrefixMatch'' + && has(f.urlRewrite.path.replacePrefixMatch))) ? ((size(self.matches) + != 1 || !has(self.matches[0].path) || self.matches[0].path.type + != ''PathPrefix'') ? false : true) : true' + - message: Within backendRefs, when using RequestRedirect filter + with path.replacePrefixMatch, exactly one PathPrefix match must + be specified + rule: '(has(self.backendRefs) && self.backendRefs.exists_one(b, + (has(b.filters) && b.filters.exists_one(f, has(f.requestRedirect) + && has(f.requestRedirect.path) && f.requestRedirect.path.type + == ''ReplacePrefixMatch'' && has(f.requestRedirect.path.replacePrefixMatch))) + )) ? ((size(self.matches) != 1 || !has(self.matches[0].path) + || self.matches[0].path.type != ''PathPrefix'') ? false : true) + : true' + - message: Within backendRefs, When using URLRewrite filter with + path.replacePrefixMatch, exactly one PathPrefix match must be + specified + rule: '(has(self.backendRefs) && self.backendRefs.exists_one(b, + (has(b.filters) && b.filters.exists_one(f, has(f.urlRewrite) + && has(f.urlRewrite.path) && f.urlRewrite.path.type == ''ReplacePrefixMatch'' + && has(f.urlRewrite.path.replacePrefixMatch))) )) ? ((size(self.matches) + != 1 || !has(self.matches[0].path) || self.matches[0].path.type + != ''PathPrefix'') ? false : true) : true' + maxItems: 16 + type: array + type: object + status: + description: Status defines the current state of HTTPRoute. + properties: + parents: + description: |- + Parents is a list of parent resources (usually Gateways) that are + associated with the route, and the status of the route with respect to + each parent. When this route attaches to a parent, the controller that + manages the parent must add an entry to this list when the controller + first sees the route and should update the entry as appropriate when the + route or gateway is modified. + + + Note that parent references that cannot be resolved by an implementation + of this API will not be added to this list. Implementations of this API + can only populate Route status for the Gateways/parent resources they are + responsible for. + + + A maximum of 32 Gateways will be represented in this list. An empty list + means the route has not been attached to any Gateway. + items: + description: |- + RouteParentStatus describes the status of a route with respect to an + associated Parent. + properties: + conditions: + description: |- + Conditions describes the status of the route with respect to the Gateway. + Note that the route's availability is also subject to the Gateway's own + status conditions and listener status. + + + If the Route's ParentRef specifies an existing Gateway that supports + Routes of this kind AND that Gateway's controller has sufficient access, + then that Gateway's controller MUST set the "Accepted" condition on the + Route, to indicate whether the route has been accepted or rejected by the + Gateway, and why. + + + A Route MUST be considered "Accepted" if at least one of the Route's + rules is implemented by the Gateway. + + + There are a number of cases where the "Accepted" condition may not be set + due to lack of controller visibility, that includes when: + + + * The Route refers to a non-existent parent. + * The Route is of a type that the controller does not support. + * The Route is in a namespace the controller does not have access to. + 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 + maxItems: 8 + minItems: 1 + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + controllerName: + description: |- + ControllerName is a domain/path string that indicates the name of the + controller that wrote this status. This corresponds with the + controllerName field on GatewayClass. + + + Example: "example.net/gateway-controller". + + + The format of this field is DOMAIN "/" PATH, where DOMAIN and PATH are + valid Kubernetes names + (https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names). + + + Controllers MUST populate this field when writing status. Controllers should ensure that + entries to status populated with their ControllerName are cleaned up when they are no + longer necessary. + maxLength: 253 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*\/[A-Za-z0-9\/\-._~%!$&'()*+,;=:]+$ + type: string + parentRef: + description: |- + ParentRef corresponds with a ParentRef in the spec that this + RouteParentStatus struct describes the status of. + properties: + group: + default: gateway.networking.k8s.io + description: |- + Group is the group of the referent. + When unspecified, "gateway.networking.k8s.io" is inferred. + To set the core API group (such as for a "Service" kind referent), + Group must be explicitly set to "" (empty string). + + + Support: Core + maxLength: 253 + pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + kind: + default: Gateway + description: |- + Kind is kind of the referent. + + + There are two kinds of parent resources with "Core" support: + + + * Gateway (Gateway conformance profile) + * Service (Mesh conformance profile, ClusterIP Services only) + + + Support for other resources is Implementation-Specific. + maxLength: 63 + minLength: 1 + pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ + type: string + name: + description: |- + Name is the name of the referent. + + + Support: Core + maxLength: 253 + minLength: 1 + type: string + namespace: + description: |- + Namespace is the namespace of the referent. When unspecified, this refers + to the local namespace of the Route. + + + Note that there are specific rules for ParentRefs which cross namespace + boundaries. Cross-namespace references are only valid if they are explicitly + allowed by something in the namespace they are referring to. For example: + Gateway has the AllowedRoutes field, and ReferenceGrant provides a + generic way to enable any other kind of cross-namespace reference. + + + + + + Support: Core + maxLength: 63 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ + type: string + port: + description: |- + Port is the network port this Route targets. It can be interpreted + differently based on the type of parent resource. + + + When the parent resource is a Gateway, this targets all listeners + listening on the specified port that also support this kind of Route(and + select this Route). It's not recommended to set `Port` unless the + networking behaviors specified in a Route must apply to a specific port + as opposed to a listener(s) whose port(s) may be changed. When both Port + and SectionName are specified, the name and port of the selected listener + must match both specified values. + + + + + + Implementations MAY choose to support other parent resources. + Implementations supporting other types of parent resources MUST clearly + document how/if Port is interpreted. + + + For the purpose of status, an attachment is considered successful as + long as the parent resource accepts it partially. For example, Gateway + listeners can restrict which Routes can attach to them by Route kind, + namespace, or hostname. If 1 of 2 Gateway listeners accept attachment + from the referencing Route, the Route MUST be considered successfully + attached. If no Gateway listeners accept attachment from this Route, + the Route MUST be considered detached from the Gateway. + + + Support: Extended + format: int32 + maximum: 65535 + minimum: 1 + type: integer + sectionName: + description: |- + SectionName is the name of a section within the target resource. In the + following resources, SectionName is interpreted as the following: + + + * Gateway: Listener name. When both Port (experimental) and SectionName + are specified, the name and port of the selected listener must match + both specified values. + * Service: Port name. When both Port (experimental) and SectionName + are specified, the name and port of the selected listener must match + both specified values. + + + Implementations MAY choose to support attaching Routes to other resources. + If that is the case, they MUST clearly document how SectionName is + interpreted. + + + When unspecified (empty string), this will reference the entire resource. + For the purpose of status, an attachment is considered successful if at + least one section in the parent resource accepts it. For example, Gateway + listeners can restrict which Routes can attach to them by Route kind, + namespace, or hostname. If 1 of 2 Gateway listeners accept attachment from + the referencing Route, the Route MUST be considered successfully + attached. If no Gateway listeners accept attachment from this Route, the + Route MUST be considered detached from the Gateway. + + + Support: Core + maxLength: 253 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + required: + - name + type: object + required: + - controllerName + - parentRef + type: object + maxItems: 32 + type: array + required: + - parents + type: object + required: + - spec + type: object + served: true + storage: false + subresources: + status: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: null + storedVersions: null +--- +# +# config/crd/standard/gateway.networking.k8s.io_referencegrants.yaml +# +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + api-approved.kubernetes.io: https://github.com/kubernetes-sigs/gateway-api/pull/2997 + gateway.networking.k8s.io/bundle-version: v1.1.0 + gateway.networking.k8s.io/channel: standard + creationTimestamp: null + name: referencegrants.gateway.networking.k8s.io +spec: + group: gateway.networking.k8s.io + names: + categories: + - gateway-api + kind: ReferenceGrant + listKind: ReferenceGrantList + plural: referencegrants + shortNames: + - refgrant + singular: referencegrant + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + deprecated: true + deprecationWarning: The v1alpha2 version of ReferenceGrant has been deprecated + and will be removed in a future release of the API. Please upgrade to v1beta1. + name: v1alpha2 + schema: + openAPIV3Schema: + description: |- + ReferenceGrant identifies kinds of resources in other namespaces that are + trusted to reference the specified kinds of resources in the same namespace + as the policy. + + + Each ReferenceGrant can be used to represent a unique trust relationship. + Additional Reference Grants can be used to add to the set of trusted + sources of inbound references for the namespace they are defined within. + + + A ReferenceGrant is required for all cross-namespace references in Gateway API + (with the exception of cross-namespace Route-Gateway attachment, which is + governed by the AllowedRoutes configuration on the Gateway, and cross-namespace + Service ParentRefs on a "consumer" mesh Route, which defines routing rules + applicable only to workloads in the Route namespace). ReferenceGrants allowing + a reference from a Route to a Service are only applicable to BackendRefs. + + + ReferenceGrant is a form of runtime verification allowing users to assert + which cross-namespace object references are permitted. Implementations that + support ReferenceGrant MUST NOT permit cross-namespace references which have + no grant, and MUST respond to the removal of a grant by revoking the access + that the grant allowed. + 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: Spec defines the desired state of ReferenceGrant. + properties: + from: + description: |- + From describes the trusted namespaces and kinds that can reference the + resources described in "To". Each entry in this list MUST be considered + to be an additional place that references can be valid from, or to put + this another way, entries MUST be combined using OR. + + + Support: Core + items: + description: ReferenceGrantFrom describes trusted namespaces and + kinds. + properties: + group: + description: |- + Group is the group of the referent. + When empty, the Kubernetes core API group is inferred. + + + Support: Core + maxLength: 253 + pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + kind: + description: |- + Kind is the kind of the referent. Although implementations may support + additional resources, the following types are part of the "Core" + support level for this field. + + + When used to permit a SecretObjectReference: + + + * Gateway + + + When used to permit a BackendObjectReference: + + + * GRPCRoute + * HTTPRoute + * TCPRoute + * TLSRoute + * UDPRoute + maxLength: 63 + minLength: 1 + pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ + type: string + namespace: + description: |- + Namespace is the namespace of the referent. + + + Support: Core + maxLength: 63 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ + type: string + required: + - group + - kind + - namespace + type: object + maxItems: 16 + minItems: 1 + type: array + to: + description: |- + To describes the resources that may be referenced by the resources + described in "From". Each entry in this list MUST be considered to be an + additional place that references can be valid to, or to put this another + way, entries MUST be combined using OR. + + + Support: Core + items: + description: |- + ReferenceGrantTo describes what Kinds are allowed as targets of the + references. + properties: + group: + description: |- + Group is the group of the referent. + When empty, the Kubernetes core API group is inferred. + + + Support: Core + maxLength: 253 + pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + kind: + description: |- + Kind is the kind of the referent. Although implementations may support + additional resources, the following types are part of the "Core" + support level for this field: + + + * Secret when used to permit a SecretObjectReference + * Service when used to permit a BackendObjectReference + maxLength: 63 + minLength: 1 + pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ + type: string + name: + description: |- + Name is the name of the referent. When unspecified, this policy + refers to all resources of the specified Group and Kind in the local + namespace. + maxLength: 253 + minLength: 1 + type: string + required: + - group + - kind + type: object + maxItems: 16 + minItems: 1 + type: array + required: + - from + - to + type: object + type: object + served: false + storage: false + subresources: {} + - additionalPrinterColumns: + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v1beta1 + schema: + openAPIV3Schema: + description: |- + ReferenceGrant identifies kinds of resources in other namespaces that are + trusted to reference the specified kinds of resources in the same namespace + as the policy. + + + Each ReferenceGrant can be used to represent a unique trust relationship. + Additional Reference Grants can be used to add to the set of trusted + sources of inbound references for the namespace they are defined within. + + + All cross-namespace references in Gateway API (with the exception of cross-namespace + Gateway-route attachment) require a ReferenceGrant. + + + ReferenceGrant is a form of runtime verification allowing users to assert + which cross-namespace object references are permitted. Implementations that + support ReferenceGrant MUST NOT permit cross-namespace references which have + no grant, and MUST respond to the removal of a grant by revoking the access + that the grant allowed. + 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: Spec defines the desired state of ReferenceGrant. + properties: + from: + description: |- + From describes the trusted namespaces and kinds that can reference the + resources described in "To". Each entry in this list MUST be considered + to be an additional place that references can be valid from, or to put + this another way, entries MUST be combined using OR. + + + Support: Core + items: + description: ReferenceGrantFrom describes trusted namespaces and + kinds. + properties: + group: + description: |- + Group is the group of the referent. + When empty, the Kubernetes core API group is inferred. + + + Support: Core + maxLength: 253 + pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + kind: + description: |- + Kind is the kind of the referent. Although implementations may support + additional resources, the following types are part of the "Core" + support level for this field. + + + When used to permit a SecretObjectReference: + + + * Gateway + + + When used to permit a BackendObjectReference: + + + * GRPCRoute + * HTTPRoute + * TCPRoute + * TLSRoute + * UDPRoute + maxLength: 63 + minLength: 1 + pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ + type: string + namespace: + description: |- + Namespace is the namespace of the referent. + + + Support: Core + maxLength: 63 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ + type: string + required: + - group + - kind + - namespace + type: object + maxItems: 16 + minItems: 1 + type: array + to: + description: |- + To describes the resources that may be referenced by the resources + described in "From". Each entry in this list MUST be considered to be an + additional place that references can be valid to, or to put this another + way, entries MUST be combined using OR. + + + Support: Core + items: + description: |- + ReferenceGrantTo describes what Kinds are allowed as targets of the + references. + properties: + group: + description: |- + Group is the group of the referent. + When empty, the Kubernetes core API group is inferred. + + + Support: Core + maxLength: 253 + pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + kind: + description: |- + Kind is the kind of the referent. Although implementations may support + additional resources, the following types are part of the "Core" + support level for this field: + + + * Secret when used to permit a SecretObjectReference + * Service when used to permit a BackendObjectReference + maxLength: 63 + minLength: 1 + pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ + type: string + name: + description: |- + Name is the name of the referent. When unspecified, this policy + refers to all resources of the specified Group and Kind in the local + namespace. + maxLength: 253 + minLength: 1 + type: string + required: + - group + - kind + type: object + maxItems: 16 + minItems: 1 + type: array + required: + - from + - to + type: object + type: object + served: true + storage: true + subresources: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: null + storedVersions: null diff --git a/charts/traefik/traefik/31.1.0/crds/hub.traefik.io_accesscontrolpolicies.yaml b/charts/traefik/traefik/31.1.0/crds/hub.traefik.io_accesscontrolpolicies.yaml new file mode 100644 index 000000000..821f969b6 --- /dev/null +++ b/charts/traefik/traefik/31.1.0/crds/hub.traefik.io_accesscontrolpolicies.yaml @@ -0,0 +1,368 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.14.0 + name: accesscontrolpolicies.hub.traefik.io +spec: + group: hub.traefik.io + names: + kind: AccessControlPolicy + listKind: AccessControlPolicyList + plural: accesscontrolpolicies + singular: accesscontrolpolicy + scope: Cluster + versions: + - name: v1alpha1 + schema: + openAPIV3Schema: + description: AccessControlPolicy defines an access control policy. + 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: AccessControlPolicySpec configures an access control policy. + properties: + apiKey: + description: AccessControlPolicyAPIKey configure an APIKey control + policy. + properties: + forwardHeaders: + additionalProperties: + type: string + description: ForwardHeaders instructs the middleware to forward + key metadata as header values upon successful authentication. + type: object + keySource: + description: KeySource defines how to extract API keys from requests. + properties: + cookie: + description: Cookie is the name of a cookie. + type: string + header: + description: Header is the name of a header. + type: string + headerAuthScheme: + description: |- + HeaderAuthScheme sets an optional auth scheme when Header is set to "Authorization". + If set, this scheme is removed from the token, and all requests not including it are dropped. + type: string + query: + description: Query is the name of a query parameter. + type: string + type: object + keys: + description: Keys define the set of authorized keys to access + a protected resource. + items: + description: AccessControlPolicyAPIKeyKey defines an API key. + properties: + id: + description: ID is the unique identifier of the key. + type: string + metadata: + additionalProperties: + type: string + description: Metadata holds arbitrary metadata for this + key, can be used by ForwardHeaders. + type: object + value: + description: Value is the SHAKE-256 hash (using 64 bytes) + of the API key. + type: string + required: + - id + - value + type: object + type: array + required: + - keySource + type: object + basicAuth: + description: AccessControlPolicyBasicAuth holds the HTTP basic authentication + configuration. + properties: + forwardUsernameHeader: + type: string + realm: + type: string + stripAuthorizationHeader: + type: boolean + users: + items: + type: string + type: array + type: object + jwt: + description: AccessControlPolicyJWT configures a JWT access control + policy. + properties: + claims: + type: string + forwardHeaders: + additionalProperties: + type: string + type: object + jwksFile: + type: string + jwksUrl: + type: string + publicKey: + type: string + signingSecret: + type: string + signingSecretBase64Encoded: + type: boolean + stripAuthorizationHeader: + type: boolean + tokenQueryKey: + type: string + type: object + oAuthIntro: + description: AccessControlOAuthIntro configures an OAuth 2.0 Token + Introspection access control policy. + properties: + claims: + type: string + clientConfig: + description: AccessControlOAuthIntroClientConfig configures the + OAuth 2.0 client for issuing token introspection requests. + properties: + headers: + additionalProperties: + type: string + description: Headers to set when sending requests to the Authorization + Server. + type: object + maxRetries: + default: 3 + description: MaxRetries defines the number of retries for + introspection requests. + type: integer + timeoutSeconds: + default: 5 + description: TimeoutSeconds configures the maximum amount + of seconds to wait before giving up on requests. + type: integer + tls: + description: TLS configures TLS communication with the Authorization + Server. + properties: + ca: + description: CA sets the CA bundle used to sign the Authorization + Server certificate. + type: string + insecureSkipVerify: + description: |- + InsecureSkipVerify skips the Authorization Server certificate validation. + For testing purposes only, do not use in production. + type: boolean + type: object + tokenTypeHint: + description: |- + TokenTypeHint is a hint to pass to the Authorization Server. + See https://tools.ietf.org/html/rfc7662#section-2.1 for more information. + type: string + url: + description: URL of the Authorization Server. + type: string + required: + - url + type: object + forwardHeaders: + additionalProperties: + type: string + type: object + tokenSource: + description: |- + TokenSource describes how to extract tokens from HTTP requests. + If multiple sources are set, the order is the following: header > query > cookie. + properties: + cookie: + description: Cookie is the name of a cookie. + type: string + header: + description: Header is the name of a header. + type: string + headerAuthScheme: + description: |- + HeaderAuthScheme sets an optional auth scheme when Header is set to "Authorization". + If set, this scheme is removed from the token, and all requests not including it are dropped. + type: string + query: + description: Query is the name of a query parameter. + type: string + type: object + required: + - clientConfig + - tokenSource + type: object + oidc: + description: AccessControlPolicyOIDC holds the OIDC authentication + configuration. + properties: + authParams: + additionalProperties: + type: string + type: object + claims: + type: string + clientId: + type: string + disableAuthRedirectionPaths: + items: + type: string + type: array + forwardHeaders: + additionalProperties: + type: string + type: object + issuer: + type: string + logoutUrl: + type: string + redirectUrl: + type: string + scopes: + items: + type: string + type: array + secret: + description: |- + SecretReference represents a Secret Reference. It has enough information to retrieve secret + in any namespace + properties: + name: + description: name is unique within a namespace to reference + a secret resource. + type: string + namespace: + description: namespace defines the space within which the + secret name must be unique. + type: string + type: object + x-kubernetes-map-type: atomic + session: + description: Session holds session configuration. + properties: + domain: + type: string + path: + type: string + refresh: + type: boolean + sameSite: + type: string + secure: + type: boolean + type: object + stateCookie: + description: StateCookie holds state cookie configuration. + properties: + domain: + type: string + path: + type: string + sameSite: + type: string + secure: + type: boolean + type: object + type: object + oidcGoogle: + description: AccessControlPolicyOIDCGoogle holds the Google OIDC authentication + configuration. + properties: + authParams: + additionalProperties: + type: string + type: object + clientId: + type: string + emails: + description: Emails are the allowed emails to connect. + items: + type: string + minItems: 1 + type: array + forwardHeaders: + additionalProperties: + type: string + type: object + logoutUrl: + type: string + redirectUrl: + type: string + secret: + description: |- + SecretReference represents a Secret Reference. It has enough information to retrieve secret + in any namespace + properties: + name: + description: name is unique within a namespace to reference + a secret resource. + type: string + namespace: + description: namespace defines the space within which the + secret name must be unique. + type: string + type: object + x-kubernetes-map-type: atomic + session: + description: Session holds session configuration. + properties: + domain: + type: string + path: + type: string + refresh: + type: boolean + sameSite: + type: string + secure: + type: boolean + type: object + stateCookie: + description: StateCookie holds state cookie configuration. + properties: + domain: + type: string + path: + type: string + sameSite: + type: string + secure: + type: boolean + type: object + type: object + type: object + status: + description: The current status of this access control policy. + properties: + specHash: + type: string + syncedAt: + format: date-time + type: string + version: + type: string + type: object + type: object + served: true + storage: true diff --git a/charts/traefik/traefik/31.1.0/crds/hub.traefik.io_apiaccesses.yaml b/charts/traefik/traefik/31.1.0/crds/hub.traefik.io_apiaccesses.yaml new file mode 100644 index 000000000..d1b9998c9 --- /dev/null +++ b/charts/traefik/traefik/31.1.0/crds/hub.traefik.io_apiaccesses.yaml @@ -0,0 +1,153 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.14.0 + name: apiaccesses.hub.traefik.io +spec: + group: hub.traefik.io + names: + kind: APIAccess + listKind: APIAccessList + plural: apiaccesses + singular: apiaccess + scope: Namespaced + versions: + - name: v1alpha1 + schema: + openAPIV3Schema: + description: APIAccess defines who can access to a set of APIs. + 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: The desired behavior of this APIAccess. + properties: + apiSelector: + description: |- + APISelector selects the APIs that will be accessible to the configured audience. + Multiple APIAccesses can select the same set of APIs. + This field is optional and follows standard label selector semantics. + An empty APISelector matches any API. + 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 + required: + - key + - operator + type: object + type: array + 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 + apis: + description: |- + APIs defines a set of APIs that will be accessible to the configured audience. + Multiple APIAccesses can select the same APIs. + When combined with APISelector, this set of APIs is appended to the matching APIs. + items: + description: APIReference references an API. + properties: + name: + description: Name of the API. + maxLength: 253 + type: string + required: + - name + type: object + maxItems: 100 + type: array + x-kubernetes-validations: + - message: duplicated apis + rule: self.all(x, self.exists_one(y, x.name == y.name)) + everyone: + description: Everyone indicates that all users will have access to + the selected APIs. + type: boolean + groups: + description: Groups are the consumer groups that will gain access + to the selected APIs. + items: + type: string + type: array + operationFilter: + description: |- + OperationFilter specifies the allowed operations on APIs and APIVersions. + If not set, all operations are available. + An empty OperationFilter prohibits all operations. + properties: + include: + description: Include defines the names of OperationSets that will + be accessible. + items: + type: string + maxItems: 100 + type: array + type: object + type: object + x-kubernetes-validations: + - message: groups and everyone are mutually exclusive + rule: '(has(self.everyone) && has(self.groups)) ? !(self.everyone && + self.groups.size() > 0) : true' + status: + description: The current status of this APIAccess. + properties: + hash: + description: Hash is a hash representing the APIAccess. + type: string + syncedAt: + format: date-time + type: string + version: + type: string + type: object + type: object + served: true + storage: true diff --git a/charts/traefik/traefik/31.1.0/crds/hub.traefik.io_apiportals.yaml b/charts/traefik/traefik/31.1.0/crds/hub.traefik.io_apiportals.yaml new file mode 100644 index 000000000..bc0417016 --- /dev/null +++ b/charts/traefik/traefik/31.1.0/crds/hub.traefik.io_apiportals.yaml @@ -0,0 +1,139 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.14.0 + name: apiportals.hub.traefik.io +spec: + group: hub.traefik.io + names: + kind: APIPortal + listKind: APIPortalList + plural: apiportals + singular: apiportal + scope: Namespaced + versions: + - name: v1alpha1 + schema: + openAPIV3Schema: + description: APIPortal defines a developer portal for accessing the documentation + of APIs. + 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: The desired behavior of this APIPortal. + properties: + description: + description: Description of the APIPortal. + type: string + title: + description: Title is the public facing name of the APIPortal. + type: string + trustedUrls: + description: TrustedURLs are the urls that are trusted by the OAuth + 2.0 authorization server. + items: + type: string + maxItems: 1 + minItems: 1 + type: array + x-kubernetes-validations: + - message: must be a valid URLs + rule: self.all(x, isURL(x)) + ui: + description: UI holds the UI customization options. + properties: + logoUrl: + description: LogoURL is the public URL of the logo. + type: string + type: object + required: + - trustedUrls + type: object + status: + description: The current status of this APIPortal. + properties: + hash: + description: Hash is a hash representing the APIPortal. + type: string + oidc: + description: OIDC is the OIDC configuration for accessing the exposed + APIPortal WebUI. + properties: + clientId: + description: ClientID is the OIDC ClientID for accessing the exposed + APIPortal WebUI. + type: string + companyClaim: + description: CompanyClaim is the name of the JWT claim containing + the user company. + type: string + emailClaim: + description: EmailClaim is the name of the JWT claim containing + the user email. + type: string + firstnameClaim: + description: FirstnameClaim is the name of the JWT claim containing + the user firstname. + type: string + generic: + description: Generic indicates whether or not the APIPortal authentication + relies on Generic OIDC. + type: boolean + groupsClaim: + description: GroupsClaim is the name of the JWT claim containing + the user groups. + type: string + issuer: + description: Issuer is the OIDC issuer for accessing the exposed + APIPortal WebUI. + type: string + lastnameClaim: + description: LastnameClaim is the name of the JWT claim containing + the user lastname. + type: string + scopes: + description: Scopes is the OIDC scopes for getting user attributes + during the authentication to the exposed APIPortal WebUI. + type: string + secretName: + description: SecretName is the name of the secret containing the + OIDC ClientSecret for accessing the exposed APIPortal WebUI. + type: string + syncedAttributes: + description: SyncedAttributes configure the user attributes to + sync. + items: + type: string + type: array + userIdClaim: + description: UserIDClaim is the name of the JWT claim containing + the user ID. + type: string + type: object + syncedAt: + format: date-time + type: string + version: + type: string + type: object + type: object + served: true + storage: true diff --git a/charts/traefik/traefik/31.1.0/crds/hub.traefik.io_apiratelimits.yaml b/charts/traefik/traefik/31.1.0/crds/hub.traefik.io_apiratelimits.yaml new file mode 100644 index 000000000..8e328d3c5 --- /dev/null +++ b/charts/traefik/traefik/31.1.0/crds/hub.traefik.io_apiratelimits.yaml @@ -0,0 +1,166 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.14.0 + name: apiratelimits.hub.traefik.io +spec: + group: hub.traefik.io + names: + kind: APIRateLimit + listKind: APIRateLimitList + plural: apiratelimits + singular: apiratelimit + scope: Namespaced + versions: + - name: v1alpha1 + schema: + openAPIV3Schema: + description: APIRateLimit defines how group of consumers are rate limited + on a set of APIs. + 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: The desired behavior of this APIRateLimit. + properties: + apiSelector: + description: |- + APISelector selects the APIs that will be rate limited. + Multiple APIRateLimits can select the same set of APIs. + This field is optional and follows standard label selector semantics. + An empty APISelector matches any API. + 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 + required: + - key + - operator + type: object + type: array + 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 + apis: + description: |- + APIs defines a set of APIs that will be rate limited. + Multiple APIRateLimits can select the same APIs. + When combined with APISelector, this set of APIs is appended to the matching APIs. + items: + description: APIReference references an API. + properties: + name: + description: Name of the API. + maxLength: 253 + type: string + required: + - name + type: object + maxItems: 100 + type: array + x-kubernetes-validations: + - message: duplicated apis + rule: self.all(x, self.exists_one(y, x.name == y.name)) + everyone: + description: |- + Everyone indicates that all users will, by default, be rate limited with this configuration. + If an APIRateLimit explicitly target a group, the default rate limit will be ignored. + type: boolean + groups: + description: |- + Groups are the consumer groups that will be rate limited. + Multiple APIRateLimits can target the same set of consumer groups, the most restrictive one applies. + When a consumer belongs to multiple groups, the least restrictive APIRateLimit applies. + items: + type: string + type: array + limit: + description: Limit is the maximum number of token in the bucket. + type: integer + x-kubernetes-validations: + - message: must be a positive number + rule: self >= 0 + period: + description: Period is the unit of time for the Limit. + format: duration + type: string + x-kubernetes-validations: + - message: must be between 1s and 1h + rule: self >= duration('1s') && self <= duration('1h') + strategy: + description: |- + Strategy defines how the bucket state will be synchronized between the different Traefik Hub instances. + It can be, either "local" or "distributed". + enum: + - local + - distributed + type: string + required: + - limit + type: object + x-kubernetes-validations: + - message: groups and everyone are mutually exclusive + rule: '(has(self.everyone) && has(self.groups)) ? !(self.everyone && + self.groups.size() > 0) : true' + status: + description: The current status of this APIRateLimit. + properties: + hash: + description: Hash is a hash representing the APIRateLimit. + type: string + syncedAt: + format: date-time + type: string + version: + type: string + type: object + type: object + served: true + storage: true diff --git a/charts/traefik/traefik/31.1.0/crds/hub.traefik.io_apis.yaml b/charts/traefik/traefik/31.1.0/crds/hub.traefik.io_apis.yaml new file mode 100644 index 000000000..a7b9e5e60 --- /dev/null +++ b/charts/traefik/traefik/31.1.0/crds/hub.traefik.io_apis.yaml @@ -0,0 +1,190 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.14.0 + name: apis.hub.traefik.io +spec: + group: hub.traefik.io + names: + kind: API + listKind: APIList + plural: apis + singular: api + scope: Namespaced + versions: + - name: v1alpha1 + schema: + openAPIV3Schema: + description: |- + API defines an HTTP interface that is exposed to external clients. It specifies the supported versions + and provides instructions for accessing its documentation. Once instantiated, an API object is associated + with an Ingress, IngressRoute, or HTTPRoute resource, enabling the exposure of the described API to the outside world. + 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: APISpec describes the API. + properties: + openApiSpec: + description: OpenAPISpec defines the API contract as an OpenAPI specification. + properties: + operationSets: + description: OperationSets defines the sets of operations to be + referenced for granular filtering in APIAccesses. + items: + description: |- + OperationSet gives a name to a set of matching OpenAPI operations. + This set of operations can then be referenced for granular filtering in APIAccesses. + properties: + matchers: + description: Matchers defines a list of alternative rules + for matching OpenAPI operations. + items: + description: OperationMatcher defines criteria for matching + an OpenAPI operation. + minProperties: 1 + properties: + methods: + description: Methods specifies the HTTP methods to + be included for selection. + items: + type: string + maxItems: 10 + type: array + path: + description: Path specifies the exact path of the + operations to select. + maxLength: 255 + type: string + x-kubernetes-validations: + - message: must start with a '/' + rule: self.startsWith('/') + - message: cannot contains '../' + rule: '!self.matches(r"""(\/\.\.\/)|(\/\.\.$)""")' + pathPrefix: + description: PathPrefix specifies the path prefix + of the operations to select. + maxLength: 255 + type: string + x-kubernetes-validations: + - message: must start with a '/' + rule: self.startsWith('/') + - message: cannot contains '../' + rule: '!self.matches(r"""(\/\.\.\/)|(\/\.\.$)""")' + pathRegex: + description: PathRegex specifies a regular expression + pattern for matching operations based on their paths. + type: string + type: object + x-kubernetes-validations: + - message: path, pathPrefix and pathRegex are mutually + exclusive + rule: '[has(self.path), has(self.pathPrefix), has(self.pathRegex)].filter(x, + x).size() <= 1' + maxItems: 100 + minItems: 1 + type: array + name: + description: Name is the name of the OperationSet to reference + in APIAccesses. + maxLength: 253 + type: string + required: + - matchers + - name + type: object + maxItems: 100 + type: array + override: + description: Override holds data used to override OpenAPI specification. + properties: + servers: + items: + properties: + url: + type: string + x-kubernetes-validations: + - message: must be a valid URL + rule: isURL(self) + required: + - url + type: object + maxItems: 100 + minItems: 1 + type: array + required: + - servers + type: object + path: + description: |- + Path specifies the endpoint path within the Kubernetes Service where the OpenAPI specification can be obtained. + The Service queried is determined by the associated Ingress, IngressRoute, or HTTPRoute resource to which the API is attached. + It's important to note that this option is incompatible if the Ingress or IngressRoute specifies multiple backend services. + The Path must be accessible via a GET request method and should serve a YAML or JSON document containing the OpenAPI specification. + maxLength: 255 + type: string + x-kubernetes-validations: + - message: must start with a '/' + rule: self.startsWith('/') + - message: cannot contains '../' + rule: '!self.matches(r"""(\/\.\.\/)|(\/\.\.$)""")' + url: + description: |- + URL is a Traefik Hub agent accessible URL for obtaining the OpenAPI specification. + The URL must be accessible via a GET request method and should serve a YAML or JSON document containing the OpenAPI specification. + type: string + x-kubernetes-validations: + - message: must be a valid URL + rule: isURL(self) + type: object + x-kubernetes-validations: + - message: path or url must be defined + rule: has(self.path) || has(self.url) + versions: + description: Versions are the different APIVersions available. + items: + description: APIVersionRef references an APIVersion. + properties: + name: + description: Name of the APIVersion. + maxLength: 253 + type: string + required: + - name + type: object + maxItems: 100 + minItems: 1 + type: array + type: object + status: + description: The current status of this API. + properties: + hash: + description: Hash is a hash representing the API. + type: string + syncedAt: + format: date-time + type: string + version: + type: string + type: object + type: object + served: true + storage: true diff --git a/charts/traefik/traefik/31.1.0/crds/hub.traefik.io_apiversions.yaml b/charts/traefik/traefik/31.1.0/crds/hub.traefik.io_apiversions.yaml new file mode 100644 index 000000000..97184effe --- /dev/null +++ b/charts/traefik/traefik/31.1.0/crds/hub.traefik.io_apiversions.yaml @@ -0,0 +1,194 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.14.0 + name: apiversions.hub.traefik.io +spec: + group: hub.traefik.io + names: + kind: APIVersion + listKind: APIVersionList + plural: apiversions + singular: apiversion + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .spec.title + name: Title + type: string + - jsonPath: .spec.release + name: Release + type: string + name: v1alpha1 + schema: + openAPIV3Schema: + description: APIVersion defines a version of an 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: The desired behavior of this APIVersion. + properties: + openApiSpec: + description: OpenAPISpec defines the API contract as an OpenAPI specification. + properties: + operationSets: + description: OperationSets defines the sets of operations to be + referenced for granular filtering in APIAccesses. + items: + description: |- + OperationSet gives a name to a set of matching OpenAPI operations. + This set of operations can then be referenced for granular filtering in APIAccesses. + properties: + matchers: + description: Matchers defines a list of alternative rules + for matching OpenAPI operations. + items: + description: OperationMatcher defines criteria for matching + an OpenAPI operation. + minProperties: 1 + properties: + methods: + description: Methods specifies the HTTP methods to + be included for selection. + items: + type: string + maxItems: 10 + type: array + path: + description: Path specifies the exact path of the + operations to select. + maxLength: 255 + type: string + x-kubernetes-validations: + - message: must start with a '/' + rule: self.startsWith('/') + - message: cannot contains '../' + rule: '!self.matches(r"""(\/\.\.\/)|(\/\.\.$)""")' + pathPrefix: + description: PathPrefix specifies the path prefix + of the operations to select. + maxLength: 255 + type: string + x-kubernetes-validations: + - message: must start with a '/' + rule: self.startsWith('/') + - message: cannot contains '../' + rule: '!self.matches(r"""(\/\.\.\/)|(\/\.\.$)""")' + pathRegex: + description: PathRegex specifies a regular expression + pattern for matching operations based on their paths. + type: string + type: object + x-kubernetes-validations: + - message: path, pathPrefix and pathRegex are mutually + exclusive + rule: '[has(self.path), has(self.pathPrefix), has(self.pathRegex)].filter(x, + x).size() <= 1' + maxItems: 100 + minItems: 1 + type: array + name: + description: Name is the name of the OperationSet to reference + in APIAccesses. + maxLength: 253 + type: string + required: + - matchers + - name + type: object + maxItems: 100 + type: array + override: + description: Override holds data used to override OpenAPI specification. + properties: + servers: + items: + properties: + url: + type: string + x-kubernetes-validations: + - message: must be a valid URL + rule: isURL(self) + required: + - url + type: object + maxItems: 100 + minItems: 1 + type: array + required: + - servers + type: object + path: + description: |- + Path specifies the endpoint path within the Kubernetes Service where the OpenAPI specification can be obtained. + The Service queried is determined by the associated Ingress, IngressRoute, or HTTPRoute resource to which the API is attached. + It's important to note that this option is incompatible if the Ingress or IngressRoute specifies multiple backend services. + The Path must be accessible via a GET request method and should serve a YAML or JSON document containing the OpenAPI specification. + maxLength: 255 + type: string + x-kubernetes-validations: + - message: must start with a '/' + rule: self.startsWith('/') + - message: cannot contains '../' + rule: '!self.matches(r"""(\/\.\.\/)|(\/\.\.$)""")' + url: + description: |- + URL is a Traefik Hub agent accessible URL for obtaining the OpenAPI specification. + The URL must be accessible via a GET request method and should serve a YAML or JSON document containing the OpenAPI specification. + type: string + x-kubernetes-validations: + - message: must be a valid URL + rule: isURL(self) + type: object + x-kubernetes-validations: + - message: path or url must be defined + rule: has(self.path) || has(self.url) + release: + description: |- + Release is the version number of the API. + This value must follow the SemVer format: https://semver.org/ + maxLength: 100 + type: string + x-kubernetes-validations: + - message: must be a valid semver version + rule: self.matches(r"""^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-]+)*))?$""") + title: + description: Title is the public facing name of the APIVersion. + type: string + required: + - release + type: object + status: + description: The current status of this APIVersion. + properties: + hash: + description: Hash is a hash representing the APIVersion. + type: string + syncedAt: + format: date-time + type: string + version: + type: string + type: object + type: object + served: true + storage: true + subresources: {} diff --git a/charts/traefik/traefik/31.1.0/crds/traefik.io_ingressroutes.yaml b/charts/traefik/traefik/31.1.0/crds/traefik.io_ingressroutes.yaml new file mode 100644 index 000000000..6ce60d68e --- /dev/null +++ b/charts/traefik/traefik/31.1.0/crds/traefik.io_ingressroutes.yaml @@ -0,0 +1,366 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.16.1 + name: ingressroutes.traefik.io +spec: + group: traefik.io + names: + kind: IngressRoute + listKind: IngressRouteList + plural: ingressroutes + singular: ingressroute + scope: Namespaced + versions: + - name: v1alpha1 + schema: + openAPIV3Schema: + description: IngressRoute is the CRD implementation of a Traefik HTTP Router. + 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: IngressRouteSpec defines the desired state of IngressRoute. + properties: + entryPoints: + description: |- + EntryPoints defines the list of entry point names to bind to. + Entry points have to be configured in the static configuration. + More info: https://doc.traefik.io/traefik/v3.1/routing/entrypoints/ + Default: all. + items: + type: string + type: array + routes: + description: Routes defines the list of routes. + items: + description: Route holds the HTTP route configuration. + properties: + kind: + description: |- + Kind defines the kind of the route. + Rule is the only supported kind. + enum: + - Rule + type: string + match: + description: |- + Match defines the router's rule. + More info: https://doc.traefik.io/traefik/v3.1/routing/routers/#rule + type: string + middlewares: + description: |- + Middlewares defines the list of references to Middleware resources. + More info: https://doc.traefik.io/traefik/v3.1/routing/providers/kubernetes-crd/#kind-middleware + items: + description: MiddlewareRef is a reference to a Middleware + resource. + properties: + name: + description: Name defines the name of the referenced Middleware + resource. + type: string + namespace: + description: Namespace defines the namespace of the referenced + Middleware resource. + type: string + required: + - name + type: object + type: array + priority: + description: |- + Priority defines the router's priority. + More info: https://doc.traefik.io/traefik/v3.1/routing/routers/#priority + type: integer + services: + description: |- + Services defines the list of Service. + It can contain any combination of TraefikService and/or reference to a Kubernetes Service. + items: + description: Service defines an upstream HTTP service to proxy + traffic to. + properties: + healthCheck: + description: Healthcheck defines health checks for ExternalName + services. + properties: + followRedirects: + description: |- + FollowRedirects defines whether redirects should be followed during the health check calls. + Default: true + type: boolean + headers: + additionalProperties: + type: string + description: Headers defines custom headers to be + sent to the health check endpoint. + type: object + hostname: + description: Hostname defines the value of hostname + in the Host header of the health check request. + type: string + interval: + anyOf: + - type: integer + - type: string + description: |- + Interval defines the frequency of the health check calls. + Default: 30s + x-kubernetes-int-or-string: true + method: + description: Method defines the healthcheck method. + type: string + mode: + description: |- + Mode defines the health check mode. + If defined to grpc, will use the gRPC health check protocol to probe the server. + Default: http + type: string + path: + description: Path defines the server URL path for + the health check endpoint. + type: string + port: + description: Port defines the server URL port for + the health check endpoint. + type: integer + scheme: + description: Scheme replaces the server URL scheme + for the health check endpoint. + type: string + status: + description: Status defines the expected HTTP status + code of the response to the health check request. + type: integer + timeout: + anyOf: + - type: integer + - type: string + description: |- + Timeout defines the maximum duration Traefik will wait for a health check request before considering the server unhealthy. + Default: 5s + x-kubernetes-int-or-string: true + type: object + kind: + description: Kind defines the kind of the Service. + enum: + - Service + - TraefikService + type: string + name: + description: |- + Name defines the name of the referenced Kubernetes Service or TraefikService. + The differentiation between the two is specified in the Kind field. + type: string + namespace: + description: Namespace defines the namespace of the referenced + Kubernetes Service or TraefikService. + type: string + nativeLB: + description: |- + NativeLB controls, when creating the load-balancer, + whether the LB's children are directly the pods IPs or if the only child is the Kubernetes Service clusterIP. + The Kubernetes Service itself does load-balance to the pods. + By default, NativeLB is false. + type: boolean + nodePortLB: + description: |- + NodePortLB controls, when creating the load-balancer, + whether the LB's children are directly the nodes internal IPs using the nodePort when the service type is NodePort. + It allows services to be reachable when Traefik runs externally from the Kubernetes cluster but within the same network of the nodes. + By default, NodePortLB is false. + type: boolean + passHostHeader: + description: |- + PassHostHeader defines whether the client Host header is forwarded to the upstream Kubernetes Service. + By default, passHostHeader is true. + type: boolean + port: + anyOf: + - type: integer + - type: string + description: |- + Port defines the port of a Kubernetes Service. + This can be a reference to a named port. + x-kubernetes-int-or-string: true + responseForwarding: + description: ResponseForwarding defines how Traefik forwards + the response from the upstream Kubernetes Service to + the client. + properties: + flushInterval: + description: |- + FlushInterval defines the interval, in milliseconds, in between flushes to the client while copying the response body. + A negative value means to flush immediately after each write to the client. + This configuration is ignored when ReverseProxy recognizes a response as a streaming response; + for such responses, writes are flushed to the client immediately. + Default: 100ms + type: string + type: object + scheme: + description: |- + Scheme defines the scheme to use for the request to the upstream Kubernetes Service. + It defaults to https when Kubernetes Service port is 443, http otherwise. + type: string + serversTransport: + description: |- + ServersTransport defines the name of ServersTransport resource to use. + It allows to configure the transport between Traefik and your servers. + Can only be used on a Kubernetes Service. + type: string + sticky: + description: |- + Sticky defines the sticky sessions configuration. + More info: https://doc.traefik.io/traefik/v3.1/routing/services/#sticky-sessions + properties: + cookie: + description: Cookie defines the sticky cookie configuration. + properties: + httpOnly: + description: HTTPOnly defines whether the cookie + can be accessed by client-side APIs, such as + JavaScript. + type: boolean + maxAge: + description: |- + MaxAge indicates the number of seconds until the cookie expires. + When set to a negative number, the cookie expires immediately. + When set to zero, the cookie never expires. + type: integer + name: + description: Name defines the Cookie name. + type: string + sameSite: + description: |- + SameSite defines the same site policy. + More info: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie/SameSite + type: string + secure: + description: Secure defines whether the cookie + can only be transmitted over an encrypted connection + (i.e. HTTPS). + type: boolean + type: object + type: object + strategy: + description: |- + Strategy defines the load balancing strategy between the servers. + RoundRobin is the only supported value at the moment. + type: string + weight: + description: |- + Weight defines the weight and should only be specified when Name references a TraefikService object + (and to be precise, one that embeds a Weighted Round Robin). + type: integer + required: + - name + type: object + type: array + syntax: + description: |- + Syntax defines the router's rule syntax. + More info: https://doc.traefik.io/traefik/v3.1/routing/routers/#rulesyntax + type: string + required: + - kind + - match + type: object + type: array + tls: + description: |- + TLS defines the TLS configuration. + More info: https://doc.traefik.io/traefik/v3.1/routing/routers/#tls + properties: + certResolver: + description: |- + CertResolver defines the name of the certificate resolver to use. + Cert resolvers have to be configured in the static configuration. + More info: https://doc.traefik.io/traefik/v3.1/https/acme/#certificate-resolvers + type: string + domains: + description: |- + Domains defines the list of domains that will be used to issue certificates. + More info: https://doc.traefik.io/traefik/v3.1/routing/routers/#domains + items: + description: Domain holds a domain name with SANs. + properties: + main: + description: Main defines the main domain name. + type: string + sans: + description: SANs defines the subject alternative domain + names. + items: + type: string + type: array + type: object + type: array + options: + description: |- + Options defines the reference to a TLSOption, that specifies the parameters of the TLS connection. + If not defined, the `default` TLSOption is used. + More info: https://doc.traefik.io/traefik/v3.1/https/tls/#tls-options + properties: + name: + description: |- + Name defines the name of the referenced TLSOption. + More info: https://doc.traefik.io/traefik/v3.1/routing/providers/kubernetes-crd/#kind-tlsoption + type: string + namespace: + description: |- + Namespace defines the namespace of the referenced TLSOption. + More info: https://doc.traefik.io/traefik/v3.1/routing/providers/kubernetes-crd/#kind-tlsoption + type: string + required: + - name + type: object + secretName: + description: SecretName is the name of the referenced Kubernetes + Secret to specify the certificate details. + type: string + store: + description: |- + Store defines the reference to the TLSStore, that will be used to store certificates. + Please note that only `default` TLSStore can be used. + properties: + name: + description: |- + Name defines the name of the referenced TLSStore. + More info: https://doc.traefik.io/traefik/v3.1/routing/providers/kubernetes-crd/#kind-tlsstore + type: string + namespace: + description: |- + Namespace defines the namespace of the referenced TLSStore. + More info: https://doc.traefik.io/traefik/v3.1/routing/providers/kubernetes-crd/#kind-tlsstore + type: string + required: + - name + type: object + type: object + required: + - routes + type: object + required: + - metadata + - spec + type: object + served: true + storage: true diff --git a/charts/traefik/traefik/31.1.0/crds/traefik.io_ingressroutetcps.yaml b/charts/traefik/traefik/31.1.0/crds/traefik.io_ingressroutetcps.yaml new file mode 100644 index 000000000..9db38f869 --- /dev/null +++ b/charts/traefik/traefik/31.1.0/crds/traefik.io_ingressroutetcps.yaml @@ -0,0 +1,247 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.16.1 + name: ingressroutetcps.traefik.io +spec: + group: traefik.io + names: + kind: IngressRouteTCP + listKind: IngressRouteTCPList + plural: ingressroutetcps + singular: ingressroutetcp + scope: Namespaced + versions: + - name: v1alpha1 + schema: + openAPIV3Schema: + description: IngressRouteTCP is the CRD implementation of a Traefik TCP Router. + 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: IngressRouteTCPSpec defines the desired state of IngressRouteTCP. + properties: + entryPoints: + description: |- + EntryPoints defines the list of entry point names to bind to. + Entry points have to be configured in the static configuration. + More info: https://doc.traefik.io/traefik/v3.1/routing/entrypoints/ + Default: all. + items: + type: string + type: array + routes: + description: Routes defines the list of routes. + items: + description: RouteTCP holds the TCP route configuration. + properties: + match: + description: |- + Match defines the router's rule. + More info: https://doc.traefik.io/traefik/v3.1/routing/routers/#rule_1 + type: string + middlewares: + description: Middlewares defines the list of references to MiddlewareTCP + resources. + items: + description: ObjectReference is a generic reference to a Traefik + resource. + properties: + name: + description: Name defines the name of the referenced Traefik + resource. + type: string + namespace: + description: Namespace defines the namespace of the referenced + Traefik resource. + type: string + required: + - name + type: object + type: array + priority: + description: |- + Priority defines the router's priority. + More info: https://doc.traefik.io/traefik/v3.1/routing/routers/#priority_1 + type: integer + services: + description: Services defines the list of TCP services. + items: + description: ServiceTCP defines an upstream TCP service to + proxy traffic to. + properties: + name: + description: Name defines the name of the referenced Kubernetes + Service. + type: string + namespace: + description: Namespace defines the namespace of the referenced + Kubernetes Service. + type: string + nativeLB: + description: |- + NativeLB controls, when creating the load-balancer, + whether the LB's children are directly the pods IPs or if the only child is the Kubernetes Service clusterIP. + The Kubernetes Service itself does load-balance to the pods. + By default, NativeLB is false. + type: boolean + nodePortLB: + description: |- + NodePortLB controls, when creating the load-balancer, + whether the LB's children are directly the nodes internal IPs using the nodePort when the service type is NodePort. + It allows services to be reachable when Traefik runs externally from the Kubernetes cluster but within the same network of the nodes. + By default, NodePortLB is false. + type: boolean + port: + anyOf: + - type: integer + - type: string + description: |- + Port defines the port of a Kubernetes Service. + This can be a reference to a named port. + x-kubernetes-int-or-string: true + proxyProtocol: + description: |- + ProxyProtocol defines the PROXY protocol configuration. + More info: https://doc.traefik.io/traefik/v3.1/routing/services/#proxy-protocol + properties: + version: + description: Version defines the PROXY Protocol version + to use. + type: integer + type: object + serversTransport: + description: |- + ServersTransport defines the name of ServersTransportTCP resource to use. + It allows to configure the transport between Traefik and your servers. + Can only be used on a Kubernetes Service. + type: string + terminationDelay: + description: |- + TerminationDelay defines the deadline that the proxy sets, after one of its connected peers indicates + it has closed the writing capability of its connection, to close the reading capability as well, + hence fully terminating the connection. + It is a duration in milliseconds, defaulting to 100. + A negative value means an infinite deadline (i.e. the reading capability is never closed). + Deprecated: TerminationDelay will not be supported in future APIVersions, please use ServersTransport to configure the TerminationDelay instead. + type: integer + tls: + description: TLS determines whether to use TLS when dialing + with the backend. + type: boolean + weight: + description: Weight defines the weight used when balancing + requests between multiple Kubernetes Service. + type: integer + required: + - name + - port + type: object + type: array + syntax: + description: |- + Syntax defines the router's rule syntax. + More info: https://doc.traefik.io/traefik/v3.1/routing/routers/#rulesyntax_1 + type: string + required: + - match + type: object + type: array + tls: + description: |- + TLS defines the TLS configuration on a layer 4 / TCP Route. + More info: https://doc.traefik.io/traefik/v3.1/routing/routers/#tls_1 + properties: + certResolver: + description: |- + CertResolver defines the name of the certificate resolver to use. + Cert resolvers have to be configured in the static configuration. + More info: https://doc.traefik.io/traefik/v3.1/https/acme/#certificate-resolvers + type: string + domains: + description: |- + Domains defines the list of domains that will be used to issue certificates. + More info: https://doc.traefik.io/traefik/v3.1/routing/routers/#domains + items: + description: Domain holds a domain name with SANs. + properties: + main: + description: Main defines the main domain name. + type: string + sans: + description: SANs defines the subject alternative domain + names. + items: + type: string + type: array + type: object + type: array + options: + description: |- + Options defines the reference to a TLSOption, that specifies the parameters of the TLS connection. + If not defined, the `default` TLSOption is used. + More info: https://doc.traefik.io/traefik/v3.1/https/tls/#tls-options + properties: + name: + description: Name defines the name of the referenced Traefik + resource. + type: string + namespace: + description: Namespace defines the namespace of the referenced + Traefik resource. + type: string + required: + - name + type: object + passthrough: + description: Passthrough defines whether a TLS router will terminate + the TLS connection. + type: boolean + secretName: + description: SecretName is the name of the referenced Kubernetes + Secret to specify the certificate details. + type: string + store: + description: |- + Store defines the reference to the TLSStore, that will be used to store certificates. + Please note that only `default` TLSStore can be used. + properties: + name: + description: Name defines the name of the referenced Traefik + resource. + type: string + namespace: + description: Namespace defines the namespace of the referenced + Traefik resource. + type: string + required: + - name + type: object + type: object + required: + - routes + type: object + required: + - metadata + - spec + type: object + served: true + storage: true diff --git a/charts/traefik/traefik/31.1.0/crds/traefik.io_ingressrouteudps.yaml b/charts/traefik/traefik/31.1.0/crds/traefik.io_ingressrouteudps.yaml new file mode 100644 index 000000000..9b04a8355 --- /dev/null +++ b/charts/traefik/traefik/31.1.0/crds/traefik.io_ingressrouteudps.yaml @@ -0,0 +1,111 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.16.1 + name: ingressrouteudps.traefik.io +spec: + group: traefik.io + names: + kind: IngressRouteUDP + listKind: IngressRouteUDPList + plural: ingressrouteudps + singular: ingressrouteudp + scope: Namespaced + versions: + - name: v1alpha1 + schema: + openAPIV3Schema: + description: IngressRouteUDP is a CRD implementation of a Traefik UDP Router. + 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: IngressRouteUDPSpec defines the desired state of a IngressRouteUDP. + properties: + entryPoints: + description: |- + EntryPoints defines the list of entry point names to bind to. + Entry points have to be configured in the static configuration. + More info: https://doc.traefik.io/traefik/v3.1/routing/entrypoints/ + Default: all. + items: + type: string + type: array + routes: + description: Routes defines the list of routes. + items: + description: RouteUDP holds the UDP route configuration. + properties: + services: + description: Services defines the list of UDP services. + items: + description: ServiceUDP defines an upstream UDP service to + proxy traffic to. + properties: + name: + description: Name defines the name of the referenced Kubernetes + Service. + type: string + namespace: + description: Namespace defines the namespace of the referenced + Kubernetes Service. + type: string + nativeLB: + description: |- + NativeLB controls, when creating the load-balancer, + whether the LB's children are directly the pods IPs or if the only child is the Kubernetes Service clusterIP. + The Kubernetes Service itself does load-balance to the pods. + By default, NativeLB is false. + type: boolean + nodePortLB: + description: |- + NodePortLB controls, when creating the load-balancer, + whether the LB's children are directly the nodes internal IPs using the nodePort when the service type is NodePort. + It allows services to be reachable when Traefik runs externally from the Kubernetes cluster but within the same network of the nodes. + By default, NodePortLB is false. + type: boolean + port: + anyOf: + - type: integer + - type: string + description: |- + Port defines the port of a Kubernetes Service. + This can be a reference to a named port. + x-kubernetes-int-or-string: true + weight: + description: Weight defines the weight used when balancing + requests between multiple Kubernetes Service. + type: integer + required: + - name + - port + type: object + type: array + type: object + type: array + required: + - routes + type: object + required: + - metadata + - spec + type: object + served: true + storage: true diff --git a/charts/traefik/traefik/31.1.0/crds/traefik.io_middlewares.yaml b/charts/traefik/traefik/31.1.0/crds/traefik.io_middlewares.yaml new file mode 100644 index 000000000..7bc7f0546 --- /dev/null +++ b/charts/traefik/traefik/31.1.0/crds/traefik.io_middlewares.yaml @@ -0,0 +1,1098 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.16.1 + name: middlewares.traefik.io +spec: + group: traefik.io + names: + kind: Middleware + listKind: MiddlewareList + plural: middlewares + singular: middleware + scope: Namespaced + versions: + - name: v1alpha1 + schema: + openAPIV3Schema: + description: |- + Middleware is the CRD implementation of a Traefik Middleware. + More info: https://doc.traefik.io/traefik/v3.1/middlewares/http/overview/ + 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: MiddlewareSpec defines the desired state of a Middleware. + properties: + addPrefix: + description: |- + AddPrefix holds the add prefix middleware configuration. + This middleware updates the path of a request before forwarding it. + More info: https://doc.traefik.io/traefik/v3.1/middlewares/http/addprefix/ + properties: + prefix: + description: |- + Prefix is the string to add before the current path in the requested URL. + It should include a leading slash (/). + type: string + type: object + basicAuth: + description: |- + BasicAuth holds the basic auth middleware configuration. + This middleware restricts access to your services to known users. + More info: https://doc.traefik.io/traefik/v3.1/middlewares/http/basicauth/ + properties: + headerField: + description: |- + HeaderField defines a header field to store the authenticated user. + More info: https://doc.traefik.io/traefik/v3.1/middlewares/http/basicauth/#headerfield + type: string + realm: + description: |- + Realm allows the protected resources on a server to be partitioned into a set of protection spaces, each with its own authentication scheme. + Default: traefik. + type: string + removeHeader: + description: |- + RemoveHeader sets the removeHeader option to true to remove the authorization header before forwarding the request to your service. + Default: false. + type: boolean + secret: + description: Secret is the name of the referenced Kubernetes Secret + containing user credentials. + type: string + type: object + buffering: + description: |- + Buffering holds the buffering middleware configuration. + This middleware retries or limits the size of requests that can be forwarded to backends. + More info: https://doc.traefik.io/traefik/v3.1/middlewares/http/buffering/#maxrequestbodybytes + properties: + maxRequestBodyBytes: + description: |- + MaxRequestBodyBytes defines the maximum allowed body size for the request (in bytes). + If the request exceeds the allowed size, it is not forwarded to the service, and the client gets a 413 (Request Entity Too Large) response. + Default: 0 (no maximum). + format: int64 + type: integer + maxResponseBodyBytes: + description: |- + MaxResponseBodyBytes defines the maximum allowed response size from the service (in bytes). + If the response exceeds the allowed size, it is not forwarded to the client. The client gets a 500 (Internal Server Error) response instead. + Default: 0 (no maximum). + format: int64 + type: integer + memRequestBodyBytes: + description: |- + MemRequestBodyBytes defines the threshold (in bytes) from which the request will be buffered on disk instead of in memory. + Default: 1048576 (1Mi). + format: int64 + type: integer + memResponseBodyBytes: + description: |- + MemResponseBodyBytes defines the threshold (in bytes) from which the response will be buffered on disk instead of in memory. + Default: 1048576 (1Mi). + format: int64 + type: integer + retryExpression: + description: |- + RetryExpression defines the retry conditions. + It is a logical combination of functions with operators AND (&&) and OR (||). + More info: https://doc.traefik.io/traefik/v3.1/middlewares/http/buffering/#retryexpression + type: string + type: object + chain: + description: |- + Chain holds the configuration of the chain middleware. + This middleware enables to define reusable combinations of other pieces of middleware. + More info: https://doc.traefik.io/traefik/v3.1/middlewares/http/chain/ + properties: + middlewares: + description: Middlewares is the list of MiddlewareRef which composes + the chain. + items: + description: MiddlewareRef is a reference to a Middleware resource. + properties: + name: + description: Name defines the name of the referenced Middleware + resource. + type: string + namespace: + description: Namespace defines the namespace of the referenced + Middleware resource. + type: string + required: + - name + type: object + type: array + type: object + circuitBreaker: + description: CircuitBreaker holds the circuit breaker configuration. + properties: + checkPeriod: + anyOf: + - type: integer + - type: string + description: CheckPeriod is the interval between successive checks + of the circuit breaker condition (when in standby state). + x-kubernetes-int-or-string: true + expression: + description: Expression is the condition that triggers the tripped + state. + type: string + fallbackDuration: + anyOf: + - type: integer + - type: string + description: FallbackDuration is the duration for which the circuit + breaker will wait before trying to recover (from a tripped state). + x-kubernetes-int-or-string: true + recoveryDuration: + anyOf: + - type: integer + - type: string + description: RecoveryDuration is the duration for which the circuit + breaker will try to recover (as soon as it is in recovering + state). + x-kubernetes-int-or-string: true + responseCode: + description: ResponseCode is the status code that the circuit + breaker will return while it is in the open state. + type: integer + type: object + compress: + description: |- + Compress holds the compress middleware configuration. + This middleware compresses responses before sending them to the client, using gzip compression. + More info: https://doc.traefik.io/traefik/v3.1/middlewares/http/compress/ + properties: + defaultEncoding: + description: DefaultEncoding specifies the default encoding if + the `Accept-Encoding` header is not in the request or contains + a wildcard (`*`). + type: string + excludedContentTypes: + description: |- + ExcludedContentTypes defines the list of content types to compare the Content-Type header of the incoming requests and responses before compressing. + `application/grpc` is always excluded. + items: + type: string + type: array + includedContentTypes: + description: IncludedContentTypes defines the list of content + types to compare the Content-Type header of the responses before + compressing. + items: + type: string + type: array + minResponseBodyBytes: + description: |- + MinResponseBodyBytes defines the minimum amount of bytes a response body must have to be compressed. + Default: 1024. + type: integer + type: object + contentType: + description: |- + ContentType holds the content-type middleware configuration. + This middleware exists to enable the correct behavior until at least the default one can be changed in a future version. + properties: + autoDetect: + description: |- + AutoDetect specifies whether to let the `Content-Type` header, if it has not been set by the backend, + be automatically set to a value derived from the contents of the response. + Deprecated: AutoDetect option is deprecated, Content-Type middleware is only meant to be used to enable the content-type detection, please remove any usage of this option. + type: boolean + type: object + digestAuth: + description: |- + DigestAuth holds the digest auth middleware configuration. + This middleware restricts access to your services to known users. + More info: https://doc.traefik.io/traefik/v3.1/middlewares/http/digestauth/ + properties: + headerField: + description: |- + HeaderField defines a header field to store the authenticated user. + More info: https://doc.traefik.io/traefik/v3.1/middlewares/http/basicauth/#headerfield + type: string + realm: + description: |- + Realm allows the protected resources on a server to be partitioned into a set of protection spaces, each with its own authentication scheme. + Default: traefik. + type: string + removeHeader: + description: RemoveHeader defines whether to remove the authorization + header before forwarding the request to the backend. + type: boolean + secret: + description: Secret is the name of the referenced Kubernetes Secret + containing user credentials. + type: string + type: object + errors: + description: |- + ErrorPage holds the custom error middleware configuration. + This middleware returns a custom page in lieu of the default, according to configured ranges of HTTP Status codes. + More info: https://doc.traefik.io/traefik/v3.1/middlewares/http/errorpages/ + properties: + query: + description: |- + Query defines the URL for the error page (hosted by service). + The {status} variable can be used in order to insert the status code in the URL. + type: string + service: + description: |- + Service defines the reference to a Kubernetes Service that will serve the error page. + More info: https://doc.traefik.io/traefik/v3.1/middlewares/http/errorpages/#service + properties: + healthCheck: + description: Healthcheck defines health checks for ExternalName + services. + properties: + followRedirects: + description: |- + FollowRedirects defines whether redirects should be followed during the health check calls. + Default: true + type: boolean + headers: + additionalProperties: + type: string + description: Headers defines custom headers to be sent + to the health check endpoint. + type: object + hostname: + description: Hostname defines the value of hostname in + the Host header of the health check request. + type: string + interval: + anyOf: + - type: integer + - type: string + description: |- + Interval defines the frequency of the health check calls. + Default: 30s + x-kubernetes-int-or-string: true + method: + description: Method defines the healthcheck method. + type: string + mode: + description: |- + Mode defines the health check mode. + If defined to grpc, will use the gRPC health check protocol to probe the server. + Default: http + type: string + path: + description: Path defines the server URL path for the + health check endpoint. + type: string + port: + description: Port defines the server URL port for the + health check endpoint. + type: integer + scheme: + description: Scheme replaces the server URL scheme for + the health check endpoint. + type: string + status: + description: Status defines the expected HTTP status code + of the response to the health check request. + type: integer + timeout: + anyOf: + - type: integer + - type: string + description: |- + Timeout defines the maximum duration Traefik will wait for a health check request before considering the server unhealthy. + Default: 5s + x-kubernetes-int-or-string: true + type: object + kind: + description: Kind defines the kind of the Service. + enum: + - Service + - TraefikService + type: string + name: + description: |- + Name defines the name of the referenced Kubernetes Service or TraefikService. + The differentiation between the two is specified in the Kind field. + type: string + namespace: + description: Namespace defines the namespace of the referenced + Kubernetes Service or TraefikService. + type: string + nativeLB: + description: |- + NativeLB controls, when creating the load-balancer, + whether the LB's children are directly the pods IPs or if the only child is the Kubernetes Service clusterIP. + The Kubernetes Service itself does load-balance to the pods. + By default, NativeLB is false. + type: boolean + nodePortLB: + description: |- + NodePortLB controls, when creating the load-balancer, + whether the LB's children are directly the nodes internal IPs using the nodePort when the service type is NodePort. + It allows services to be reachable when Traefik runs externally from the Kubernetes cluster but within the same network of the nodes. + By default, NodePortLB is false. + type: boolean + passHostHeader: + description: |- + PassHostHeader defines whether the client Host header is forwarded to the upstream Kubernetes Service. + By default, passHostHeader is true. + type: boolean + port: + anyOf: + - type: integer + - type: string + description: |- + Port defines the port of a Kubernetes Service. + This can be a reference to a named port. + x-kubernetes-int-or-string: true + responseForwarding: + description: ResponseForwarding defines how Traefik forwards + the response from the upstream Kubernetes Service to the + client. + properties: + flushInterval: + description: |- + FlushInterval defines the interval, in milliseconds, in between flushes to the client while copying the response body. + A negative value means to flush immediately after each write to the client. + This configuration is ignored when ReverseProxy recognizes a response as a streaming response; + for such responses, writes are flushed to the client immediately. + Default: 100ms + type: string + type: object + scheme: + description: |- + Scheme defines the scheme to use for the request to the upstream Kubernetes Service. + It defaults to https when Kubernetes Service port is 443, http otherwise. + type: string + serversTransport: + description: |- + ServersTransport defines the name of ServersTransport resource to use. + It allows to configure the transport between Traefik and your servers. + Can only be used on a Kubernetes Service. + type: string + sticky: + description: |- + Sticky defines the sticky sessions configuration. + More info: https://doc.traefik.io/traefik/v3.1/routing/services/#sticky-sessions + properties: + cookie: + description: Cookie defines the sticky cookie configuration. + properties: + httpOnly: + description: HTTPOnly defines whether the cookie can + be accessed by client-side APIs, such as JavaScript. + type: boolean + maxAge: + description: |- + MaxAge indicates the number of seconds until the cookie expires. + When set to a negative number, the cookie expires immediately. + When set to zero, the cookie never expires. + type: integer + name: + description: Name defines the Cookie name. + type: string + sameSite: + description: |- + SameSite defines the same site policy. + More info: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie/SameSite + type: string + secure: + description: Secure defines whether the cookie can + only be transmitted over an encrypted connection + (i.e. HTTPS). + type: boolean + type: object + type: object + strategy: + description: |- + Strategy defines the load balancing strategy between the servers. + RoundRobin is the only supported value at the moment. + type: string + weight: + description: |- + Weight defines the weight and should only be specified when Name references a TraefikService object + (and to be precise, one that embeds a Weighted Round Robin). + type: integer + required: + - name + type: object + status: + description: |- + Status defines which status or range of statuses should result in an error page. + It can be either a status code as a number (500), + as multiple comma-separated numbers (500,502), + as ranges by separating two codes with a dash (500-599), + or a combination of the two (404,418,500-599). + items: + type: string + type: array + type: object + forwardAuth: + description: |- + ForwardAuth holds the forward auth middleware configuration. + This middleware delegates the request authentication to a Service. + More info: https://doc.traefik.io/traefik/v3.1/middlewares/http/forwardauth/ + properties: + addAuthCookiesToResponse: + description: AddAuthCookiesToResponse defines the list of cookies + to copy from the authentication server response to the response. + items: + type: string + type: array + address: + description: Address defines the authentication server address. + type: string + authRequestHeaders: + description: |- + AuthRequestHeaders defines the list of the headers to copy from the request to the authentication server. + If not set or empty then all request headers are passed. + items: + type: string + type: array + authResponseHeaders: + description: AuthResponseHeaders defines the list of headers to + copy from the authentication server response and set on forwarded + request, replacing any existing conflicting headers. + items: + type: string + type: array + authResponseHeadersRegex: + description: |- + AuthResponseHeadersRegex defines the regex to match headers to copy from the authentication server response and set on forwarded request, after stripping all headers that match the regex. + More info: https://doc.traefik.io/traefik/v3.1/middlewares/http/forwardauth/#authresponseheadersregex + type: string + tls: + description: TLS defines the configuration used to secure the + connection to the authentication server. + properties: + caOptional: + description: 'Deprecated: TLS client authentication is a server + side option (see https://github.com/golang/go/blob/740a490f71d026bb7d2d13cb8fa2d6d6e0572b70/src/crypto/tls/common.go#L634).' + type: boolean + caSecret: + description: |- + CASecret is the name of the referenced Kubernetes Secret containing the CA to validate the server certificate. + The CA certificate is extracted from key `tls.ca` or `ca.crt`. + type: string + certSecret: + description: |- + CertSecret is the name of the referenced Kubernetes Secret containing the client certificate. + The client certificate is extracted from the keys `tls.crt` and `tls.key`. + type: string + insecureSkipVerify: + description: InsecureSkipVerify defines whether the server + certificates should be validated. + type: boolean + type: object + trustForwardHeader: + description: 'TrustForwardHeader defines whether to trust (ie: + forward) all X-Forwarded-* headers.' + type: boolean + type: object + grpcWeb: + description: |- + GrpcWeb holds the gRPC web middleware configuration. + This middleware converts a gRPC web request to an HTTP/2 gRPC request. + properties: + allowOrigins: + description: |- + AllowOrigins is a list of allowable origins. + Can also be a wildcard origin "*". + items: + type: string + type: array + type: object + headers: + description: |- + Headers holds the headers middleware configuration. + This middleware manages the requests and responses headers. + More info: https://doc.traefik.io/traefik/v3.1/middlewares/http/headers/#customrequestheaders + properties: + accessControlAllowCredentials: + description: AccessControlAllowCredentials defines whether the + request can include user credentials. + type: boolean + accessControlAllowHeaders: + description: AccessControlAllowHeaders defines the Access-Control-Request-Headers + values sent in preflight response. + items: + type: string + type: array + accessControlAllowMethods: + description: AccessControlAllowMethods defines the Access-Control-Request-Method + values sent in preflight response. + items: + type: string + type: array + accessControlAllowOriginList: + description: AccessControlAllowOriginList is a list of allowable + origins. Can also be a wildcard origin "*". + items: + type: string + type: array + accessControlAllowOriginListRegex: + description: AccessControlAllowOriginListRegex is a list of allowable + origins written following the Regular Expression syntax (https://golang.org/pkg/regexp/). + items: + type: string + type: array + accessControlExposeHeaders: + description: AccessControlExposeHeaders defines the Access-Control-Expose-Headers + values sent in preflight response. + items: + type: string + type: array + accessControlMaxAge: + description: AccessControlMaxAge defines the time that a preflight + request may be cached. + format: int64 + type: integer + addVaryHeader: + description: AddVaryHeader defines whether the Vary header is + automatically added/updated when the AccessControlAllowOriginList + is set. + type: boolean + allowedHosts: + description: AllowedHosts defines the fully qualified list of + allowed domain names. + items: + type: string + type: array + browserXssFilter: + description: BrowserXSSFilter defines whether to add the X-XSS-Protection + header with the value 1; mode=block. + type: boolean + contentSecurityPolicy: + description: ContentSecurityPolicy defines the Content-Security-Policy + header value. + type: string + contentSecurityPolicyReportOnly: + description: ContentSecurityPolicyReportOnly defines the Content-Security-Policy-Report-Only + header value. + type: string + contentTypeNosniff: + description: ContentTypeNosniff defines whether to add the X-Content-Type-Options + header with the nosniff value. + type: boolean + customBrowserXSSValue: + description: |- + CustomBrowserXSSValue defines the X-XSS-Protection header value. + This overrides the BrowserXssFilter option. + type: string + customFrameOptionsValue: + description: |- + CustomFrameOptionsValue defines the X-Frame-Options header value. + This overrides the FrameDeny option. + type: string + customRequestHeaders: + additionalProperties: + type: string + description: CustomRequestHeaders defines the header names and + values to apply to the request. + type: object + customResponseHeaders: + additionalProperties: + type: string + description: CustomResponseHeaders defines the header names and + values to apply to the response. + type: object + featurePolicy: + description: 'Deprecated: FeaturePolicy option is deprecated, + please use PermissionsPolicy instead.' + type: string + forceSTSHeader: + description: ForceSTSHeader defines whether to add the STS header + even when the connection is HTTP. + type: boolean + frameDeny: + description: FrameDeny defines whether to add the X-Frame-Options + header with the DENY value. + type: boolean + hostsProxyHeaders: + description: HostsProxyHeaders defines the header keys that may + hold a proxied hostname value for the request. + items: + type: string + type: array + isDevelopment: + description: |- + IsDevelopment defines whether to mitigate the unwanted effects of the AllowedHosts, SSL, and STS options when developing. + Usually testing takes place using HTTP, not HTTPS, and on localhost, not your production domain. + If you would like your development environment to mimic production with complete Host blocking, SSL redirects, + and STS headers, leave this as false. + type: boolean + permissionsPolicy: + description: |- + PermissionsPolicy defines the Permissions-Policy header value. + This allows sites to control browser features. + type: string + publicKey: + description: PublicKey is the public key that implements HPKP + to prevent MITM attacks with forged certificates. + type: string + referrerPolicy: + description: |- + ReferrerPolicy defines the Referrer-Policy header value. + This allows sites to control whether browsers forward the Referer header to other sites. + type: string + sslForceHost: + description: 'Deprecated: SSLForceHost option is deprecated, please + use RedirectRegex instead.' + type: boolean + sslHost: + description: 'Deprecated: SSLHost option is deprecated, please + use RedirectRegex instead.' + type: string + sslProxyHeaders: + additionalProperties: + type: string + description: |- + SSLProxyHeaders defines the header keys with associated values that would indicate a valid HTTPS request. + It can be useful when using other proxies (example: "X-Forwarded-Proto": "https"). + type: object + sslRedirect: + description: 'Deprecated: SSLRedirect option is deprecated, please + use EntryPoint redirection or RedirectScheme instead.' + type: boolean + sslTemporaryRedirect: + description: 'Deprecated: SSLTemporaryRedirect option is deprecated, + please use EntryPoint redirection or RedirectScheme instead.' + type: boolean + stsIncludeSubdomains: + description: STSIncludeSubdomains defines whether the includeSubDomains + directive is appended to the Strict-Transport-Security header. + type: boolean + stsPreload: + description: STSPreload defines whether the preload flag is appended + to the Strict-Transport-Security header. + type: boolean + stsSeconds: + description: |- + STSSeconds defines the max-age of the Strict-Transport-Security header. + If set to 0, the header is not set. + format: int64 + type: integer + type: object + inFlightReq: + description: |- + InFlightReq holds the in-flight request middleware configuration. + This middleware limits the number of requests being processed and served concurrently. + More info: https://doc.traefik.io/traefik/v3.1/middlewares/http/inflightreq/ + properties: + amount: + description: |- + Amount defines the maximum amount of allowed simultaneous in-flight request. + The middleware responds with HTTP 429 Too Many Requests if there are already amount requests in progress (based on the same sourceCriterion strategy). + format: int64 + type: integer + sourceCriterion: + description: |- + SourceCriterion defines what criterion is used to group requests as originating from a common source. + If several strategies are defined at the same time, an error will be raised. + If none are set, the default is to use the requestHost. + More info: https://doc.traefik.io/traefik/v3.1/middlewares/http/inflightreq/#sourcecriterion + properties: + ipStrategy: + description: |- + IPStrategy holds the IP strategy configuration used by Traefik to determine the client IP. + More info: https://doc.traefik.io/traefik/v3.1/middlewares/http/ipallowlist/#ipstrategy + properties: + depth: + description: Depth tells Traefik to use the X-Forwarded-For + header and take the IP located at the depth position + (starting from the right). + type: integer + excludedIPs: + description: ExcludedIPs configures Traefik to scan the + X-Forwarded-For header and select the first IP not in + the list. + items: + type: string + type: array + type: object + requestHeaderName: + description: RequestHeaderName defines the name of the header + used to group incoming requests. + type: string + requestHost: + description: RequestHost defines whether to consider the request + Host as the source. + type: boolean + type: object + type: object + ipAllowList: + description: |- + IPAllowList holds the IP allowlist middleware configuration. + This middleware limits allowed requests based on the client IP. + More info: https://doc.traefik.io/traefik/v3.1/middlewares/http/ipallowlist/ + properties: + ipStrategy: + description: |- + IPStrategy holds the IP strategy configuration used by Traefik to determine the client IP. + More info: https://doc.traefik.io/traefik/v3.1/middlewares/http/ipallowlist/#ipstrategy + properties: + depth: + description: Depth tells Traefik to use the X-Forwarded-For + header and take the IP located at the depth position (starting + from the right). + type: integer + excludedIPs: + description: ExcludedIPs configures Traefik to scan the X-Forwarded-For + header and select the first IP not in the list. + items: + type: string + type: array + type: object + rejectStatusCode: + description: |- + RejectStatusCode defines the HTTP status code used for refused requests. + If not set, the default is 403 (Forbidden). + type: integer + sourceRange: + description: SourceRange defines the set of allowed IPs (or ranges + of allowed IPs by using CIDR notation). + items: + type: string + type: array + type: object + ipWhiteList: + description: 'Deprecated: please use IPAllowList instead.' + properties: + ipStrategy: + description: |- + IPStrategy holds the IP strategy configuration used by Traefik to determine the client IP. + More info: https://doc.traefik.io/traefik/v3.1/middlewares/http/ipallowlist/#ipstrategy + properties: + depth: + description: Depth tells Traefik to use the X-Forwarded-For + header and take the IP located at the depth position (starting + from the right). + type: integer + excludedIPs: + description: ExcludedIPs configures Traefik to scan the X-Forwarded-For + header and select the first IP not in the list. + items: + type: string + type: array + type: object + sourceRange: + description: SourceRange defines the set of allowed IPs (or ranges + of allowed IPs by using CIDR notation). Required. + items: + type: string + type: array + type: object + passTLSClientCert: + description: |- + PassTLSClientCert holds the pass TLS client cert middleware configuration. + This middleware adds the selected data from the passed client TLS certificate to a header. + More info: https://doc.traefik.io/traefik/v3.1/middlewares/http/passtlsclientcert/ + properties: + info: + description: Info selects the specific client certificate details + you want to add to the X-Forwarded-Tls-Client-Cert-Info header. + properties: + issuer: + description: Issuer defines the client certificate issuer + details to add to the X-Forwarded-Tls-Client-Cert-Info header. + properties: + commonName: + description: CommonName defines whether to add the organizationalUnit + information into the issuer. + type: boolean + country: + description: Country defines whether to add the country + information into the issuer. + type: boolean + domainComponent: + description: DomainComponent defines whether to add the + domainComponent information into the issuer. + type: boolean + locality: + description: Locality defines whether to add the locality + information into the issuer. + type: boolean + organization: + description: Organization defines whether to add the organization + information into the issuer. + type: boolean + province: + description: Province defines whether to add the province + information into the issuer. + type: boolean + serialNumber: + description: SerialNumber defines whether to add the serialNumber + information into the issuer. + type: boolean + type: object + notAfter: + description: NotAfter defines whether to add the Not After + information from the Validity part. + type: boolean + notBefore: + description: NotBefore defines whether to add the Not Before + information from the Validity part. + type: boolean + sans: + description: Sans defines whether to add the Subject Alternative + Name information from the Subject Alternative Name part. + type: boolean + serialNumber: + description: SerialNumber defines whether to add the client + serialNumber information. + type: boolean + subject: + description: Subject defines the client certificate subject + details to add to the X-Forwarded-Tls-Client-Cert-Info header. + properties: + commonName: + description: CommonName defines whether to add the organizationalUnit + information into the subject. + type: boolean + country: + description: Country defines whether to add the country + information into the subject. + type: boolean + domainComponent: + description: DomainComponent defines whether to add the + domainComponent information into the subject. + type: boolean + locality: + description: Locality defines whether to add the locality + information into the subject. + type: boolean + organization: + description: Organization defines whether to add the organization + information into the subject. + type: boolean + organizationalUnit: + description: OrganizationalUnit defines whether to add + the organizationalUnit information into the subject. + type: boolean + province: + description: Province defines whether to add the province + information into the subject. + type: boolean + serialNumber: + description: SerialNumber defines whether to add the serialNumber + information into the subject. + type: boolean + type: object + type: object + pem: + description: PEM sets the X-Forwarded-Tls-Client-Cert header with + the certificate. + type: boolean + type: object + plugin: + additionalProperties: + x-kubernetes-preserve-unknown-fields: true + description: |- + Plugin defines the middleware plugin configuration. + More info: https://doc.traefik.io/traefik/plugins/ + type: object + rateLimit: + description: |- + RateLimit holds the rate limit configuration. + This middleware ensures that services will receive a fair amount of requests, and allows one to define what fair is. + More info: https://doc.traefik.io/traefik/v3.1/middlewares/http/ratelimit/ + properties: + average: + description: |- + Average is the maximum rate, by default in requests/s, allowed for the given source. + It defaults to 0, which means no rate limiting. + The rate is actually defined by dividing Average by Period. So for a rate below 1req/s, + one needs to define a Period larger than a second. + format: int64 + type: integer + burst: + description: |- + Burst is the maximum number of requests allowed to arrive in the same arbitrarily small period of time. + It defaults to 1. + format: int64 + type: integer + period: + anyOf: + - type: integer + - type: string + description: |- + Period, in combination with Average, defines the actual maximum rate, such as: + r = Average / Period. It defaults to a second. + x-kubernetes-int-or-string: true + sourceCriterion: + description: |- + SourceCriterion defines what criterion is used to group requests as originating from a common source. + If several strategies are defined at the same time, an error will be raised. + If none are set, the default is to use the request's remote address field (as an ipStrategy). + properties: + ipStrategy: + description: |- + IPStrategy holds the IP strategy configuration used by Traefik to determine the client IP. + More info: https://doc.traefik.io/traefik/v3.1/middlewares/http/ipallowlist/#ipstrategy + properties: + depth: + description: Depth tells Traefik to use the X-Forwarded-For + header and take the IP located at the depth position + (starting from the right). + type: integer + excludedIPs: + description: ExcludedIPs configures Traefik to scan the + X-Forwarded-For header and select the first IP not in + the list. + items: + type: string + type: array + type: object + requestHeaderName: + description: RequestHeaderName defines the name of the header + used to group incoming requests. + type: string + requestHost: + description: RequestHost defines whether to consider the request + Host as the source. + type: boolean + type: object + type: object + redirectRegex: + description: |- + RedirectRegex holds the redirect regex middleware configuration. + This middleware redirects a request using regex matching and replacement. + More info: https://doc.traefik.io/traefik/v3.1/middlewares/http/redirectregex/#regex + properties: + permanent: + description: Permanent defines whether the redirection is permanent + (301). + type: boolean + regex: + description: Regex defines the regex used to match and capture + elements from the request URL. + type: string + replacement: + description: Replacement defines how to modify the URL to have + the new target URL. + type: string + type: object + redirectScheme: + description: |- + RedirectScheme holds the redirect scheme middleware configuration. + This middleware redirects requests from a scheme/port to another. + More info: https://doc.traefik.io/traefik/v3.1/middlewares/http/redirectscheme/ + properties: + permanent: + description: Permanent defines whether the redirection is permanent + (301). + type: boolean + port: + description: Port defines the port of the new URL. + type: string + scheme: + description: Scheme defines the scheme of the new URL. + type: string + type: object + replacePath: + description: |- + ReplacePath holds the replace path middleware configuration. + This middleware replaces the path of the request URL and store the original path in an X-Replaced-Path header. + More info: https://doc.traefik.io/traefik/v3.1/middlewares/http/replacepath/ + properties: + path: + description: Path defines the path to use as replacement in the + request URL. + type: string + type: object + replacePathRegex: + description: |- + ReplacePathRegex holds the replace path regex middleware configuration. + This middleware replaces the path of a URL using regex matching and replacement. + More info: https://doc.traefik.io/traefik/v3.1/middlewares/http/replacepathregex/ + properties: + regex: + description: Regex defines the regular expression used to match + and capture the path from the request URL. + type: string + replacement: + description: Replacement defines the replacement path format, + which can include captured variables. + type: string + type: object + retry: + description: |- + Retry holds the retry middleware configuration. + This middleware reissues requests a given number of times to a backend server if that server does not reply. + As soon as the server answers, the middleware stops retrying, regardless of the response status. + More info: https://doc.traefik.io/traefik/v3.1/middlewares/http/retry/ + properties: + attempts: + description: Attempts defines how many times the request should + be retried. + type: integer + initialInterval: + anyOf: + - type: integer + - type: string + description: |- + InitialInterval defines the first wait time in the exponential backoff series. + The maximum interval is calculated as twice the initialInterval. + If unspecified, requests will be retried immediately. + The value of initialInterval should be provided in seconds or as a valid duration format, + see https://pkg.go.dev/time#ParseDuration. + x-kubernetes-int-or-string: true + type: object + stripPrefix: + description: |- + StripPrefix holds the strip prefix middleware configuration. + This middleware removes the specified prefixes from the URL path. + More info: https://doc.traefik.io/traefik/v3.1/middlewares/http/stripprefix/ + properties: + forceSlash: + description: |- + Deprecated: ForceSlash option is deprecated, please remove any usage of this option. + ForceSlash ensures that the resulting stripped path is not the empty string, by replacing it with / when necessary. + Default: true. + type: boolean + prefixes: + description: Prefixes defines the prefixes to strip from the request + URL. + items: + type: string + type: array + type: object + stripPrefixRegex: + description: |- + StripPrefixRegex holds the strip prefix regex middleware configuration. + This middleware removes the matching prefixes from the URL path. + More info: https://doc.traefik.io/traefik/v3.1/middlewares/http/stripprefixregex/ + properties: + regex: + description: Regex defines the regular expression to match the + path prefix from the request URL. + items: + type: string + type: array + type: object + type: object + required: + - metadata + - spec + type: object + served: true + storage: true diff --git a/charts/traefik/traefik/31.1.0/crds/traefik.io_middlewaretcps.yaml b/charts/traefik/traefik/31.1.0/crds/traefik.io_middlewaretcps.yaml new file mode 100644 index 000000000..f09e3d412 --- /dev/null +++ b/charts/traefik/traefik/31.1.0/crds/traefik.io_middlewaretcps.yaml @@ -0,0 +1,87 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.16.1 + name: middlewaretcps.traefik.io +spec: + group: traefik.io + names: + kind: MiddlewareTCP + listKind: MiddlewareTCPList + plural: middlewaretcps + singular: middlewaretcp + scope: Namespaced + versions: + - name: v1alpha1 + schema: + openAPIV3Schema: + description: |- + MiddlewareTCP is the CRD implementation of a Traefik TCP middleware. + More info: https://doc.traefik.io/traefik/v3.1/middlewares/overview/ + 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: MiddlewareTCPSpec defines the desired state of a MiddlewareTCP. + properties: + inFlightConn: + description: InFlightConn defines the InFlightConn middleware configuration. + properties: + amount: + description: |- + Amount defines the maximum amount of allowed simultaneous connections. + The middleware closes the connection if there are already amount connections opened. + format: int64 + type: integer + type: object + ipAllowList: + description: |- + IPAllowList defines the IPAllowList middleware configuration. + This middleware accepts/refuses connections based on the client IP. + More info: https://doc.traefik.io/traefik/v3.1/middlewares/tcp/ipallowlist/ + properties: + sourceRange: + description: SourceRange defines the allowed IPs (or ranges of + allowed IPs by using CIDR notation). + items: + type: string + type: array + type: object + ipWhiteList: + description: |- + IPWhiteList defines the IPWhiteList middleware configuration. + This middleware accepts/refuses connections based on the client IP. + Deprecated: please use IPAllowList instead. + More info: https://doc.traefik.io/traefik/v3.1/middlewares/tcp/ipwhitelist/ + properties: + sourceRange: + description: SourceRange defines the allowed IPs (or ranges of + allowed IPs by using CIDR notation). + items: + type: string + type: array + type: object + type: object + required: + - metadata + - spec + type: object + served: true + storage: true diff --git a/charts/traefik/traefik/31.1.0/crds/traefik.io_serverstransports.yaml b/charts/traefik/traefik/31.1.0/crds/traefik.io_serverstransports.yaml new file mode 100644 index 000000000..a447c97f1 --- /dev/null +++ b/charts/traefik/traefik/31.1.0/crds/traefik.io_serverstransports.yaml @@ -0,0 +1,139 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.16.1 + name: serverstransports.traefik.io +spec: + group: traefik.io + names: + kind: ServersTransport + listKind: ServersTransportList + plural: serverstransports + singular: serverstransport + scope: Namespaced + versions: + - name: v1alpha1 + schema: + openAPIV3Schema: + description: |- + ServersTransport is the CRD implementation of a ServersTransport. + If no serversTransport is specified, the default@internal will be used. + The default@internal serversTransport is created from the static configuration. + More info: https://doc.traefik.io/traefik/v3.1/routing/services/#serverstransport_1 + 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: ServersTransportSpec defines the desired state of a ServersTransport. + properties: + certificatesSecrets: + description: CertificatesSecrets defines a list of secret storing + client certificates for mTLS. + items: + type: string + type: array + disableHTTP2: + description: DisableHTTP2 disables HTTP/2 for connections with backend + servers. + type: boolean + forwardingTimeouts: + description: ForwardingTimeouts defines the timeouts for requests + forwarded to the backend servers. + properties: + dialTimeout: + anyOf: + - type: integer + - type: string + description: DialTimeout is the amount of time to wait until a + connection to a backend server can be established. + x-kubernetes-int-or-string: true + idleConnTimeout: + anyOf: + - type: integer + - type: string + description: IdleConnTimeout is the maximum period for which an + idle HTTP keep-alive connection will remain open before closing + itself. + x-kubernetes-int-or-string: true + pingTimeout: + anyOf: + - type: integer + - type: string + description: PingTimeout is the timeout after which the HTTP/2 + connection will be closed if a response to ping is not received. + x-kubernetes-int-or-string: true + readIdleTimeout: + anyOf: + - type: integer + - type: string + description: ReadIdleTimeout is the timeout after which a health + check using ping frame will be carried out if no frame is received + on the HTTP/2 connection. + x-kubernetes-int-or-string: true + responseHeaderTimeout: + anyOf: + - type: integer + - type: string + description: ResponseHeaderTimeout is the amount of time to wait + for a server's response headers after fully writing the request + (including its body, if any). + x-kubernetes-int-or-string: true + type: object + insecureSkipVerify: + description: InsecureSkipVerify disables SSL certificate verification. + type: boolean + maxIdleConnsPerHost: + description: MaxIdleConnsPerHost controls the maximum idle (keep-alive) + to keep per-host. + type: integer + peerCertURI: + description: PeerCertURI defines the peer cert URI used to match against + SAN URI during the peer certificate verification. + type: string + rootCAsSecrets: + description: RootCAsSecrets defines a list of CA secret used to validate + self-signed certificate. + items: + type: string + type: array + serverName: + description: ServerName defines the server name used to contact the + server. + type: string + spiffe: + description: Spiffe defines the SPIFFE configuration. + properties: + ids: + description: IDs defines the allowed SPIFFE IDs (takes precedence + over the SPIFFE TrustDomain). + items: + type: string + type: array + trustDomain: + description: TrustDomain defines the allowed SPIFFE trust domain. + type: string + type: object + type: object + required: + - metadata + - spec + type: object + served: true + storage: true diff --git a/charts/traefik/traefik/31.1.0/crds/traefik.io_serverstransporttcps.yaml b/charts/traefik/traefik/31.1.0/crds/traefik.io_serverstransporttcps.yaml new file mode 100644 index 000000000..319044709 --- /dev/null +++ b/charts/traefik/traefik/31.1.0/crds/traefik.io_serverstransporttcps.yaml @@ -0,0 +1,120 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.16.1 + name: serverstransporttcps.traefik.io +spec: + group: traefik.io + names: + kind: ServersTransportTCP + listKind: ServersTransportTCPList + plural: serverstransporttcps + singular: serverstransporttcp + scope: Namespaced + versions: + - name: v1alpha1 + schema: + openAPIV3Schema: + description: |- + ServersTransportTCP is the CRD implementation of a TCPServersTransport. + If no tcpServersTransport is specified, a default one named default@internal will be used. + The default@internal tcpServersTransport can be configured in the static configuration. + More info: https://doc.traefik.io/traefik/v3.1/routing/services/#serverstransport_3 + 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: ServersTransportTCPSpec defines the desired state of a ServersTransportTCP. + properties: + dialKeepAlive: + anyOf: + - type: integer + - type: string + description: DialKeepAlive is the interval between keep-alive probes + for an active network connection. If zero, keep-alive probes are + sent with a default value (currently 15 seconds), if supported by + the protocol and operating system. Network protocols or operating + systems that do not support keep-alives ignore this field. If negative, + keep-alive probes are disabled. + x-kubernetes-int-or-string: true + dialTimeout: + anyOf: + - type: integer + - type: string + description: DialTimeout is the amount of time to wait until a connection + to a backend server can be established. + x-kubernetes-int-or-string: true + terminationDelay: + anyOf: + - type: integer + - type: string + description: TerminationDelay defines the delay to wait before fully + terminating the connection, after one connected peer has closed + its writing capability. + x-kubernetes-int-or-string: true + tls: + description: TLS defines the TLS configuration + properties: + certificatesSecrets: + description: CertificatesSecrets defines a list of secret storing + client certificates for mTLS. + items: + type: string + type: array + insecureSkipVerify: + description: InsecureSkipVerify disables TLS certificate verification. + type: boolean + peerCertURI: + description: |- + MaxIdleConnsPerHost controls the maximum idle (keep-alive) to keep per-host. + PeerCertURI defines the peer cert URI used to match against SAN URI during the peer certificate verification. + type: string + rootCAsSecrets: + description: RootCAsSecrets defines a list of CA secret used to + validate self-signed certificates. + items: + type: string + type: array + serverName: + description: ServerName defines the server name used to contact + the server. + type: string + spiffe: + description: Spiffe defines the SPIFFE configuration. + properties: + ids: + description: IDs defines the allowed SPIFFE IDs (takes precedence + over the SPIFFE TrustDomain). + items: + type: string + type: array + trustDomain: + description: TrustDomain defines the allowed SPIFFE trust + domain. + type: string + type: object + type: object + type: object + required: + - metadata + - spec + type: object + served: true + storage: true diff --git a/charts/traefik/traefik/31.1.0/crds/traefik.io_tlsoptions.yaml b/charts/traefik/traefik/31.1.0/crds/traefik.io_tlsoptions.yaml new file mode 100644 index 000000000..932f95811 --- /dev/null +++ b/charts/traefik/traefik/31.1.0/crds/traefik.io_tlsoptions.yaml @@ -0,0 +1,114 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.16.1 + name: tlsoptions.traefik.io +spec: + group: traefik.io + names: + kind: TLSOption + listKind: TLSOptionList + plural: tlsoptions + singular: tlsoption + scope: Namespaced + versions: + - name: v1alpha1 + schema: + openAPIV3Schema: + description: |- + TLSOption is the CRD implementation of a Traefik TLS Option, allowing to configure some parameters of the TLS connection. + More info: https://doc.traefik.io/traefik/v3.1/https/tls/#tls-options + 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: TLSOptionSpec defines the desired state of a TLSOption. + properties: + alpnProtocols: + description: |- + ALPNProtocols defines the list of supported application level protocols for the TLS handshake, in order of preference. + More info: https://doc.traefik.io/traefik/v3.1/https/tls/#alpn-protocols + items: + type: string + type: array + cipherSuites: + description: |- + CipherSuites defines the list of supported cipher suites for TLS versions up to TLS 1.2. + More info: https://doc.traefik.io/traefik/v3.1/https/tls/#cipher-suites + items: + type: string + type: array + clientAuth: + description: ClientAuth defines the server's policy for TLS Client + Authentication. + properties: + clientAuthType: + description: ClientAuthType defines the client authentication + type to apply. + enum: + - NoClientCert + - RequestClientCert + - RequireAnyClientCert + - VerifyClientCertIfGiven + - RequireAndVerifyClientCert + type: string + secretNames: + description: SecretNames defines the names of the referenced Kubernetes + Secret storing certificate details. + items: + type: string + type: array + type: object + curvePreferences: + description: |- + CurvePreferences defines the preferred elliptic curves in a specific order. + More info: https://doc.traefik.io/traefik/v3.1/https/tls/#curve-preferences + items: + type: string + type: array + maxVersion: + description: |- + MaxVersion defines the maximum TLS version that Traefik will accept. + Possible values: VersionTLS10, VersionTLS11, VersionTLS12, VersionTLS13. + Default: None. + type: string + minVersion: + description: |- + MinVersion defines the minimum TLS version that Traefik will accept. + Possible values: VersionTLS10, VersionTLS11, VersionTLS12, VersionTLS13. + Default: VersionTLS10. + type: string + preferServerCipherSuites: + description: |- + PreferServerCipherSuites defines whether the server chooses a cipher suite among his own instead of among the client's. + It is enabled automatically when minVersion or maxVersion is set. + Deprecated: https://github.com/golang/go/issues/45430 + type: boolean + sniStrict: + description: SniStrict defines whether Traefik allows connections + from clients connections that do not specify a server_name extension. + type: boolean + type: object + required: + - metadata + - spec + type: object + served: true + storage: true diff --git a/charts/traefik/traefik/31.1.0/crds/traefik.io_tlsstores.yaml b/charts/traefik/traefik/31.1.0/crds/traefik.io_tlsstores.yaml new file mode 100644 index 000000000..37afedc02 --- /dev/null +++ b/charts/traefik/traefik/31.1.0/crds/traefik.io_tlsstores.yaml @@ -0,0 +1,97 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.16.1 + name: tlsstores.traefik.io +spec: + group: traefik.io + names: + kind: TLSStore + listKind: TLSStoreList + plural: tlsstores + singular: tlsstore + scope: Namespaced + versions: + - name: v1alpha1 + schema: + openAPIV3Schema: + description: |- + TLSStore is the CRD implementation of a Traefik TLS Store. + For the time being, only the TLSStore named default is supported. + This means that you cannot have two stores that are named default in different Kubernetes namespaces. + More info: https://doc.traefik.io/traefik/v3.1/https/tls/#certificates-stores + 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: TLSStoreSpec defines the desired state of a TLSStore. + properties: + certificates: + description: Certificates is a list of secret names, each secret holding + a key/certificate pair to add to the store. + items: + description: Certificate holds a secret name for the TLSStore resource. + properties: + secretName: + description: SecretName is the name of the referenced Kubernetes + Secret to specify the certificate details. + type: string + required: + - secretName + type: object + type: array + defaultCertificate: + description: DefaultCertificate defines the default certificate configuration. + properties: + secretName: + description: SecretName is the name of the referenced Kubernetes + Secret to specify the certificate details. + type: string + required: + - secretName + type: object + defaultGeneratedCert: + description: DefaultGeneratedCert defines the default generated certificate + configuration. + properties: + domain: + description: Domain is the domain definition for the DefaultCertificate. + properties: + main: + description: Main defines the main domain name. + type: string + sans: + description: SANs defines the subject alternative domain names. + items: + type: string + type: array + type: object + resolver: + description: Resolver is the name of the resolver that will be + used to issue the DefaultCertificate. + type: string + type: object + type: object + required: + - metadata + - spec + type: object + served: true + storage: true diff --git a/charts/traefik/traefik/31.1.0/crds/traefik.io_traefikservices.yaml b/charts/traefik/traefik/31.1.0/crds/traefik.io_traefikservices.yaml new file mode 100644 index 000000000..1e1b279d5 --- /dev/null +++ b/charts/traefik/traefik/31.1.0/crds/traefik.io_traefikservices.yaml @@ -0,0 +1,639 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.16.1 + name: traefikservices.traefik.io +spec: + group: traefik.io + names: + kind: TraefikService + listKind: TraefikServiceList + plural: traefikservices + singular: traefikservice + scope: Namespaced + versions: + - name: v1alpha1 + schema: + openAPIV3Schema: + description: |- + TraefikService is the CRD implementation of a Traefik Service. + TraefikService object allows to: + - Apply weight to Services on load-balancing + - Mirror traffic on services + More info: https://doc.traefik.io/traefik/v3.1/routing/providers/kubernetes-crd/#kind-traefikservice + 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: TraefikServiceSpec defines the desired state of a TraefikService. + properties: + mirroring: + description: Mirroring defines the Mirroring service configuration. + properties: + healthCheck: + description: Healthcheck defines health checks for ExternalName + services. + properties: + followRedirects: + description: |- + FollowRedirects defines whether redirects should be followed during the health check calls. + Default: true + type: boolean + headers: + additionalProperties: + type: string + description: Headers defines custom headers to be sent to + the health check endpoint. + type: object + hostname: + description: Hostname defines the value of hostname in the + Host header of the health check request. + type: string + interval: + anyOf: + - type: integer + - type: string + description: |- + Interval defines the frequency of the health check calls. + Default: 30s + x-kubernetes-int-or-string: true + method: + description: Method defines the healthcheck method. + type: string + mode: + description: |- + Mode defines the health check mode. + If defined to grpc, will use the gRPC health check protocol to probe the server. + Default: http + type: string + path: + description: Path defines the server URL path for the health + check endpoint. + type: string + port: + description: Port defines the server URL port for the health + check endpoint. + type: integer + scheme: + description: Scheme replaces the server URL scheme for the + health check endpoint. + type: string + status: + description: Status defines the expected HTTP status code + of the response to the health check request. + type: integer + timeout: + anyOf: + - type: integer + - type: string + description: |- + Timeout defines the maximum duration Traefik will wait for a health check request before considering the server unhealthy. + Default: 5s + x-kubernetes-int-or-string: true + type: object + kind: + description: Kind defines the kind of the Service. + enum: + - Service + - TraefikService + type: string + maxBodySize: + description: |- + MaxBodySize defines the maximum size allowed for the body of the request. + If the body is larger, the request is not mirrored. + Default value is -1, which means unlimited size. + format: int64 + type: integer + mirrors: + description: Mirrors defines the list of mirrors where Traefik + will duplicate the traffic. + items: + description: MirrorService holds the mirror configuration. + properties: + healthCheck: + description: Healthcheck defines health checks for ExternalName + services. + properties: + followRedirects: + description: |- + FollowRedirects defines whether redirects should be followed during the health check calls. + Default: true + type: boolean + headers: + additionalProperties: + type: string + description: Headers defines custom headers to be sent + to the health check endpoint. + type: object + hostname: + description: Hostname defines the value of hostname + in the Host header of the health check request. + type: string + interval: + anyOf: + - type: integer + - type: string + description: |- + Interval defines the frequency of the health check calls. + Default: 30s + x-kubernetes-int-or-string: true + method: + description: Method defines the healthcheck method. + type: string + mode: + description: |- + Mode defines the health check mode. + If defined to grpc, will use the gRPC health check protocol to probe the server. + Default: http + type: string + path: + description: Path defines the server URL path for the + health check endpoint. + type: string + port: + description: Port defines the server URL port for the + health check endpoint. + type: integer + scheme: + description: Scheme replaces the server URL scheme for + the health check endpoint. + type: string + status: + description: Status defines the expected HTTP status + code of the response to the health check request. + type: integer + timeout: + anyOf: + - type: integer + - type: string + description: |- + Timeout defines the maximum duration Traefik will wait for a health check request before considering the server unhealthy. + Default: 5s + x-kubernetes-int-or-string: true + type: object + kind: + description: Kind defines the kind of the Service. + enum: + - Service + - TraefikService + type: string + name: + description: |- + Name defines the name of the referenced Kubernetes Service or TraefikService. + The differentiation between the two is specified in the Kind field. + type: string + namespace: + description: Namespace defines the namespace of the referenced + Kubernetes Service or TraefikService. + type: string + nativeLB: + description: |- + NativeLB controls, when creating the load-balancer, + whether the LB's children are directly the pods IPs or if the only child is the Kubernetes Service clusterIP. + The Kubernetes Service itself does load-balance to the pods. + By default, NativeLB is false. + type: boolean + nodePortLB: + description: |- + NodePortLB controls, when creating the load-balancer, + whether the LB's children are directly the nodes internal IPs using the nodePort when the service type is NodePort. + It allows services to be reachable when Traefik runs externally from the Kubernetes cluster but within the same network of the nodes. + By default, NodePortLB is false. + type: boolean + passHostHeader: + description: |- + PassHostHeader defines whether the client Host header is forwarded to the upstream Kubernetes Service. + By default, passHostHeader is true. + type: boolean + percent: + description: |- + Percent defines the part of the traffic to mirror. + Supported values: 0 to 100. + type: integer + port: + anyOf: + - type: integer + - type: string + description: |- + Port defines the port of a Kubernetes Service. + This can be a reference to a named port. + x-kubernetes-int-or-string: true + responseForwarding: + description: ResponseForwarding defines how Traefik forwards + the response from the upstream Kubernetes Service to the + client. + properties: + flushInterval: + description: |- + FlushInterval defines the interval, in milliseconds, in between flushes to the client while copying the response body. + A negative value means to flush immediately after each write to the client. + This configuration is ignored when ReverseProxy recognizes a response as a streaming response; + for such responses, writes are flushed to the client immediately. + Default: 100ms + type: string + type: object + scheme: + description: |- + Scheme defines the scheme to use for the request to the upstream Kubernetes Service. + It defaults to https when Kubernetes Service port is 443, http otherwise. + type: string + serversTransport: + description: |- + ServersTransport defines the name of ServersTransport resource to use. + It allows to configure the transport between Traefik and your servers. + Can only be used on a Kubernetes Service. + type: string + sticky: + description: |- + Sticky defines the sticky sessions configuration. + More info: https://doc.traefik.io/traefik/v3.1/routing/services/#sticky-sessions + properties: + cookie: + description: Cookie defines the sticky cookie configuration. + properties: + httpOnly: + description: HTTPOnly defines whether the cookie + can be accessed by client-side APIs, such as JavaScript. + type: boolean + maxAge: + description: |- + MaxAge indicates the number of seconds until the cookie expires. + When set to a negative number, the cookie expires immediately. + When set to zero, the cookie never expires. + type: integer + name: + description: Name defines the Cookie name. + type: string + sameSite: + description: |- + SameSite defines the same site policy. + More info: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie/SameSite + type: string + secure: + description: Secure defines whether the cookie can + only be transmitted over an encrypted connection + (i.e. HTTPS). + type: boolean + type: object + type: object + strategy: + description: |- + Strategy defines the load balancing strategy between the servers. + RoundRobin is the only supported value at the moment. + type: string + weight: + description: |- + Weight defines the weight and should only be specified when Name references a TraefikService object + (and to be precise, one that embeds a Weighted Round Robin). + type: integer + required: + - name + type: object + type: array + name: + description: |- + Name defines the name of the referenced Kubernetes Service or TraefikService. + The differentiation between the two is specified in the Kind field. + type: string + namespace: + description: Namespace defines the namespace of the referenced + Kubernetes Service or TraefikService. + type: string + nativeLB: + description: |- + NativeLB controls, when creating the load-balancer, + whether the LB's children are directly the pods IPs or if the only child is the Kubernetes Service clusterIP. + The Kubernetes Service itself does load-balance to the pods. + By default, NativeLB is false. + type: boolean + nodePortLB: + description: |- + NodePortLB controls, when creating the load-balancer, + whether the LB's children are directly the nodes internal IPs using the nodePort when the service type is NodePort. + It allows services to be reachable when Traefik runs externally from the Kubernetes cluster but within the same network of the nodes. + By default, NodePortLB is false. + type: boolean + passHostHeader: + description: |- + PassHostHeader defines whether the client Host header is forwarded to the upstream Kubernetes Service. + By default, passHostHeader is true. + type: boolean + port: + anyOf: + - type: integer + - type: string + description: |- + Port defines the port of a Kubernetes Service. + This can be a reference to a named port. + x-kubernetes-int-or-string: true + responseForwarding: + description: ResponseForwarding defines how Traefik forwards the + response from the upstream Kubernetes Service to the client. + properties: + flushInterval: + description: |- + FlushInterval defines the interval, in milliseconds, in between flushes to the client while copying the response body. + A negative value means to flush immediately after each write to the client. + This configuration is ignored when ReverseProxy recognizes a response as a streaming response; + for such responses, writes are flushed to the client immediately. + Default: 100ms + type: string + type: object + scheme: + description: |- + Scheme defines the scheme to use for the request to the upstream Kubernetes Service. + It defaults to https when Kubernetes Service port is 443, http otherwise. + type: string + serversTransport: + description: |- + ServersTransport defines the name of ServersTransport resource to use. + It allows to configure the transport between Traefik and your servers. + Can only be used on a Kubernetes Service. + type: string + sticky: + description: |- + Sticky defines the sticky sessions configuration. + More info: https://doc.traefik.io/traefik/v3.1/routing/services/#sticky-sessions + properties: + cookie: + description: Cookie defines the sticky cookie configuration. + properties: + httpOnly: + description: HTTPOnly defines whether the cookie can be + accessed by client-side APIs, such as JavaScript. + type: boolean + maxAge: + description: |- + MaxAge indicates the number of seconds until the cookie expires. + When set to a negative number, the cookie expires immediately. + When set to zero, the cookie never expires. + type: integer + name: + description: Name defines the Cookie name. + type: string + sameSite: + description: |- + SameSite defines the same site policy. + More info: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie/SameSite + type: string + secure: + description: Secure defines whether the cookie can only + be transmitted over an encrypted connection (i.e. HTTPS). + type: boolean + type: object + type: object + strategy: + description: |- + Strategy defines the load balancing strategy between the servers. + RoundRobin is the only supported value at the moment. + type: string + weight: + description: |- + Weight defines the weight and should only be specified when Name references a TraefikService object + (and to be precise, one that embeds a Weighted Round Robin). + type: integer + required: + - name + type: object + weighted: + description: Weighted defines the Weighted Round Robin configuration. + properties: + services: + description: Services defines the list of Kubernetes Service and/or + TraefikService to load-balance, with weight. + items: + description: Service defines an upstream HTTP service to proxy + traffic to. + properties: + healthCheck: + description: Healthcheck defines health checks for ExternalName + services. + properties: + followRedirects: + description: |- + FollowRedirects defines whether redirects should be followed during the health check calls. + Default: true + type: boolean + headers: + additionalProperties: + type: string + description: Headers defines custom headers to be sent + to the health check endpoint. + type: object + hostname: + description: Hostname defines the value of hostname + in the Host header of the health check request. + type: string + interval: + anyOf: + - type: integer + - type: string + description: |- + Interval defines the frequency of the health check calls. + Default: 30s + x-kubernetes-int-or-string: true + method: + description: Method defines the healthcheck method. + type: string + mode: + description: |- + Mode defines the health check mode. + If defined to grpc, will use the gRPC health check protocol to probe the server. + Default: http + type: string + path: + description: Path defines the server URL path for the + health check endpoint. + type: string + port: + description: Port defines the server URL port for the + health check endpoint. + type: integer + scheme: + description: Scheme replaces the server URL scheme for + the health check endpoint. + type: string + status: + description: Status defines the expected HTTP status + code of the response to the health check request. + type: integer + timeout: + anyOf: + - type: integer + - type: string + description: |- + Timeout defines the maximum duration Traefik will wait for a health check request before considering the server unhealthy. + Default: 5s + x-kubernetes-int-or-string: true + type: object + kind: + description: Kind defines the kind of the Service. + enum: + - Service + - TraefikService + type: string + name: + description: |- + Name defines the name of the referenced Kubernetes Service or TraefikService. + The differentiation between the two is specified in the Kind field. + type: string + namespace: + description: Namespace defines the namespace of the referenced + Kubernetes Service or TraefikService. + type: string + nativeLB: + description: |- + NativeLB controls, when creating the load-balancer, + whether the LB's children are directly the pods IPs or if the only child is the Kubernetes Service clusterIP. + The Kubernetes Service itself does load-balance to the pods. + By default, NativeLB is false. + type: boolean + nodePortLB: + description: |- + NodePortLB controls, when creating the load-balancer, + whether the LB's children are directly the nodes internal IPs using the nodePort when the service type is NodePort. + It allows services to be reachable when Traefik runs externally from the Kubernetes cluster but within the same network of the nodes. + By default, NodePortLB is false. + type: boolean + passHostHeader: + description: |- + PassHostHeader defines whether the client Host header is forwarded to the upstream Kubernetes Service. + By default, passHostHeader is true. + type: boolean + port: + anyOf: + - type: integer + - type: string + description: |- + Port defines the port of a Kubernetes Service. + This can be a reference to a named port. + x-kubernetes-int-or-string: true + responseForwarding: + description: ResponseForwarding defines how Traefik forwards + the response from the upstream Kubernetes Service to the + client. + properties: + flushInterval: + description: |- + FlushInterval defines the interval, in milliseconds, in between flushes to the client while copying the response body. + A negative value means to flush immediately after each write to the client. + This configuration is ignored when ReverseProxy recognizes a response as a streaming response; + for such responses, writes are flushed to the client immediately. + Default: 100ms + type: string + type: object + scheme: + description: |- + Scheme defines the scheme to use for the request to the upstream Kubernetes Service. + It defaults to https when Kubernetes Service port is 443, http otherwise. + type: string + serversTransport: + description: |- + ServersTransport defines the name of ServersTransport resource to use. + It allows to configure the transport between Traefik and your servers. + Can only be used on a Kubernetes Service. + type: string + sticky: + description: |- + Sticky defines the sticky sessions configuration. + More info: https://doc.traefik.io/traefik/v3.1/routing/services/#sticky-sessions + properties: + cookie: + description: Cookie defines the sticky cookie configuration. + properties: + httpOnly: + description: HTTPOnly defines whether the cookie + can be accessed by client-side APIs, such as JavaScript. + type: boolean + maxAge: + description: |- + MaxAge indicates the number of seconds until the cookie expires. + When set to a negative number, the cookie expires immediately. + When set to zero, the cookie never expires. + type: integer + name: + description: Name defines the Cookie name. + type: string + sameSite: + description: |- + SameSite defines the same site policy. + More info: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie/SameSite + type: string + secure: + description: Secure defines whether the cookie can + only be transmitted over an encrypted connection + (i.e. HTTPS). + type: boolean + type: object + type: object + strategy: + description: |- + Strategy defines the load balancing strategy between the servers. + RoundRobin is the only supported value at the moment. + type: string + weight: + description: |- + Weight defines the weight and should only be specified when Name references a TraefikService object + (and to be precise, one that embeds a Weighted Round Robin). + type: integer + required: + - name + type: object + type: array + sticky: + description: |- + Sticky defines whether sticky sessions are enabled. + More info: https://doc.traefik.io/traefik/v3.1/routing/providers/kubernetes-crd/#stickiness-and-load-balancing + properties: + cookie: + description: Cookie defines the sticky cookie configuration. + properties: + httpOnly: + description: HTTPOnly defines whether the cookie can be + accessed by client-side APIs, such as JavaScript. + type: boolean + maxAge: + description: |- + MaxAge indicates the number of seconds until the cookie expires. + When set to a negative number, the cookie expires immediately. + When set to zero, the cookie never expires. + type: integer + name: + description: Name defines the Cookie name. + type: string + sameSite: + description: |- + SameSite defines the same site policy. + More info: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie/SameSite + type: string + secure: + description: Secure defines whether the cookie can only + be transmitted over an encrypted connection (i.e. HTTPS). + type: boolean + type: object + type: object + type: object + type: object + required: + - metadata + - spec + type: object + served: true + storage: true diff --git a/charts/traefik/traefik/31.1.0/templates/NOTES.txt b/charts/traefik/traefik/31.1.0/templates/NOTES.txt new file mode 100644 index 000000000..a1a10bfb3 --- /dev/null +++ b/charts/traefik/traefik/31.1.0/templates/NOTES.txt @@ -0,0 +1,36 @@ + + +{{ .Release.Name }} with {{ .Values.image.registry }}/{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }} has been deployed successfully on {{ template "traefik.namespace" . }} namespace ! + +{{- if .Values.persistence }} +{{- if and .Values.persistence.enabled (empty .Values.deployment.initContainer)}} + +🚨 When enabling persistence for certificates, permissions on acme.json can be +lost when Traefik restarts. You can ensure correct permissions with an +initContainer. See https://github.com/traefik/traefik-helm-chart/blob/master/EXAMPLES.md#use-traefik-native-lets-encrypt-integration-without-cert-manager +for more info. 🚨 + +{{- end }} +{{- end }} +{{- with .Values.providers.kubernetesCRD.labelSelector }} + {{- $labelsApplied := include "traefik.labels" $ }} + {{- $labelSelectors := regexSplit "," . -1 }} + {{- range $labelSelectors }} + {{- $labelSelectorRaw := regexSplit "=" . -1 }} + {{- $labelSelector := printf "%s: %s" (first $labelSelectorRaw) (last $labelSelectorRaw) }} + {{- if not (contains $labelSelector $labelsApplied) }} +🚨 Resources populated with this chart don't match with labelSelector `{{.}}` applied on kubernetesCRD provider 🚨 + {{- end }} + {{- end }} +{{- end }} +{{- with .Values.providers.kubernetesIngress.labelSelector }} + {{- $labelsApplied := include "traefik.labels" $ }} + {{- $labelSelectors := regexSplit "," . -1 }} + {{- range $labelSelectors }} + {{- $labelSelectorRaw := regexSplit "=" . -1 }} + {{- $labelSelector := printf "%s: %s" (first $labelSelectorRaw) (last $labelSelectorRaw) }} + {{- if not (contains $labelSelector $labelsApplied) }} +🚨 Resources populated with this chart don't match with labelSelector `{{.}}` applied on kubernetesIngress provider 🚨 + {{- end }} + {{- end }} +{{- end }} diff --git a/charts/traefik/traefik/31.1.0/templates/_helpers.tpl b/charts/traefik/traefik/31.1.0/templates/_helpers.tpl new file mode 100644 index 000000000..2183f84ab --- /dev/null +++ b/charts/traefik/traefik/31.1.0/templates/_helpers.tpl @@ -0,0 +1,161 @@ +{{/* vim: set filetype=mustache: */}} + +{{/* +Expand the name of the chart. +*/}} +{{- define "traefik.name" -}} +{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}} +{{- end -}} + +{{/* +Create chart name and version as used by the chart label. +*/}} +{{- define "traefik.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}} +{{- end -}} + +{{/* +Create the chart image name. +*/}} +{{- define "traefik.image-name" -}} +{{- printf "%s/%s:%s" .Values.image.registry .Values.image.repository (.Values.image.tag | default .Chart.AppVersion) }} +{{- 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 "traefik.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 -}} + +{{/* +Allow customization of the instance label value. +*/}} +{{- define "traefik.instance-name" -}} +{{- default (printf "%s-%s" .Release.Name .Release.Namespace) .Values.instanceLabelOverride | trunc 63 | trimSuffix "-" -}} +{{- end -}} + +{{/* Shared labels used for selector*/}} +{{/* This is an immutable field: this should not change between upgrade */}} +{{- define "traefik.labelselector" -}} +app.kubernetes.io/name: {{ template "traefik.name" . }} +app.kubernetes.io/instance: {{ template "traefik.instance-name" . }} +{{- end }} + +{{/* Shared labels used in metada */}} +{{- define "traefik.labels" -}} +{{ include "traefik.labelselector" . }} +helm.sh/chart: {{ template "traefik.chart" . }} +app.kubernetes.io/managed-by: {{ .Release.Service }} +{{- with .Values.commonLabels }} +{{ toYaml . }} +{{- end }} +{{- end }} + +{{/* +Construct the namespace for all namespaced resources +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +Preserve the default behavior of the Release namespace if no override is provided +*/}} +{{- define "traefik.namespace" -}} +{{- if .Values.namespaceOverride -}} +{{- .Values.namespaceOverride | trunc 63 | trimSuffix "-" -}} +{{- else -}} +{{- .Release.Namespace -}} +{{- end -}} +{{- end -}} + +{{/* +The name of the service account to use +*/}} +{{- define "traefik.serviceAccountName" -}} +{{- default (include "traefik.fullname" .) .Values.serviceAccount.name -}} +{{- end -}} + +{{/* +The name of the ClusterRole and ClusterRoleBinding to use. +Adds the namespace to name to prevent duplicate resource names when there +are multiple namespaced releases with the same release name. +*/}} +{{- define "traefik.clusterRoleName" -}} +{{- (printf "%s-%s" (include "traefik.fullname" .) .Release.Namespace) | trunc 63 | trimSuffix "-" }} +{{- end -}} + +{{/* +Construct the path for the providers.kubernetesingress.ingressendpoint.publishedservice. +By convention this will simply use the / to match the name of the +service generated. +Users can provide an override for an explicit service they want bound via `.Values.providers.kubernetesIngress.publishedService.pathOverride` +*/}} +{{- define "providers.kubernetesIngress.publishedServicePath" -}} +{{- $defServiceName := printf "%s/%s" .Release.Namespace (include "traefik.fullname" .) -}} +{{- $servicePath := default $defServiceName .Values.providers.kubernetesIngress.publishedService.pathOverride }} +{{- print $servicePath | trimSuffix "-" -}} +{{- end -}} + +{{/* +Construct a comma-separated list of whitelisted namespaces +*/}} +{{- define "providers.kubernetesCRD.namespaces" -}} +{{- default (include "traefik.namespace" .) (join "," .Values.providers.kubernetesCRD.namespaces) }} +{{- end -}} +{{- define "providers.kubernetesGateway.namespaces" -}} +{{- default (include "traefik.namespace" .) (join "," .Values.providers.kubernetesGateway.namespaces) }} +{{- end -}} +{{- define "providers.kubernetesIngress.namespaces" -}} +{{- default (include "traefik.namespace" .) (join "," .Values.providers.kubernetesIngress.namespaces) }} +{{- end -}} + +{{/* +Renders a complete tree, even values that contains template. +*/}} +{{- define "traefik.render" -}} + {{- if typeIs "string" .value }} + {{- tpl .value .context }} + {{ else }} + {{- tpl (.value | toYaml) .context }} + {{- end }} +{{- end -}} + +{{- define "imageVersion" -}} +{{/* +Traefik hub is based on v3.1 (v3.0 before v3.3.1) of traefik proxy, so this is a hack to avoid to much complexity in RBAC management which are +based on semverCompare +*/}} +{{- if $.Values.hub.token -}} +{{ if and (regexMatch "v[0-9]+.[0-9]+.[0-9]+" (default "" $.Values.image.tag)) (semverCompare "=3.1.2-0" $version) }} + - "--providers.kubernetescrd.disableClusterScopeResources=true" + {{- end }} + {{- if .Values.providers.kubernetesCRD.nativeLBByDefault }} + - "--providers.kubernetescrd.nativeLBByDefault=true" + {{- end }} + {{- end }} + {{- if .Values.providers.kubernetesIngress.enabled }} + - "--providers.kubernetesingress" + {{- if .Values.providers.kubernetesIngress.allowExternalNameServices }} + - "--providers.kubernetesingress.allowExternalNameServices=true" + {{- end }} + {{- if .Values.providers.kubernetesIngress.allowEmptyServices }} + - "--providers.kubernetesingress.allowEmptyServices=true" + {{- end }} + {{- if and .Values.service.enabled .Values.providers.kubernetesIngress.publishedService.enabled }} + - "--providers.kubernetesingress.ingressendpoint.publishedservice={{ template "providers.kubernetesIngress.publishedServicePath" . }}" + {{- end }} + {{- if .Values.providers.kubernetesIngress.labelSelector }} + - "--providers.kubernetesingress.labelSelector={{ .Values.providers.kubernetesIngress.labelSelector }}" + {{- end }} + {{- if .Values.providers.kubernetesIngress.ingressClass }} + - "--providers.kubernetesingress.ingressClass={{ .Values.providers.kubernetesIngress.ingressClass }}" + {{- end }} + {{- if .Values.rbac.namespaced }} + {{- if semverCompare "<3.1.2-0" $version }} + - "--providers.kubernetesingress.disableIngressClassLookup=true" + {{- else }} + - "--providers.kubernetesingress.disableClusterScopeResources=true" + {{- end }} + {{- end }} + {{- if .Values.providers.kubernetesIngress.nativeLBByDefault }} + - "--providers.kubernetesingress.nativeLBByDefault=true" + {{- end }} + {{- end }} + {{- if .Values.experimental.kubernetesGateway.enabled }} + - "--experimental.kubernetesgateway" + {{- end }} + {{- with .Values.providers.kubernetesCRD }} + {{- if (and .enabled (or .namespaces (and $.Values.rbac.enabled $.Values.rbac.namespaced))) }} + - "--providers.kubernetescrd.namespaces={{ template "providers.kubernetesCRD.namespaces" $ }}" + {{- end }} + {{- end }} + {{- with .Values.providers.kubernetesGateway }} + {{- if .enabled }} + - "--providers.kubernetesgateway" + {{- if or .namespaces (and $.Values.rbac.enabled $.Values.rbac.namespaced) }} + - "--providers.kubernetesgateway.namespaces={{ template "providers.kubernetesGateway.namespaces" $ }}" + {{- end }} + {{- if .experimentalChannel }} + - "--providers.kubernetesgateway.experimentalchannel=true" + {{- end }} + {{- with .labelselector }} + - "--providers.kubernetesgateway.labelselector={{ . }}" + {{- end }} + {{- end }} + {{- end }} + {{- with .Values.providers.kubernetesIngress }} + {{- if (and .enabled (or .namespaces (and $.Values.rbac.enabled $.Values.rbac.namespaced))) }} + - "--providers.kubernetesingress.namespaces={{ template "providers.kubernetesIngress.namespaces" $ }}" + {{- end }} + {{- end }} + {{- with .Values.providers.file }} + {{- if .enabled }} + - "--providers.file.directory=/etc/traefik/dynamic" + {{- if .watch }} + - "--providers.file.watch=true" + {{- end }} + {{- end }} + {{- end }} + {{- range $entrypoint, $config := $.Values.ports }} + {{- if $config }} + {{- if $config.redirectTo }} + {{- $toPort := index $.Values.ports $config.redirectTo.port }} + - "--entryPoints.{{ $entrypoint }}.http.redirections.entryPoint.to=:{{ $toPort.exposedPort }}" + - "--entryPoints.{{ $entrypoint }}.http.redirections.entryPoint.scheme=https" + {{- if $config.redirectTo.priority }} + - "--entryPoints.{{ $entrypoint }}.http.redirections.entryPoint.priority={{ $config.redirectTo.priority }}" + {{- end }} + {{- if $config.redirectTo.permanent }} + - "--entryPoints.{{ $entrypoint }}.http.redirections.entryPoint.permanent=true" + {{- end }} + {{- end }} + {{- if $config.middlewares }} + - "--entryPoints.{{ $entrypoint }}.http.middlewares={{ join "," $config.middlewares }}" + {{- end }} + {{- if $config.tls }} + {{- if $config.tls.enabled }} + - "--entryPoints.{{ $entrypoint }}.http.tls=true" + {{- if $config.tls.options }} + - "--entryPoints.{{ $entrypoint }}.http.tls.options={{ $config.tls.options }}" + {{- end }} + {{- if $config.tls.certResolver }} + - "--entryPoints.{{ $entrypoint }}.http.tls.certResolver={{ $config.tls.certResolver }}" + {{- end }} + {{- if $config.tls.domains }} + {{- range $index, $domain := $config.tls.domains }} + {{- if $domain.main }} + - "--entryPoints.{{ $entrypoint }}.http.tls.domains[{{ $index }}].main={{ $domain.main }}" + {{- end }} + {{- if $domain.sans }} + - "--entryPoints.{{ $entrypoint }}.http.tls.domains[{{ $index }}].sans={{ join "," $domain.sans }}" + {{- end }} + {{- end }} + {{- end }} + {{- if $config.http3 }} + {{- if $config.http3.enabled }} + - "--entryPoints.{{ $entrypoint }}.http3" + {{- if $config.http3.advertisedPort }} + - "--entryPoints.{{ $entrypoint }}.http3.advertisedPort={{ $config.http3.advertisedPort }}" + {{- end }} + {{- end }} + {{- end }} + {{- end }} + {{- end }} + {{- if $config.allowACMEByPass }} + {{- if (semverCompare "<3.1.3-0" $version) }} + {{- fail "ERROR: allowACMEByPass has been introduced with Traefik v3.1.3+" -}} + {{- end }} + - "--entryPoints.name.allowACMEByPass=true" + {{- end }} + {{- if $config.forwardedHeaders }} + {{- if $config.forwardedHeaders.trustedIPs }} + - "--entryPoints.{{ $entrypoint }}.forwardedHeaders.trustedIPs={{ join "," $config.forwardedHeaders.trustedIPs }}" + {{- end }} + {{- if $config.forwardedHeaders.insecure }} + - "--entryPoints.{{ $entrypoint }}.forwardedHeaders.insecure" + {{- end }} + {{- end }} + {{- if $config.proxyProtocol }} + {{- if $config.proxyProtocol.trustedIPs }} + - "--entryPoints.{{ $entrypoint }}.proxyProtocol.trustedIPs={{ join "," $config.proxyProtocol.trustedIPs }}" + {{- end }} + {{- if $config.proxyProtocol.insecure }} + - "--entryPoints.{{ $entrypoint }}.proxyProtocol.insecure" + {{- end }} + {{- end }} + {{- with $config.transport }} + {{- with .respondingTimeouts }} + {{- if and (ne .readTimeout nil) (toString .readTimeout) }} + - "--entryPoints.{{ $entrypoint }}.transport.respondingTimeouts.readTimeout={{ .readTimeout }}" + {{- end }} + {{- if and (ne .writeTimeout nil) (toString .writeTimeout) }} + - "--entryPoints.{{ $entrypoint }}.transport.respondingTimeouts.writeTimeout={{ .writeTimeout }}" + {{- end }} + {{- if and (ne .idleTimeout nil) (toString .idleTimeout) }} + - "--entryPoints.{{ $entrypoint }}.transport.respondingTimeouts.idleTimeout={{ .idleTimeout }}" + {{- end }} + {{- end }} + {{- with .lifeCycle }} + {{- if and (ne .requestAcceptGraceTimeout nil) (toString .requestAcceptGraceTimeout) }} + - "--entryPoints.{{ $entrypoint }}.transport.lifeCycle.requestAcceptGraceTimeout={{ .requestAcceptGraceTimeout }}" + {{- end }} + {{- if and (ne .graceTimeOut nil) (toString .graceTimeOut) }} + - "--entryPoints.{{ $entrypoint }}.transport.lifeCycle.graceTimeOut={{ .graceTimeOut }}" + {{- end }} + {{- end }} + {{- if and (ne .keepAliveMaxRequests nil) (toString .keepAliveMaxRequests) }} + - "--entryPoints.{{ $entrypoint }}.transport.keepAliveMaxRequests={{ .keepAliveMaxRequests }}" + {{- end }} + {{- if and (ne .keepAliveMaxTime nil) (toString .keepAliveMaxTime) }} + - "--entryPoints.{{ $entrypoint }}.transport.keepAliveMaxTime={{ .keepAliveMaxTime }}" + {{- end }} + {{- end }} + {{- end }} + {{- end }} + {{- with .Values.logs }} + {{- if and .general.format (not (has .general.format (list "common" "json"))) }} + {{- fail "ERROR: .Values.logs.general.format must be either common or json" }} + {{- end }} + {{- with .general.format }} + - "--log.format={{ . }}" + {{- end }} + {{- with .general.filePath }} + - "--log.filePath={{ . }}" + {{- end }} + {{- if and (or (eq .general.format "common") (not .general.format)) (eq .general.noColor true) }} + - "--log.noColor={{ .general.noColor }}" + {{- end }} + {{- with .general.level }} + - "--log.level={{ . | upper }}" + {{- end }} + {{- if .access.enabled }} + - "--accesslog=true" + {{- with .access.format }} + - "--accesslog.format={{ . }}" + {{- end }} + {{- with .access.filePath }} + - "--accesslog.filepath={{ . }}" + {{- end }} + {{- if .access.addInternals }} + - "--accesslog.addinternals" + {{- end }} + {{- with .access.bufferingSize }} + - "--accesslog.bufferingsize={{ . }}" + {{- end }} + {{- with .access.filters }} + {{- with .statuscodes }} + - "--accesslog.filters.statuscodes={{ . }}" + {{- end }} + {{- if .retryattempts }} + - "--accesslog.filters.retryattempts" + {{- end }} + {{- with .minduration }} + - "--accesslog.filters.minduration={{ . }}" + {{- end }} + {{- end }} + - "--accesslog.fields.defaultmode={{ .access.fields.general.defaultmode }}" + {{- range $fieldname, $fieldaction := .access.fields.general.names }} + - "--accesslog.fields.names.{{ $fieldname }}={{ $fieldaction }}" + {{- end }} + - "--accesslog.fields.headers.defaultmode={{ .access.fields.headers.defaultmode }}" + {{- range $fieldname, $fieldaction := .access.fields.headers.names }} + - "--accesslog.fields.headers.names.{{ $fieldname }}={{ $fieldaction }}" + {{- end }} + {{- end }} + {{- end }} + {{- range $resolver, $config := $.Values.certResolvers }} + {{- range $option, $setting := $config }} + {{- if kindIs "map" $setting }} + {{- range $field, $value := $setting }} + - "--certificatesresolvers.{{ $resolver }}.acme.{{ $option }}.{{ $field }}={{ if kindIs "slice" $value }}{{ join "," $value }}{{ else }}{{ $value }}{{ end }}" + {{- end }} + {{- else }} + - "--certificatesresolvers.{{ $resolver }}.acme.{{ $option }}={{ $setting }}" + {{- end }} + {{- end }} + {{- end }} + {{- with .Values.additionalArguments }} + {{- range . }} + - {{ . | quote }} + {{- end }} + {{- end }} + {{- with .Values.hub }} + {{- if .token }} + - "--hub.token=$(HUB_TOKEN)" + {{- if and (not .apimanagement.enabled) ($.Values.hub.apimanagement.admission.listenAddr) }} + {{- fail "ERROR: Cannot configure admission without enabling hub.apimanagement" }} + {{- end }} + {{- with .apimanagement }} + {{- if .enabled }} + {{- $listenAddr := default ":9943" .admission.listenAddr }} + - "--hub.apimanagement" + - "--hub.apimanagement.admission.listenAddr={{ $listenAddr }}" + {{- with .admission.secretName }} + - "--hub.apimanagement.admission.secretName={{ . }}" + {{- end }} + {{- end }} + {{- end }} + {{- with .platformUrl }} + - "--hub.platformUrl={{ . }}" + {{- end -}} + {{- range $field, $value := .ratelimit.redis }} + {{- if has $field (list "cluster" "database" "endpoints" "username" "password" "timeout") -}} + {{- with $value }} + - "--hub.ratelimit.redis.{{ $field }}={{ $value }}" + {{- end }} + {{- end }} + {{- end }} + {{- range $field, $value := .ratelimit.redis.sentinel }} + {{- if has $field (list "masterset" "password" "username") -}} + {{- with $value }} + - "--hub.ratelimit.redis.sentinel.{{ $field }}={{ $value }}" + {{- end }} + {{- end }} + {{- end }} + {{- range $field, $value := .ratelimit.redis.tls }} + {{- if has $field (list "ca" "cert" "insecureSkipVerify" "key") -}} + {{- with $value }} + - "--hub.ratelimit.redis.tls.{{ $field }}={{ $value }}" + {{- end }} + {{- end }} + {{- end }} + {{- with .sendlogs }} + - "--hub.sendlogs={{ . }}" + {{- end }} + {{- end }} + {{- end }} + env: + {{- if ($.Values.resources.limits).cpu }} + - name: GOMAXPROCS + valueFrom: + resourceFieldRef: + resource: limits.cpu + divisor: '1' + {{- end }} + {{- if ($.Values.resources.limits).memory }} + - name: GOMEMLIMIT + valueFrom: + resourceFieldRef: + resource: limits.memory + divisor: '1' + {{- end }} + {{- with .Values.hub.token }} + - name: HUB_TOKEN + valueFrom: + secretKeyRef: + name: {{ . }} + key: token + {{- end }} + {{- with .Values.env }} + {{- toYaml . | nindent 10 }} + {{- end }} + {{- with .Values.envFrom }} + envFrom: + {{- toYaml . | nindent 10 }} + {{- end }} + {{- if .Values.deployment.additionalContainers }} + {{- toYaml .Values.deployment.additionalContainers | nindent 6 }} + {{- end }} + volumes: + - name: {{ .Values.persistence.name }} + {{- if .Values.persistence.enabled }} + persistentVolumeClaim: + claimName: {{ default (include "traefik.fullname" .) .Values.persistence.existingClaim }} + {{- else }} + emptyDir: {} + {{- end }} + - name: tmp + emptyDir: {} + {{- $root := . }} + {{- range .Values.volumes }} + - name: {{ tpl (.name) $root | replace "." "-" }} + {{- if eq .type "secret" }} + secret: + secretName: {{ tpl (.name) $root }} + {{- else if eq .type "configMap" }} + configMap: + name: {{ tpl (.name) $root }} + {{- end }} + {{- end }} + {{- if .Values.deployment.additionalVolumes }} + {{- toYaml .Values.deployment.additionalVolumes | nindent 8 }} + {{- end }} + {{- if gt (len .Values.experimental.plugins) 0 }} + - name: plugins + emptyDir: {} + {{- end }} + {{- if .Values.providers.file.enabled }} + - name: traefik-extra-config + configMap: + name: {{ template "traefik.fullname" . }}-file-provider + {{- end }} + {{- if .Values.affinity }} + affinity: + {{- tpl (toYaml .Values.affinity) . | nindent 8 }} + {{- end }} + {{- with .Values.tolerations }} + tolerations: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.nodeSelector }} + nodeSelector: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- if .Values.priorityClassName }} + priorityClassName: {{ .Values.priorityClassName }} + {{- end }} + {{- with .Values.podSecurityContext }} + securityContext: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- if .Values.topologySpreadConstraints }} + {{- if (semverCompare "<1.19.0-0" .Capabilities.KubeVersion.Version) }} + {{- fail "ERROR: topologySpreadConstraints are supported only on kubernetes >= v1.19" -}} + {{- end }} + topologySpreadConstraints: + {{- tpl (toYaml .Values.topologySpreadConstraints) . | nindent 8 }} + {{- end }} +{{ end -}} diff --git a/charts/traefik/traefik/31.1.0/templates/_service-metrics.tpl b/charts/traefik/traefik/31.1.0/templates/_service-metrics.tpl new file mode 100644 index 000000000..d16a3629d --- /dev/null +++ b/charts/traefik/traefik/31.1.0/templates/_service-metrics.tpl @@ -0,0 +1,25 @@ +{{- define "traefik.metrics-service-metadata" }} + labels: + {{- include "traefik.metricsservicelabels" . | nindent 4 -}} + {{- with .Values.metrics.prometheus.service.labels }} + {{- toYaml . | nindent 4 }} + {{- end }} +{{- end }} + +{{/* Labels used for metrics-relevant selector*/}} +{{/* This is an immutable field: this should not change between upgrade */}} +{{- define "traefik.metricslabelselector" -}} +{{- include "traefik.labelselector" . }} +app.kubernetes.io/component: metrics +{{- end }} + +{{/* Shared labels used in metadata of metrics-service and servicemonitor */}} +{{- define "traefik.metricsservicelabels" -}} +{{ include "traefik.metricslabelselector" . }} +helm.sh/chart: {{ template "traefik.chart" . }} +app.kubernetes.io/managed-by: {{ .Release.Service }} +{{- with .Values.commonLabels }} +{{ toYaml . }} +{{- end }} +{{- end }} + diff --git a/charts/traefik/traefik/31.1.0/templates/_service.tpl b/charts/traefik/traefik/31.1.0/templates/_service.tpl new file mode 100644 index 000000000..27d5bc477 --- /dev/null +++ b/charts/traefik/traefik/31.1.0/templates/_service.tpl @@ -0,0 +1,84 @@ +{{- define "traefik.service-name" -}} +{{- $fullname := printf "%s-%s" (include "traefik.fullname" .root) .name -}} +{{- if eq .name "default" -}} +{{- $fullname = include "traefik.fullname" .root -}} +{{- end -}} + +{{- if ge (len $fullname) 60 -}} # 64 - 4 (udp-postfix) = 60 + {{- fail "ERROR: Cannot create a service whose full name contains more than 60 characters" -}} +{{- end -}} + +{{- $fullname -}} +{{- end -}} + +{{- define "traefik.service-metadata" }} + labels: + {{- include "traefik.labels" .root | nindent 4 -}} + {{- with .service.labels }} + {{- toYaml . | nindent 4 }} + {{- end }} +{{- end }} + +{{- define "traefik.service-spec" -}} + {{- $type := default "LoadBalancer" .service.type }} + type: {{ $type }} + {{- with .service.loadBalancerClass }} + loadBalancerClass: {{ . }} + {{- end}} + {{- with .service.spec }} + {{- toYaml . | nindent 2 }} + {{- end }} + selector: + {{- include "traefik.labelselector" .root | nindent 4 }} + {{- if eq $type "LoadBalancer" }} + {{- with .service.loadBalancerSourceRanges }} + loadBalancerSourceRanges: + {{- toYaml . | nindent 2 }} + {{- end -}} + {{- end -}} + {{- with .service.externalIPs }} + externalIPs: + {{- toYaml . | nindent 2 }} + {{- end -}} + {{- with .service.ipFamilyPolicy }} + ipFamilyPolicy: {{ . }} + {{- end }} + {{- with .service.ipFamilies }} + ipFamilies: + {{- toYaml . | nindent 2 }} + {{- end -}} +{{- end }} + +{{- define "traefik.service-ports" }} + {{- range $name, $config := .ports }} + {{- if (index (default dict $config.expose) $.serviceName) }} + {{- $port := default $config.port $config.exposedPort }} + {{- if empty $port }} + {{- fail (print "ERROR: Cannot create " (trim $name) " port on Service without .port or .exposedPort") }} + {{- end }} + - port: {{ $port }} + name: {{ $name | quote }} + targetPort: {{ default $name $config.targetPort }} + protocol: {{ default "TCP" $config.protocol }} + {{- if $config.nodePort }} + nodePort: {{ $config.nodePort }} + {{- end }} + {{- if $config.appProtocol }} + appProtocol: {{ $config.appProtocol }} + {{- end }} + {{- if and ($config.http3).enabled ($config.single) }} + {{- $http3Port := default $config.exposedPort $config.http3.advertisedPort }} + - port: {{ $http3Port }} + name: "{{ $name }}-http3" + targetPort: "{{ $name }}-http3" + protocol: UDP + {{- if $config.nodePort }} + nodePort: {{ $config.nodePort }} + {{- end }} + {{- if $config.appProtocol }} + appProtocol: {{ $config.appProtocol }} + {{- end }} + {{- end }} + {{- end }} + {{- end }} +{{- end }} diff --git a/charts/traefik/traefik/31.1.0/templates/daemonset.yaml b/charts/traefik/traefik/31.1.0/templates/daemonset.yaml new file mode 100644 index 000000000..5be6a0a25 --- /dev/null +++ b/charts/traefik/traefik/31.1.0/templates/daemonset.yaml @@ -0,0 +1,50 @@ +{{- if and .Values.deployment.enabled (eq .Values.deployment.kind "DaemonSet") -}} + {{- with .Values.additionalArguments -}} + {{- range . -}} + {{- if contains ".acme." . -}} + {{- fail (printf "ACME functionality is not supported when running Traefik as a DaemonSet") -}} + {{- end -}} + {{- end -}} + {{- end -}} + {{- if eq (default .Chart.AppVersion .Values.image.tag) "latest" }} + {{- fail "\n\n ERROR: latest tag should not be used" }} + {{- end }} + {{- with .Values.updateStrategy }} + {{- if eq (.type) "RollingUpdate" }} + {{- if not (contains "%" (toString .rollingUpdate.maxUnavailable)) }} + {{- if and ($.Values.hostNetwork) (lt (float64 .rollingUpdate.maxUnavailable) 1.0) }} + {{- fail "maxUnavailable should be greater than 1 when using hostNetwork." }} + {{- end }} + {{- end }} + {{- end }} + {{- end }} + +--- +apiVersion: apps/v1 +kind: DaemonSet +metadata: + name: {{ template "traefik.fullname" . }} + namespace: {{ template "traefik.namespace" . }} + labels: + {{- include "traefik.labels" . | nindent 4 }} + {{- with .Values.deployment.labels }} + {{- toYaml . | nindent 4 }} + {{- end }} + annotations: + {{- if and .Values.providers.file.enabled (not .Values.providers.file.watch) }} + checksum/traefik-dynamic-conf: {{ include (print $.Template.BasePath "/provider-file-cm.yaml") . | sha256sum }} + {{- end }} + {{- with .Values.deployment.annotations }} + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + selector: + matchLabels: + {{- include "traefik.labelselector" . | nindent 6 }} + updateStrategy: {{ toYaml .Values.updateStrategy | nindent 4 }} + minReadySeconds: {{ .Values.deployment.minReadySeconds }} + {{- if .Values.deployment.revisionHistoryLimit }} + revisionHistoryLimit: {{ .Values.deployment.revisionHistoryLimit }} + {{- end }} + template: {{ template "traefik.podTemplate" . }} +{{- end -}} diff --git a/charts/traefik/traefik/31.1.0/templates/deployment.yaml b/charts/traefik/traefik/31.1.0/templates/deployment.yaml new file mode 100644 index 000000000..11f1f7f48 --- /dev/null +++ b/charts/traefik/traefik/31.1.0/templates/deployment.yaml @@ -0,0 +1,50 @@ +{{/* check helm version */}} +{{- if (semverCompare "= 3.9.0 is required" -}} +{{- end -}} + +{{- if and .Values.deployment.enabled (eq .Values.deployment.kind "Deployment") -}} + {{- if gt (int .Values.deployment.replicas) 1 -}} + {{- with .Values.additionalArguments -}} + {{- range . -}} + {{- if contains ".acme." . -}} + {{- fail (printf "You can not enable acme if you set more than one traefik replica") -}} + {{- end -}} + {{- end -}} + {{- end -}} + {{- end -}} + {{- if eq (default .Chart.AppVersion .Values.image.tag) "latest" }} + {{- fail "\n\n ERROR: latest tag should not be used" }} + {{- end }} +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ template "traefik.fullname" . }} + namespace: {{ template "traefik.namespace" . }} + labels: + {{- include "traefik.labels" . | nindent 4 }} + {{- with .Values.deployment.labels }} + {{- toYaml . | nindent 4 }} + {{- end }} + annotations: + {{- if and .Values.providers.file.enabled (not .Values.providers.file.watch) }} + checksum/traefik-dynamic-conf: {{ include (print $.Template.BasePath "/provider-file-cm.yaml") . | sha256sum }} + {{- end }} + {{- with .Values.deployment.annotations }} + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + {{- if not .Values.autoscaling.enabled }} + replicas: {{ default 1 .Values.deployment.replicas }} + {{- end }} + {{- if .Values.deployment.revisionHistoryLimit }} + revisionHistoryLimit: {{ .Values.deployment.revisionHistoryLimit }} + {{- end }} + selector: + matchLabels: + {{- include "traefik.labelselector" . | nindent 6 }} + strategy: {{ toYaml .Values.updateStrategy | nindent 4 }} + minReadySeconds: {{ .Values.deployment.minReadySeconds }} + template: {{ template "traefik.podTemplate" . }} +{{- end -}} diff --git a/charts/traefik/traefik/31.1.0/templates/extra-objects.yaml b/charts/traefik/traefik/31.1.0/templates/extra-objects.yaml new file mode 100644 index 000000000..fb38e9773 --- /dev/null +++ b/charts/traefik/traefik/31.1.0/templates/extra-objects.yaml @@ -0,0 +1,4 @@ +{{- range .Values.extraObjects }} +--- +{{ include "traefik.render" (dict "value" . "context" $) }} +{{- end }} diff --git a/charts/traefik/traefik/31.1.0/templates/gateway.yaml b/charts/traefik/traefik/31.1.0/templates/gateway.yaml new file mode 100644 index 000000000..13696dffc --- /dev/null +++ b/charts/traefik/traefik/31.1.0/templates/gateway.yaml @@ -0,0 +1,58 @@ +{{- if and (.Values.gateway).enabled (.Values.providers.kubernetesGateway).enabled }} + {{- if not .Values.gateway.listeners }} + {{- fail "ERROR: gateway must have at least one listener or should be disabled" }} + {{- end }} +--- +apiVersion: gateway.networking.k8s.io/v1 +kind: Gateway +metadata: + name: {{ default "traefik-gateway" .Values.gateway.name }} + namespace: {{ template "traefik.namespace" . }} + labels: + {{- include "traefik.labels" . | nindent 4 }} + {{- with .Values.gateway.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + gatewayClassName: {{ default "traefik" .Values.gatewayClass.name }} + listeners: + {{- range $name, $config := .Values.gateway.listeners }} + - name: {{ $name }} + {{ if not .port }} + {{- fail "ERROR: port needs to be specified" }} + {{- end -}} + {{ $found := false }} + {{- range $portName, $portConfig := $.Values.ports -}} + {{- if eq $portConfig.port $config.port -}} + {{ $found = true }} + {{- end -}} + {{- end -}} + {{ if not $found }} + {{- fail (printf "ERROR: port %0.f is not declared in ports" .port ) }} + {{- end -}} + port: {{ .port }} + protocol: {{ .protocol }} + {{- with .hostname }} + hostname: {{ . | toYaml }} + {{- end }} + {{- with .namespacePolicy }} + allowedRoutes: + namespaces: + from: {{ . }} + {{- end }} + {{ if and (eq .protocol "HTTPS") (not .certificateRefs) }} + {{- fail "ERROR: certificateRefs needs to be specified using HTTPS" }} + {{- end }} + {{ if or .certificateRefs .mode }} + tls: + {{ with .mode }} + mode: {{ . }} + {{- end }} + {{ with .certificateRefs }} + certificateRefs: + {{- toYaml . | nindent 10 }} + {{- end }} + {{- end }} + {{- end }} +{{- end }} diff --git a/charts/traefik/traefik/31.1.0/templates/gatewayclass.yaml b/charts/traefik/traefik/31.1.0/templates/gatewayclass.yaml new file mode 100644 index 000000000..7f98c1ed4 --- /dev/null +++ b/charts/traefik/traefik/31.1.0/templates/gatewayclass.yaml @@ -0,0 +1,14 @@ +{{- if and (.Values.gatewayClass).enabled (.Values.providers.kubernetesGateway).enabled }} +--- +apiVersion: gateway.networking.k8s.io/v1 +kind: GatewayClass +metadata: + name: {{ default "traefik" .Values.gatewayClass.name }} + labels: + {{- include "traefik.labels" . | nindent 4 }} + {{- with .Values.gatewayClass.labels }} + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + controllerName: traefik.io/gateway-controller +{{- end }} diff --git a/charts/traefik/traefik/31.1.0/templates/hpa.yaml b/charts/traefik/traefik/31.1.0/templates/hpa.yaml new file mode 100644 index 000000000..cfa1e5a49 --- /dev/null +++ b/charts/traefik/traefik/31.1.0/templates/hpa.yaml @@ -0,0 +1,35 @@ +{{- if .Values.autoscaling.enabled }} + +{{- if not .Values.autoscaling.maxReplicas }} + {{- fail "ERROR: maxReplicas is required on HPA" }} +{{- end }} + +{{- if semverCompare ">=1.23.0-0" .Capabilities.KubeVersion.Version }} +apiVersion: autoscaling/v2 +{{- else }} +apiVersion: autoscaling/v2beta2 +{{- end }} +kind: HorizontalPodAutoscaler +metadata: + name: {{ template "traefik.fullname" . }} + namespace: {{ template "traefik.namespace" . }} + labels: + {{- include "traefik.labels" . | nindent 4 }} +spec: + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: {{ template "traefik.fullname" . }} +{{- if .Values.autoscaling.minReplicas }} + minReplicas: {{ .Values.autoscaling.minReplicas }} +{{- end }} + maxReplicas: {{ .Values.autoscaling.maxReplicas }} +{{- if .Values.autoscaling.metrics }} + metrics: +{{ toYaml .Values.autoscaling.metrics | indent 4 }} +{{- end }} +{{- if .Values.autoscaling.behavior }} + behavior: +{{ toYaml .Values.autoscaling.behavior | indent 4 }} +{{- end }} +{{- end }} diff --git a/charts/traefik/traefik/31.1.0/templates/hub-admission-controller.yaml b/charts/traefik/traefik/31.1.0/templates/hub-admission-controller.yaml new file mode 100644 index 000000000..37b2314f5 --- /dev/null +++ b/charts/traefik/traefik/31.1.0/templates/hub-admission-controller.yaml @@ -0,0 +1,198 @@ +{{- if .Values.hub.token -}} +{{- if .Values.hub.apimanagement.enabled }} +{{- $cert := include "traefik-hub.webhook_cert" . | fromYaml }} +--- +apiVersion: v1 +kind: Secret +type: kubernetes.io/tls +metadata: + name: hub-agent-cert + namespace: {{ template "traefik.namespace" . }} + labels: + {{- include "traefik.labels" . | nindent 4 }} +data: + tls.crt: {{ $cert.Cert }} + tls.key: {{ $cert.Key }} + +--- +apiVersion: admissionregistration.k8s.io/v1 +kind: MutatingWebhookConfiguration +metadata: + name: hub-acp + labels: + {{- include "traefik.labels" . | nindent 4 }} +webhooks: + - name: admission.traefik.svc + clientConfig: + service: + name: admission + namespace: {{ template "traefik.namespace" . }} + path: /acp + caBundle: {{ $cert.Cert }} + sideEffects: None + admissionReviewVersions: + - v1 + rules: + - operations: + - CREATE + - UPDATE + - DELETE + apiGroups: + - hub.traefik.io + apiVersions: + - v1alpha1 + resources: + - accesscontrolpolicies + +--- +apiVersion: admissionregistration.k8s.io/v1 +kind: MutatingWebhookConfiguration +metadata: + name: hub-api + labels: + {{- include "traefik.labels" . | nindent 4 }} +webhooks: + - name: hub-agent.traefik.portal + clientConfig: + service: + name: admission + namespace: {{ template "traefik.namespace" . }} + path: /api-portal + caBundle: {{ $cert.Cert }} + sideEffects: None + admissionReviewVersions: + - v1 + rules: + - operations: + - CREATE + - UPDATE + - DELETE + apiGroups: + - hub.traefik.io + apiVersions: + - v1alpha1 + resources: + - apiportals + - name: hub-agent.traefik.api + clientConfig: + service: + name: admission + namespace: {{ template "traefik.namespace" . }} + path: /api + caBundle: {{ $cert.Cert }} + sideEffects: None + admissionReviewVersions: + - v1 + rules: + - operations: + - CREATE + - UPDATE + - DELETE + apiGroups: + - hub.traefik.io + apiVersions: + - v1alpha1 + resources: + - apis + - name: hub-agent.traefik.access + clientConfig: + service: + name: admission + namespace: {{ template "traefik.namespace" . }} + path: /api-access + caBundle: {{ $cert.Cert }} + sideEffects: None + admissionReviewVersions: + - v1 + rules: + - operations: + - CREATE + - UPDATE + - DELETE + apiGroups: + - hub.traefik.io + apiVersions: + - v1alpha1 + resources: + - apiaccesses + - name: hub-agent.traefik.plan + clientConfig: + service: + name: admission + namespace: {{ template "traefik.namespace" . }} + path: /api-plan + caBundle: {{ $cert.Cert }} + sideEffects: None + admissionReviewVersions: + - v1 + rules: + - operations: + - CREATE + - UPDATE + - DELETE + apiGroups: + - hub.traefik.io + apiVersions: + - v1alpha1 + resources: + - apiplans + - name: hub-agent.traefik.bundle + clientConfig: + service: + name: admission + namespace: {{ template "traefik.namespace" . }} + path: /api-bundle + caBundle: {{ $cert.Cert }} + sideEffects: None + admissionReviewVersions: + - v1 + rules: + - operations: + - CREATE + - UPDATE + - DELETE + apiGroups: + - hub.traefik.io + apiVersions: + - v1alpha1 + resources: + - apibundles + - name: hub-agent.traefik.version + clientConfig: + service: + name: admission + namespace: {{ template "traefik.namespace" . }} + path: /api-version + caBundle: {{ $cert.Cert }} + sideEffects: None + admissionReviewVersions: + - v1 + rules: + - operations: + - CREATE + - UPDATE + - DELETE + apiGroups: + - hub.traefik.io + apiVersions: + - v1alpha1 + resources: + - apiversions + +--- +apiVersion: v1 +kind: Service +metadata: + name: admission + namespace: {{ template "traefik.namespace" . }} + labels: + {{- include "traefik.labels" . | nindent 4 }} +spec: + ports: + - name: https + port: 443 + targetPort: admission + selector: + {{- include "traefik.labelselector" . | nindent 4 }} +{{- end -}} +{{- end -}} diff --git a/charts/traefik/traefik/31.1.0/templates/hub-apiportal.yaml b/charts/traefik/traefik/31.1.0/templates/hub-apiportal.yaml new file mode 100644 index 000000000..246b127ed --- /dev/null +++ b/charts/traefik/traefik/31.1.0/templates/hub-apiportal.yaml @@ -0,0 +1,19 @@ +{{- if .Values.hub.apimanagement.enabled }} +--- +apiVersion: v1 +kind: Service +metadata: + name: apiportal + namespace: {{ template "traefik.namespace" . }} + labels: + {{- include "traefik.labels" . | nindent 4 }} +spec: + ports: + - name: apiportal + port: 9903 + protocol: TCP + targetPort: apiportal + selector: + {{- include "traefik.labelselector" . | nindent 4 }} +{{- end -}} + diff --git a/charts/traefik/traefik/31.1.0/templates/ingressclass.yaml b/charts/traefik/traefik/31.1.0/templates/ingressclass.yaml new file mode 100644 index 000000000..6a8ff8199 --- /dev/null +++ b/charts/traefik/traefik/31.1.0/templates/ingressclass.yaml @@ -0,0 +1,12 @@ +{{- if .Values.ingressClass.enabled -}} +apiVersion: networking.k8s.io/v1 +kind: IngressClass +metadata: + annotations: + ingressclass.kubernetes.io/is-default-class: {{ .Values.ingressClass.isDefaultClass | quote }} + labels: + {{- include "traefik.labels" . | nindent 4 }} + name: {{ .Values.ingressClass.name | default (include "traefik.fullname" .) }} +spec: + controller: traefik.io/ingress-controller +{{- end -}} diff --git a/charts/traefik/traefik/31.1.0/templates/ingressroute.yaml b/charts/traefik/traefik/31.1.0/templates/ingressroute.yaml new file mode 100644 index 000000000..2f35abb2a --- /dev/null +++ b/charts/traefik/traefik/31.1.0/templates/ingressroute.yaml @@ -0,0 +1,43 @@ +{{ range $name, $config := .Values.ingressRoute }} +{{ if $config.enabled }} +--- +apiVersion: traefik.io/v1alpha1 +kind: IngressRoute +metadata: + name: {{ $.Release.Name }}-{{ $name }} + namespace: {{ template "traefik.namespace" $ }} + annotations: + {{- if and $.Values.ingressClass.enabled $.Values.providers.kubernetesCRD.enabled $.Values.providers.kubernetesCRD.ingressClass }} + kubernetes.io/ingress.class: {{ $.Values.providers.kubernetesCRD.ingressClass }} + {{- end }} + {{- with $config.annotations }} + {{- toYaml . | nindent 4 }} + {{- end }} + labels: + {{- include "traefik.labels" $ | nindent 4 }} + {{- with $config.labels }} + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + entryPoints: + {{- range $config.entryPoints }} + - {{ . }} + {{- end }} + routes: + - match: {{ $config.matchRule }} + kind: Rule + {{- with $config.services }} + services: + {{- toYaml . | nindent 6 }} + {{- end -}} + {{- with $config.middlewares }} + middlewares: + {{- toYaml . | nindent 6 }} + {{- end -}} + + {{- with $config.tls }} + tls: + {{- toYaml . | nindent 4 }} + {{- end }} +{{- end -}} +{{ end }} diff --git a/charts/traefik/traefik/31.1.0/templates/poddisruptionbudget.yaml b/charts/traefik/traefik/31.1.0/templates/poddisruptionbudget.yaml new file mode 100644 index 000000000..f1716397c --- /dev/null +++ b/charts/traefik/traefik/31.1.0/templates/poddisruptionbudget.yaml @@ -0,0 +1,23 @@ +{{- if .Values.podDisruptionBudget.enabled -}} +{{- if .Capabilities.APIVersions.Has "policy/v1/PodDisruptionBudget" }} +apiVersion: policy/v1 +{{- else }} +apiVersion: policy/v1beta1 +{{- end }} +kind: PodDisruptionBudget +metadata: + name: {{ template "traefik.fullname" . }} + namespace: {{ template "traefik.namespace" . }} + labels: + {{- include "traefik.labels" . | nindent 4 }} +spec: + selector: + matchLabels: + {{- include "traefik.labelselector" . | nindent 6 }} + {{- if .Values.podDisruptionBudget.minAvailable }} + minAvailable: {{ .Values.podDisruptionBudget.minAvailable }} + {{- end }} + {{- if .Values.podDisruptionBudget.maxUnavailable }} + maxUnavailable: {{ .Values.podDisruptionBudget.maxUnavailable }} + {{- end }} +{{- end -}} diff --git a/charts/traefik/traefik/31.1.0/templates/prometheusrules.yaml b/charts/traefik/traefik/31.1.0/templates/prometheusrules.yaml new file mode 100644 index 000000000..3231aba6c --- /dev/null +++ b/charts/traefik/traefik/31.1.0/templates/prometheusrules.yaml @@ -0,0 +1,28 @@ +{{- if .Values.metrics.prometheus }} +{{- if (.Values.metrics.prometheus.prometheusRule).enabled }} + {{- if (not (.Capabilities.APIVersions.Has "monitoring.coreos.com/v1")) }} + {{- if (not (.Values.metrics.prometheus.disableAPICheck)) }} + {{- fail "ERROR: You have to deploy monitoring.coreos.com/v1 first" }} + {{- end }} + {{- end }} +apiVersion: monitoring.coreos.com/v1 +kind: PrometheusRule +metadata: + name: {{ template "traefik.fullname" . }} + namespace: {{ .Values.metrics.prometheus.prometheusRule.namespace | default (include "traefik.namespace" .) }} + labels: + {{- include "traefik.labels" . | nindent 4 }} + {{- with .Values.metrics.prometheus.prometheusRule.additionalLabels }} + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + {{- if .Values.metrics.prometheus.prometheusRule.rules }} + groups: + - name: {{ template "traefik.name" $ }} + rules: + {{- with .Values.metrics.prometheus.prometheusRule.rules }} + {{- toYaml . | nindent 4 }} + {{- end }} + {{- end }} +{{- end }} +{{- end }} diff --git a/charts/traefik/traefik/31.1.0/templates/provider-file-cm.yaml b/charts/traefik/traefik/31.1.0/templates/provider-file-cm.yaml new file mode 100644 index 000000000..139a5a6ab --- /dev/null +++ b/charts/traefik/traefik/31.1.0/templates/provider-file-cm.yaml @@ -0,0 +1,12 @@ +{{- if .Values.providers.file.enabled -}} +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ template "traefik.fullname" . }}-file-provider + namespace: {{ template "traefik.namespace" . }} + labels: + {{- include "traefik.labels" . | nindent 4 }} +data: + config.yml: + {{ toYaml .Values.providers.file.content | nindent 4 }} +{{- end -}} diff --git a/charts/traefik/traefik/31.1.0/templates/pvc.yaml b/charts/traefik/traefik/31.1.0/templates/pvc.yaml new file mode 100644 index 000000000..7ab96f960 --- /dev/null +++ b/charts/traefik/traefik/31.1.0/templates/pvc.yaml @@ -0,0 +1,26 @@ +{{- if and .Values.persistence.enabled (not .Values.persistence.existingClaim) -}} +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: {{ template "traefik.fullname" . }} + namespace: {{ template "traefik.namespace" . }} + annotations: + {{- with .Values.persistence.annotations }} + {{ toYaml . | nindent 4 }} + {{- end }} + helm.sh/resource-policy: keep + labels: + {{- include "traefik.labels" . | nindent 4 }} +spec: + accessModes: + - {{ .Values.persistence.accessMode | quote }} + resources: + requests: + storage: {{ .Values.persistence.size | quote }} + {{- if .Values.persistence.storageClass }} + storageClassName: {{ .Values.persistence.storageClass | quote }} + {{- end }} + {{- if .Values.persistence.volumeName }} + volumeName: {{ .Values.persistence.volumeName | quote }} + {{- end }} +{{- end -}} diff --git a/charts/traefik/traefik/31.1.0/templates/rbac/clusterrole.yaml b/charts/traefik/traefik/31.1.0/templates/rbac/clusterrole.yaml new file mode 100644 index 000000000..3fa943e54 --- /dev/null +++ b/charts/traefik/traefik/31.1.0/templates/rbac/clusterrole.yaml @@ -0,0 +1,254 @@ +{{- $version := include "imageVersion" $ }} +{{- if and .Values.rbac.enabled (not .Values.rbac.namespaced) }} +--- +kind: ClusterRole +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: {{ template "traefik.clusterRoleName" . }} + labels: + {{- include "traefik.labels" . | nindent 4 }} + {{- range .Values.rbac.aggregateTo }} + rbac.authorization.k8s.io/aggregate-to-{{ . }}: "true" + {{- end }} +rules: + {{- if semverCompare ">=v3.1.0-0" $version }} + - apiGroups: + - "" + resources: + - nodes + verbs: + - get + - list + - watch + {{- end }} + {{- if (semverCompare "=v3.1.0-0" $version) }} + - apiGroups: + - discovery.k8s.io + resources: + - endpointslices + verbs: + - list + - watch + {{- end }} + - apiGroups: + - gateway.networking.k8s.io + resources: + - gatewayclasses + - gateways + - httproutes + - referencegrants + - tcproutes + - tlsroutes + verbs: + - get + - list + - watch + - apiGroups: + - gateway.networking.k8s.io + resources: + - gatewayclasses/status + - gateways/status + - httproutes/status + - tcproutes/status + - tlsroutes/status + verbs: + - update + {{- end }} + {{- if .Values.hub.token }} + - apiGroups: + - coordination.k8s.io + resources: + - leases + verbs: + - get + - list + - watch + - create + - update + - patch + - delete + {{- end }} + {{- if .Values.hub.token }} + {{- if or (semverCompare ">=v3.1.0-0" $version) .Values.hub.apimanagement.enabled }} + - apiGroups: + - "" + resources: + - endpoints + verbs: + - list + - watch + {{- end }} + - apiGroups: + - "" + resources: + - namespaces + {{- if .Values.hub.apimanagement.enabled }} + - pods + {{- end }} + verbs: + - get + - list + {{- if .Values.hub.apimanagement.enabled }} + - watch + {{- end }} + {{- if .Values.hub.apimanagement.enabled }} + - apiGroups: + - hub.traefik.io + resources: + - accesscontrolpolicies + - apiaccesses + - apiportals + - apiratelimits + - apis + - apiversions + verbs: + - list + - watch + - create + - update + - patch + - delete + - get + - apiGroups: + - "" + resources: + - events + verbs: + - create + - patch + - apiGroups: + - apps + resources: + - replicasets + verbs: + - get + - list + - watch + {{- if (semverCompare "=1.25.0-0" .Capabilities.KubeVersion.Version }} + {{- fail "ERROR: PodSecurityPolicy has been removed in Kubernetes v1.25+" }} +{{- end }} +--- +apiVersion: policy/v1beta1 +kind: PodSecurityPolicy +metadata: + annotations: + seccomp.security.alpha.kubernetes.io/allowedProfileNames: runtime/default + seccomp.security.alpha.kubernetes.io/defaultProfileName: runtime/default + name: {{ template "traefik.fullname" . }} + labels: + {{- include "traefik.labels" . | nindent 4 }} +spec: + privileged: false + allowPrivilegeEscalation: false + requiredDropCapabilities: + - ALL +{{- if not .Values.securityContext.runAsNonRoot }} + allowedCapabilities: + - NET_BIND_SERVICE +{{- end }} + hostNetwork: {{ .Values.hostNetwork }} + hostIPC: false + hostPID: false + fsGroup: +{{- if .Values.securityContext.runAsNonRoot }} + ranges: + - max: 65535 + min: 1 + rule: MustRunAs +{{- else }} + rule: RunAsAny +{{- end }} +{{- if .Values.hostNetwork }} + hostPorts: + - max: 65535 + min: 1 +{{- end }} + readOnlyRootFilesystem: true + runAsUser: +{{- if .Values.securityContext.runAsNonRoot }} + rule: MustRunAsNonRoot +{{- else }} + rule: RunAsAny +{{- end }} + seLinux: + rule: RunAsAny + supplementalGroups: +{{- if .Values.securityContext.runAsNonRoot }} + ranges: + - max: 65535 + min: 1 + rule: MustRunAs +{{- else }} + rule: RunAsAny +{{- end }} + volumes: + - configMap + - downwardAPI + - secret + - emptyDir + - projected +{{- if .Values.persistence.enabled }} + - persistentVolumeClaim +{{- end -}} +{{- end -}} diff --git a/charts/traefik/traefik/31.1.0/templates/rbac/role.yaml b/charts/traefik/traefik/31.1.0/templates/rbac/role.yaml new file mode 100644 index 000000000..e81aaa8a6 --- /dev/null +++ b/charts/traefik/traefik/31.1.0/templates/rbac/role.yaml @@ -0,0 +1,143 @@ +{{- $version := include "imageVersion" $ }} +{{- $ingressNamespaces := concat (include "traefik.namespace" . | list) .Values.providers.kubernetesIngress.namespaces -}} +{{- $CRDNamespaces := concat (include "traefik.namespace" . | list) .Values.providers.kubernetesCRD.namespaces -}} +{{- $allNamespaces := sortAlpha (uniq (concat $ingressNamespaces $CRDNamespaces)) -}} + +{{- if and .Values.rbac.enabled .Values.rbac.namespaced -}} +{{- range $allNamespaces }} +--- +kind: Role +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: {{ template "traefik.fullname" $ }} + namespace: {{ . }} + labels: + {{- include "traefik.labels" $ | nindent 4 }} +rules: + {{- if (semverCompare "://:. Default: http://localhost:4318/v1/metrics + endpoint: "" + # -- Additional headers sent with metrics by the reporter to the OpenTelemetry Collector. + headers: {} + ## Defines the TLS configuration used by the reporter to send metrics to the OpenTelemetry Collector. + tls: + # -- The path to the certificate authority, it defaults to the system bundle. + ca: "" + # -- The path to the public certificate. When using this option, setting the key option is required. + cert: "" + # -- The path to the private key. When using this option, setting the cert option is required. + key: "" + # -- When set to true, the TLS connection accepts any certificate presented by the server regardless of the hostnames it covers. + insecureSkipVerify: # @schema type:[boolean, null] + grpc: + # -- Set to true in order to send metrics to the OpenTelemetry Collector using gRPC + enabled: false + # -- Format: ://:. Default: http://localhost:4318/v1/metrics + endpoint: "" + # -- Allows reporter to send metrics to the OpenTelemetry Collector without using a secured protocol. + insecure: false + ## Defines the TLS configuration used by the reporter to send metrics to the OpenTelemetry Collector. + tls: + # -- The path to the certificate authority, it defaults to the system bundle. + ca: "" + # -- The path to the public certificate. When using this option, setting the key option is required. + cert: "" + # -- The path to the private key. When using this option, setting the cert option is required. + key: "" + # -- When set to true, the TLS connection accepts any certificate presented by the server regardless of the hostnames it covers. + insecureSkipVerify: false + +## Tracing +# -- https://doc.traefik.io/traefik/observability/tracing/overview/ +tracing: # @schema additionalProperties: false + # -- Enables tracing for internal resources. Default: false. + addInternals: false + otlp: + # -- See https://doc.traefik.io/traefik/v3.0/observability/tracing/opentelemetry/ + enabled: false + http: + # -- Set to true in order to send metrics to the OpenTelemetry Collector using HTTP. + enabled: false + # -- Format: ://:. Default: http://localhost:4318/v1/metrics + endpoint: "" + # -- Additional headers sent with metrics by the reporter to the OpenTelemetry Collector. + headers: {} + ## Defines the TLS configuration used by the reporter to send metrics to the OpenTelemetry Collector. + tls: + # -- The path to the certificate authority, it defaults to the system bundle. + ca: "" + # -- The path to the public certificate. When using this option, setting the key option is required. + cert: "" + # -- The path to the private key. When using this option, setting the cert option is required. + key: "" + # -- When set to true, the TLS connection accepts any certificate presented by the server regardless of the hostnames it covers. + insecureSkipVerify: false + grpc: + # -- Set to true in order to send metrics to the OpenTelemetry Collector using gRPC + enabled: false + # -- Format: ://:. Default: http://localhost:4318/v1/metrics + endpoint: "" + # -- Allows reporter to send metrics to the OpenTelemetry Collector without using a secured protocol. + insecure: false + ## Defines the TLS configuration used by the reporter to send metrics to the OpenTelemetry Collector. + tls: + # -- The path to the certificate authority, it defaults to the system bundle. + ca: "" + # -- The path to the public certificate. When using this option, setting the key option is required. + cert: "" + # -- The path to the private key. When using this option, setting the cert option is required. + key: "" + # -- When set to true, the TLS connection accepts any certificate presented by the server regardless of the hostnames it covers. + insecureSkipVerify: false + +# -- Global command arguments to be passed to all traefik's pods +globalArguments: +- "--global.checknewversion" +- "--global.sendanonymoususage" + +# -- Additional arguments to be passed at Traefik's binary +# See [CLI Reference](https://docs.traefik.io/reference/static-configuration/cli/) +# Use curly braces to pass values: `helm install --set="additionalArguments={--providers.kubernetesingress.ingressclass=traefik-internal,--log.level=DEBUG}"` +additionalArguments: [] +# - "--providers.kubernetesingress.ingressclass=traefik-internal" +# - "--log.level=DEBUG" + +# -- Environment variables to be passed to Traefik's binary +# @default -- See _values.yaml_ +env: +- name: POD_NAME + valueFrom: + fieldRef: + fieldPath: metadata.name +- name: POD_NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace + +# -- Environment variables to be passed to Traefik's binary from configMaps or secrets +envFrom: [] + +ports: + traefik: + port: 9000 + # -- Use hostPort if set. + hostPort: # @schema type:[integer, null]; minimum:0 + # -- Use hostIP if set. If not set, Kubernetes will default to 0.0.0.0, which + # means it's listening on all your interfaces and all your IPs. You may want + # to set this value if you need traefik to listen on specific interface + # only. + hostIP: # @schema type:[string, null] + + # Defines whether the port is exposed if service.type is LoadBalancer or + # NodePort. + # + # -- You SHOULD NOT expose the traefik port on production deployments. + # If you want to access it from outside your cluster, + # use `kubectl port-forward` or create a secure ingress + expose: + default: false + # -- The exposed port for this service + exposedPort: 9000 + # -- The port protocol (TCP/UDP) + protocol: TCP + web: + ## -- Enable this entrypoint as a default entrypoint. When a service doesn't explicitly set an entrypoint it will only use this entrypoint. + # asDefault: true + port: 8000 + # hostPort: 8000 + # containerPort: 8000 + expose: + default: true + exposedPort: 80 + ## -- Different target traefik port on the cluster, useful for IP type LB + targetPort: # @schema type:[integer, null]; minimum:0 + # The port protocol (TCP/UDP) + protocol: TCP + # -- See [upstream documentation](https://kubernetes.io/docs/concepts/services-networking/service/#type-nodeport) + nodePort: # @schema type:[integer, null]; minimum:0 + # Port Redirections + # Added in 2.2, you can make permanent redirects via entrypoints. + # https://docs.traefik.io/routing/entrypoints/#redirection + redirectTo: {} + forwardedHeaders: + # -- Trust forwarded headers information (X-Forwarded-*). + trustedIPs: [] + insecure: false + proxyProtocol: + # -- Enable the Proxy Protocol header parsing for the entry point + trustedIPs: [] + insecure: false + # -- Set transport settings for the entrypoint; see also + # https://doc.traefik.io/traefik/routing/entrypoints/#transport + transport: + respondingTimeouts: + readTimeout: # @schema type:[string, integer, null] + writeTimeout: # @schema type:[string, integer, null] + idleTimeout: # @schema type:[string, integer, null] + lifeCycle: + requestAcceptGraceTimeout: # @schema type:[string, integer, null] + graceTimeOut: # @schema type:[string, integer, null] + keepAliveMaxRequests: # @schema type:[integer, null]; minimum:0 + keepAliveMaxTime: # @schema type:[string, integer, null] + websecure: + ## -- Enable this entrypoint as a default entrypoint. When a service doesn't explicitly set an entrypoint it will only use this entrypoint. + # asDefault: true + port: 8443 + hostPort: # @schema type:[integer, null]; minimum:0 + containerPort: # @schema type:[integer, null]; minimum:0 + expose: + default: true + exposedPort: 443 + ## -- Different target traefik port on the cluster, useful for IP type LB + targetPort: # @schema type:[integer, null]; minimum:0 + ## -- The port protocol (TCP/UDP) + protocol: TCP + # -- See [upstream documentation](https://kubernetes.io/docs/concepts/services-networking/service/#type-nodeport) + nodePort: # @schema type:[integer, null]; minimum:0 + # -- See [upstream documentation](https://kubernetes.io/docs/concepts/services-networking/service/#application-protocol) + appProtocol: # @schema type:[string, null] + # -- See [upstream documentation](https://doc.traefik.io/traefik/routing/entrypoints/#allowacmebypass) + allowACMEByPass: false + http3: + ## -- Enable HTTP/3 on the entrypoint + ## Enabling it will also enable http3 experimental feature + ## https://doc.traefik.io/traefik/routing/entrypoints/#http3 + ## There are known limitations when trying to listen on same ports for + ## TCP & UDP (Http3). There is a workaround in this chart using dual Service. + ## https://github.com/kubernetes/kubernetes/issues/47249#issuecomment-587960741 + enabled: false + advertisedPort: # @schema type:[integer, null]; minimum:0 + forwardedHeaders: + # -- Trust forwarded headers information (X-Forwarded-*). + trustedIPs: [] + insecure: false + proxyProtocol: + # -- Enable the Proxy Protocol header parsing for the entry point + trustedIPs: [] + insecure: false + # -- See [upstream documentation](https://doc.traefik.io/traefik/routing/entrypoints/#transport) + transport: + respondingTimeouts: + readTimeout: # @schema type:[string, integer, null] + writeTimeout: # @schema type:[string, integer, null] + idleTimeout: # @schema type:[string, integer, null] + lifeCycle: + requestAcceptGraceTimeout: # @schema type:[string, integer, null] + graceTimeOut: # @schema type:[string, integer, null] + keepAliveMaxRequests: # @schema type:[integer, null]; minimum:0 + keepAliveMaxTime: # @schema type:[string, integer, null] + # -- See [upstream documentation](https://doc.traefik.io/traefik/routing/entrypoints/#tls) + tls: + enabled: true + options: "" + certResolver: "" + domains: [] + # -- One can apply Middlewares on an entrypoint + # https://doc.traefik.io/traefik/middlewares/overview/ + # https://doc.traefik.io/traefik/routing/entrypoints/#middlewares + # -- /!\ It introduces here a link between your static configuration and your dynamic configuration /!\ + # It follows the provider naming convention: https://doc.traefik.io/traefik/providers/overview/#provider-namespace + # - namespace-name1@kubernetescrd + # - namespace-name2@kubernetescrd + middlewares: [] + metrics: + # -- When using hostNetwork, use another port to avoid conflict with node exporter: + # https://github.com/prometheus/prometheus/wiki/Default-port-allocations + port: 9100 + # -- You may not want to expose the metrics port on production deployments. + # If you want to access it from outside your cluster, + # use `kubectl port-forward` or create a secure ingress + expose: + default: false + # -- The exposed port for this service + exposedPort: 9100 + # -- The port protocol (TCP/UDP) + protocol: TCP + +# -- TLS Options are created as [TLSOption CRDs](https://doc.traefik.io/traefik/https/tls/#tls-options) +# When using `labelSelector`, you'll need to set labels on tlsOption accordingly. +# See EXAMPLE.md for details. +tlsOptions: {} + +# -- TLS Store are created as [TLSStore CRDs](https://doc.traefik.io/traefik/https/tls/#default-certificate). This is useful if you want to set a default certificate. See EXAMPLE.md for details. +tlsStore: {} + +service: + enabled: true + ## -- Single service is using `MixedProtocolLBService` feature gate. + ## -- When set to false, it will create two Service, one for TCP and one for UDP. + single: true + type: LoadBalancer + # -- Additional annotations applied to both TCP and UDP services (e.g. for cloud provider specific config) + annotations: {} + # -- Additional annotations for TCP service only + annotationsTCP: {} + # -- Additional annotations for UDP service only + annotationsUDP: {} + # -- Additional service labels (e.g. for filtering Service by custom labels) + labels: {} + # -- Additional entries here will be added to the service spec. + # -- Cannot contain type, selector or ports entries. + spec: {} + # externalTrafficPolicy: Cluster + # loadBalancerIP: "1.2.3.4" + # clusterIP: "2.3.4.5" + loadBalancerSourceRanges: [] + # - 192.168.0.1/32 + # - 172.16.0.0/16 + ## -- Class of the load balancer implementation + # loadBalancerClass: service.k8s.aws/nlb + externalIPs: [] + # - 1.2.3.4 + ## One of SingleStack, PreferDualStack, or RequireDualStack. + # ipFamilyPolicy: SingleStack + ## List of IP families (e.g. IPv4 and/or IPv6). + ## ref: https://kubernetes.io/docs/concepts/services-networking/dual-stack/#services + # ipFamilies: + # - IPv4 + # - IPv6 + ## + additionalServices: {} + ## -- An additional and optional internal Service. + ## Same parameters as external Service + # internal: + # type: ClusterIP + # # labels: {} + # # annotations: {} + # # spec: {} + # # loadBalancerSourceRanges: [] + # # externalIPs: [] + # # ipFamilies: [ "IPv4","IPv6" ] + +autoscaling: + # -- Create HorizontalPodAutoscaler object. + # See EXAMPLES.md for more details. + enabled: false + +persistence: + # -- Enable persistence using Persistent Volume Claims + # ref: http://kubernetes.io/docs/user-guide/persistent-volumes/ + # It can be used to store TLS certificates, see `storage` in certResolvers + enabled: false + name: data + existingClaim: "" + accessMode: ReadWriteOnce + size: 128Mi + storageClass: "" + volumeName: "" + path: /data + annotations: {} + # -- Only mount a subpath of the Volume into the pod + subPath: "" + +# -- Certificates resolvers configuration. +# Ref: https://doc.traefik.io/traefik/https/acme/#certificate-resolvers +# See EXAMPLES.md for more details. +certResolvers: {} + +# -- If hostNetwork is true, runs traefik in the host network namespace +# To prevent unschedulabel pods due to port collisions, if hostNetwork=true +# and replicas>1, a pod anti-affinity is recommended and will be set if the +# affinity is left as default. +hostNetwork: false + +# -- Whether Role Based Access Control objects like roles and rolebindings should be created +rbac: # @schema additionalProperties: false + enabled: true + # When set to true: + # 1. Use `Role` and `RoleBinding` instead of `ClusterRole` and `ClusterRoleBinding`. + # 2. Set `disableIngressClassLookup` on Kubernetes Ingress providers with Traefik Proxy v3 until v3.1.1 + # 3. Set `disableClusterScopeResources` on Kubernetes Ingress and CRD providers with Traefik Proxy v3.1.2+ + # **NOTE**: `IngressClass`, `NodePortLB` and **Gateway** provider cannot be used with namespaced RBAC. + # See [upstream documentation](https://doc.traefik.io/traefik/providers/kubernetes-ingress/#disableclusterscoperesources) for more details. + namespaced: false + # Enable user-facing roles + # https://kubernetes.io/docs/reference/access-authn-authz/rbac/#user-facing-roles + aggregateTo: [] + # List of Kubernetes secrets that are accessible for Traefik. If empty, then access is granted to every secret. + secretResourceNames: [] + +# -- Enable to create a PodSecurityPolicy and assign it to the Service Account via RoleBinding or ClusterRoleBinding +podSecurityPolicy: + enabled: false + +# -- The service account the pods will use to interact with the Kubernetes API +serviceAccount: # @schema additionalProperties: false + # If set, an existing service account is used + # If not set, a service account is created automatically using the fullname template + name: "" + +# -- Additional serviceAccount annotations (e.g. for oidc authentication) +serviceAccountAnnotations: {} + +# -- [Resources](https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/) for `traefik` container. +resources: {} + +# -- This example pod anti-affinity forces the scheduler to put traefik pods +# -- on nodes where no other traefik pods are scheduled. +# It should be used when hostNetwork: true to prevent port conflicts +affinity: {} +# podAntiAffinity: +# requiredDuringSchedulingIgnoredDuringExecution: +# - labelSelector: +# matchLabels: +# app.kubernetes.io/name: '{{ template "traefik.name" . }}' +# app.kubernetes.io/instance: '{{ .Release.Name }}-{{ .Release.Namespace }}' +# topologyKey: kubernetes.io/hostname + +# -- nodeSelector is the simplest recommended form of node selection constraint. +nodeSelector: {} +# -- Tolerations allow the scheduler to schedule pods with matching taints. +tolerations: [] +# -- You can use topology spread constraints to control +# how Pods are spread across your cluster among failure-domains. +topologySpreadConstraints: [] +# This example topologySpreadConstraints forces the scheduler to put traefik pods +# on nodes where no other traefik pods are scheduled. +# - labelSelector: +# matchLabels: +# app: '{{ template "traefik.name" . }}' +# maxSkew: 1 +# topologyKey: kubernetes.io/hostname +# whenUnsatisfiable: DoNotSchedule + +# -- [Pod Priority and Preemption](https://kubernetes.io/docs/concepts/scheduling-eviction/pod-priority-preemption/) +priorityClassName: "" + +# -- [SecurityContext](https://kubernetes.io/docs/reference/kubernetes-api/workload-resources/pod-v1/#security-context-1) +# @default -- See _values.yaml_ +securityContext: + allowPrivilegeEscalation: false + capabilities: + drop: [ALL] + readOnlyRootFilesystem: true + +# -- [Pod Security Context](https://kubernetes.io/docs/reference/kubernetes-api/workload-resources/pod-v1/#security-context) +# @default -- See _values.yaml_ +podSecurityContext: + runAsGroup: 65532 + runAsNonRoot: true + runAsUser: 65532 + +# +# -- Extra objects to deploy (value evaluated as a template) +# +# In some cases, it can avoid the need for additional, extended or adhoc deployments. +# See #595 for more details and traefik/tests/values/extra.yaml for example. +extraObjects: [] + +# -- This field override the default Release Namespace for Helm. +# It will not affect optional CRDs such as `ServiceMonitor` and `PrometheusRules` +namespaceOverride: "" + +## -- This field override the default app.kubernetes.io/instance label for all Objects. +instanceLabelOverride: "" + +# Traefik Hub configuration. See https://doc.traefik.io/traefik-hub/ +hub: + # -- Name of `Secret` with key 'token' set to a valid license token. + # It enables API Gateway. + token: "" + apimanagement: + # -- Set to true in order to enable API Management. Requires a valid license token. + enabled: false + admission: + # -- WebHook admission server listen address. Default: "0.0.0.0:9943". + listenAddr: "" + # -- Certificate of the WebHook admission server. Default: "hub-agent-cert". + secretName: "" + + ratelimit: + redis: + # -- Enable Redis Cluster. Default: true. + cluster: # @schema type:[boolean, null] + # -- Database used to store information. Default: "0". + database: # @schema type:[string, null] + # -- Endpoints of the Redis instances to connect to. Default: "". + endpoints: "" + # -- The username to use when connecting to Redis endpoints. Default: "". + username: "" + # -- The password to use when connecting to Redis endpoints. Default: "". + password: "" + sentinel: + # -- Name of the set of main nodes to use for main selection. Required when using Sentinel. Default: "". + masterset: "" + # -- Username to use for sentinel authentication (can be different from endpoint username). Default: "". + username: "" + # -- Password to use for sentinel authentication (can be different from endpoint password). Default: "". + password: "" + # -- Timeout applied on connection with redis. Default: "0s". + timeout: "" + tls: + # -- Path to the certificate authority used for the secured connection. + ca: "" + # -- Path to the public certificate used for the secure connection. + cert: "" + # -- Path to the private key used for the secure connection. + key: "" + # -- When insecureSkipVerify is set to true, the TLS connection accepts any certificate presented by the server. Default: false. + insecureSkipVerify: false + # Enable export of errors logs to the platform. Default: true. + sendlogs: # @schema type:[boolean, null] diff --git a/charts/trilio/k8s-triliovault-operator/4.0.5/.helmignore b/charts/trilio/k8s-triliovault-operator/4.0.5/.helmignore new file mode 100644 index 000000000..be86b789d --- /dev/null +++ b/charts/trilio/k8s-triliovault-operator/4.0.5/.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 +# Helm files +OWNERS diff --git a/charts/trilio/k8s-triliovault-operator/4.0.5/BUILD.bazel b/charts/trilio/k8s-triliovault-operator/4.0.5/BUILD.bazel new file mode 100644 index 000000000..c578eb034 --- /dev/null +++ b/charts/trilio/k8s-triliovault-operator/4.0.5/BUILD.bazel @@ -0,0 +1,9 @@ +load("@bazel_tools//tools/build_defs/pkg:pkg.bzl", "pkg_tar") + +pkg_tar( + name = "helm-tar", + files = glob(["**"]), + package_dir = "/opt/tvk/k8s-triliovault-operator/", + strip_prefix = "./", + visibility = ["//visibility:public"], +) diff --git a/charts/trilio/k8s-triliovault-operator/4.0.5/Chart.yaml b/charts/trilio/k8s-triliovault-operator/4.0.5/Chart.yaml new file mode 100644 index 000000000..db65bcca4 --- /dev/null +++ b/charts/trilio/k8s-triliovault-operator/4.0.5/Chart.yaml @@ -0,0 +1,24 @@ +annotations: + catalog.cattle.io/certified: partner + catalog.cattle.io/display-name: TrilioVault for Kubernetes Operator + catalog.cattle.io/kube-version: '>=1.19.0-0' + catalog.cattle.io/release-name: k8s-triliovault-operator +apiVersion: v2 +appVersion: 4.0.5 +dependencies: +- condition: observability.enabled + name: observability + repository: file://./charts/observability + version: ^0.1.0 +description: K8s-TrilioVault-Operator is an operator designed to manage the K8s-TrilioVault + Application Lifecycle. +home: https://github.com/trilioData/k8s-triliovault-operator +icon: file://assets/icons/k8s-triliovault-operator.png +kubeVersion: '>=1.19.0-0' +maintainers: +- email: prafull.ladha@trilio.io + name: prafull11 +name: k8s-triliovault-operator +sources: +- https://github.com/trilioData/k8s-triliovault-operator +version: 4.0.5 diff --git a/charts/trilio/k8s-triliovault-operator/4.0.5/LICENSE b/charts/trilio/k8s-triliovault-operator/4.0.5/LICENSE new file mode 100644 index 000000000..76b559d3b --- /dev/null +++ b/charts/trilio/k8s-triliovault-operator/4.0.5/LICENSE @@ -0,0 +1 @@ +# Placeholder for the License if we decide to provide one diff --git a/charts/trilio/k8s-triliovault-operator/4.0.5/README.md b/charts/trilio/k8s-triliovault-operator/4.0.5/README.md new file mode 100644 index 000000000..1c8cb3841 --- /dev/null +++ b/charts/trilio/k8s-triliovault-operator/4.0.5/README.md @@ -0,0 +1,206 @@ +# K8s-TrilioVault-Operator +This operator is to manage the lifecycle of TrilioVault Backup/Recovery solution. This operator install, updates and manage the TrilioVault application. + +## Introduction + +## Prerequisites + +- Kubernetes 1.19+ +- PV provisioner support +- CSI driver should be installed + +### One Click Installation + +In one click install for upstream operator, a cluster scope TVM custom resource `triliovault-manager` is created. + +```shell script +helm repo add trilio-vault-operator https://charts.k8strilio.net/trilio-stable/k8s-triliovault-operator +helm install tvm trilio-vault-operator/k8s-triliovault-operator +``` + +#### One click install with preflight Configuration + +The following table lists the configuration parameter of the upstream operator one click install feature as well as preflight check flags, their default values and usage. + +| Parameter | Description | Default | Example | +|--------------------------------------------------------------------|---------------------------------------------------------------------------------------------------|------------|-------------------------| +| `installTVK.enabled` | 1 click install feature is enabled | true | | +| `installTVK.applicationScope` | scope of TVK application created | Cluster | | +| `installTVK.tvkInstanceName` | tvk instance name | "" | "tvk-instance" | +| `installTVK.ingressConfig.host` | host of the ingress resource created | "" | | +| `installTVK.ingressConfig.tlsSecretName` | tls secret name which contains ingress certs | "" | | +| `installTVK.ingressConfig.annotations` | annotations to be added on ingress resource | "" | | +| `installTVK.ingressConfig.ingressClass` | ingress class name for the ingress resource | "" | | +| `installTVK.ComponentConfiguration.ingressController.enabled` | TVK ingress controller should be deployed | true | | +| `installTVK.ComponentConfiguration.ingressController.service.type` | TVK ingress controller service type | "NodePort" | | +| `preflight.enabled` | enables preflight check for tvk | false | | +| `preflight.storageClass` | Name of storage class to use for preflight checks (Required) | "" | | +| `preflight.cleanupOnFailure` | Cleanup the resources on cluster if preflight checks fail (Optional) | false | | +| `preflight.imagePullSecret` | Name of the secret for authentication while pulling the images from the local registry (Optional) | "" | | +| `preflight.limits` | Pod memory and cpu resource limits for DNS and volume snapshot preflight check (Optional) | "" | "cpu=600m,memory=256Mi" | +| `preflight.localRegistry` | Name of the local registry from where the images will be pulled (Optional) | "" | | +| `preflight.nodeSelector` | Node selector labels for pods to schedule on a specific nodes of cluster (Optional) | "" | "key=value" | +| `preflight.pvcStorageRequest` | PVC storage request for volume snapshot preflight check (Optional) | "" | "2Gi" | +| `preflight.requests` | Pod memory and cpu resource requests for DNS and volume snapshot preflight check (Optional) | "" | "cpu=300m,memory=128Mi" | +| `preflight.volumeSnapshotClass` | Name of volume snapshot class to use for preflight checks (Optional) | "" | | +| `preflight.logLevel` | Log Level for the preflight run (Default: "INFO") | "" | | +| `preflight.imageTag` | Image tag to use for the preflight image (Default: latest) | "" | | +| `nodeSelector` | Node selection constraints for scheduling Pods of this application. | {} | | +| `affinity` | Affinity rules for scheduling the Pod of this application. | {} | | +| `tolerations` | Taints to be tolerated by Pods of this application. | [] | | + + +Check the TVM CR configuration by running following command: + +``` +kubectl get triliovaultmanagers.triliovault.trilio.io triliovault-manager -o yaml +``` + +Once the operator pod is in running state, the TVK pods getting spawned. Confirm the [TVK pods are up](#Check-TVK-Install). + +#### Note: + +If preflight check is enabled and helm install fails, check pre-install helm hook pod logs for any failure in preflight check. Do the following steps: + +First, run this command: +``` +kubectl get pods -n +``` + +The pod name should start with `-preflight-job-preinstall-hook`. Check the logs of the pod by the following command: +``` +kubectl logs -f -n +``` + +#### The failed preflight job is not cleaned up automatically right after failure. If the user cluster version is 1.21 and above, the job will be cleaned up after 1 hour so user should collect any failure logs within 1 hr of job failure. For cluster version below 1.21, user has to clean up failed preflight job manually. + +To delete the job manually, run the following command: +``` +kubectl delete job -f -n +``` + +where job name should also start with `-preflight-job-preinstall-hook` + +Also, due to a bug at helm side where auto deletion of resources upon failure doesn't work, user needs to clean the following resources left behind to be able to run preflight again, until the bug is fixed from their side, after which this step will be handled automatically. Run the following command to clean up the temporary resources: + +1. Cleanup Service Account: + ``` + kubectl delete sa -preflight-service-account -n + ``` +2. Cleanup Cluster Role Binding: + ``` + kubectl delete clusterrolebinding --preflight-rolebinding + ``` +3. Cleanup Cluster Role: + ``` + kubectl delete clusterrole --preflight-role + ``` + +## Manual Installation + +To install the operator on local setup just run the latest helm charts inside this repo + +```shell script +helm repo add trilio-vault-operator https://charts.k8strilio.net/trilio-stable/k8s-triliovault-operator +helm install tvm trilio-vault-operator/k8s-triliovault-operator +``` + +Now, create a TrilioVaultManager CR to install the TrilioVault for Kubernetes. You can provide the custom configurations for the TVK resources as follows: + +``` +apiVersion: triliovault.trilio.io/v1 +kind: TrilioVaultManager +metadata: + labels: + triliovault: k8s + name: tvk +spec: + trilioVaultAppVersion: latest + applicationScope: Cluster + # User can configure tvk instance name + tvkInstanceName: tvk-instance + # User can configure the ingress hosts, annotations and TLS secret through the ingressConfig section + ingressConfig: + host: "trilio.co.in" + tlsSecretName: "secret-name" + # TVK components configuration, currently supports control-plane, web, exporter, web-backend, ingress-controller, admission-webhook. + # User can configure resources for all componentes and can configure service type and host for the ingress-controller + componentConfiguration: + web-backend: + resources: + requests: + memory: "400Mi" + cpu: "200m" + limits: + memory: "2584Mi" + cpu: "1000m" + ingress-controller: + enabled: true + service: + type: LoadBalancer +``` + +### Apply the Custom Resource + +Apply `TVM.yaml`: + +```shell +kubectl create -f TVM.yaml +``` + +### Check TVK Install + +Check that the pods were created: + +``` +kubectl get pods +``` + +``` +NAME READY STATUS RESTARTS AGE +k8s-triliovault-admission-webhook-6ff5f98c8-qwmfc 1/1 Running 0 81s +k8s-triliovault-backend-6f66b6b8d5-gxtmz 1/1 Running 0 81s +k8s-triliovault-control-plane-6c464c5d78-ftk6g 1/1 Running 0 81s +k8s-triliovault-exporter-59566f97dd-gs4xc 1/1 Running 0 81s +k8s-triliovault-ingress-nginx-controller-867c764cd5-qhpx6 1/1 Running 0 18s +k8s-triliovault-web-967c8475-m7pc6 1/1 Running 0 81s +tvm-k8s-triliovault-operator-66bd7d86d5-dvhzb 1/1 Running 0 6m48s +``` + +Check that ingress controller service is of type LoadBalancer: +``` +NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE +k8s-triliovault-admission-webhook ClusterIP 10.7.243.24 443/TCP 129m +k8s-triliovault-ingress-nginx-controller LoadBalancer 10.7.246.193 35.203.155.148 80:30362/TCP,443:32327/TCP 129m +k8s-triliovault-ingress-nginx-controller-admission ClusterIP 10.7.250.31 443/TCP 129m +k8s-triliovault-web ClusterIP 10.7.254.41 80/TCP 129m +k8s-triliovault-web-backend ClusterIP 10.7.252.146 80/TCP 129m +tvm-k8s-triliovault-operator-webhook-service ClusterIP 10.7.248.163 443/TCP 130m 123m +``` + +Check that ingress resources has the host defined by the user: +``` +NAME CLASS HOSTS ADDRESS PORTS AGE +k8s-triliovault k8s-triliovault-default-nginx * 35.203.155.148 80 129m +``` + +You can access the TVK UI by hitting this address in your browser: https://35.203.155.148 + +## Delete + +```shell +kubectl delete -f TVM.yaml +``` + +## Uninstall + +To uninstall/delete the operator helm chart : + +```bash +helm uninstall tvm +``` + +## TrilioVaultManager compatibility + +We maintain the version parity between the TrilioVaultManager(upstream operator) and TrilioVault for Kubernetes. Whenever +user wants to upgrade to the new version, should use the same version for upstream operator and Triliovault for Kubernetes. diff --git a/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/Chart.yaml b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/Chart.yaml new file mode 100644 index 000000000..4df538147 --- /dev/null +++ b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/Chart.yaml @@ -0,0 +1,21 @@ +apiVersion: v2 +appVersion: 0.1.0 +dependencies: +- name: visualization + repository: file://charts/visualization + version: ^0.1.0 +- name: logging + repository: file://charts/logging + version: ^0.1.0 +- name: monitoring + repository: file://charts/monitoring + version: ^0.1.0 +description: Observability Stack is designed to manage the K8s-TrilioVault Application's + Logging, Monitoring and Visualization. +icon: https://www.trilio.io/wp-content/uploads/2021/01/Trilio-2020-logo-RGB-gray-green.png +kubeVersion: '>=1.19.0-0' +maintainers: +- email: support@trilio.io + name: Trilio +name: observability +version: 0.1.0 diff --git a/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/logging/Chart.yaml b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/logging/Chart.yaml new file mode 100644 index 000000000..411d5b0fd --- /dev/null +++ b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/logging/Chart.yaml @@ -0,0 +1,18 @@ +apiVersion: v2 +appVersion: 0.1.0 +dependencies: +- condition: loki.enabled + name: loki + repository: https://grafana.github.io/helm-charts + version: ^2.15.2 +- condition: promtail.enabled + name: promtail + repository: https://grafana.github.io/helm-charts + version: ^6.7.4 +description: Logging Stack designed to manage the K8s-TrilioVault Application's Logs. +icon: https://www.trilio.io/wp-content/uploads/2021/01/Trilio-2020-logo-RGB-gray-green.png +maintainers: +- email: support@trilio.io + name: Trilio +name: logging +version: 0.1.0 diff --git a/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/logging/charts/loki/Chart.yaml b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/logging/charts/loki/Chart.yaml new file mode 100644 index 000000000..c10bab260 --- /dev/null +++ b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/logging/charts/loki/Chart.yaml @@ -0,0 +1,13 @@ +apiVersion: v1 +appVersion: v2.6.1 +description: 'Loki: like Prometheus, but for logs.' +home: https://grafana.com/loki +icon: https://raw.githubusercontent.com/grafana/loki/master/docs/sources/logo.png +kubeVersion: ^1.10.0-0 +maintainers: +- email: support@trilio.io + name: Trilio +name: loki +sources: +- https://github.com/grafana/loki +version: 2.16.0 diff --git a/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/logging/charts/loki/templates/NOTES.txt b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/logging/charts/loki/templates/NOTES.txt new file mode 100644 index 000000000..abe023a70 --- /dev/null +++ b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/logging/charts/loki/templates/NOTES.txt @@ -0,0 +1,3 @@ +Verify the application is working by running these commands: + kubectl --namespace {{ .Release.Namespace }} port-forward service/{{ include "loki.fullname" . }} {{ .Values.service.port }} + curl http://127.0.0.1:{{ .Values.service.port }}/api/prom/label diff --git a/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/logging/charts/loki/templates/_helpers.tpl b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/logging/charts/loki/templates/_helpers.tpl new file mode 100644 index 000000000..1ff9b632a --- /dev/null +++ b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/logging/charts/loki/templates/_helpers.tpl @@ -0,0 +1,99 @@ +{{/* vim: set filetype=mustache: */}} +{{/* +Expand the name of the chart. +*/}} +{{- define "loki.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 "loki.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 "loki.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}} +{{- end -}} + +{{/* +Create the name of the service account +*/}} +{{- define "loki.serviceAccountName" -}} +{{- if .Values.serviceAccount.create -}} + {{ default (include "loki.fullname" .) .Values.serviceAccount.name }} +{{- else -}} + {{ default "default" .Values.serviceAccount.name }} +{{- end -}} +{{- end -}} + +{{/* +Create the app name of loki clients. Defaults to the same logic as "loki.fullname", and default client expects "promtail". +*/}} +{{- define "client.name" -}} +{{- if .Values.client.name -}} +{{- .Values.client.name -}} +{{- else if .Values.client.fullnameOverride -}} +{{- .Values.client.fullnameOverride | trunc 63 | trimSuffix "-" -}} +{{- else -}} +{{- $name := default "promtail" .Values.client.nameOverride -}} +{{- if contains $name .Release.Name -}} +{{- .Release.Name | trunc 63 | trimSuffix "-" -}} +{{- else -}} +{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}} +{{- end -}} +{{- end -}} +{{- end -}} + +{{/* +Generate a right Ingress apiVersion +*/}} +{{- define "ingress.apiVersion" -}} +{{- if semverCompare ">=1.20-0" .Capabilities.KubeVersion.GitVersion -}} +networking.k8s.io/v1 +{{- else if semverCompare ">=1.14-0" .Capabilities.KubeVersion.GitVersion -}} +networking.k8s.io/v1beta1 +{{- else -}} +extensions/v1 +{{- end }} +{{- end -}} + +{{/* +Handle backwards compatible api versions for: + - podDisruptionBudget (policy/v1beta1) + - podSecurityPolicy (policy/v1beta1) +*/}} +{{- define "loki.podDisruptionBudget.apiVersion" -}} +{{ if $.Capabilities.APIVersions.Has "policy/v1/PodDisruptionBudgets" -}} +{{- print "policy/v1" -}} +{{- else -}} +{{- print "policy/v1beta1" -}} +{{- end -}} +{{- end -}} + + +{{/* +Common labels +*/}} +{{- define "loki.labels" -}} +app: {{ template "loki.name" . }} +chart: {{ template "loki.chart" . }} +release: {{ .Release.Name }} +heritage: {{ .Release.Service }} +{{- end }} + diff --git a/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/logging/charts/loki/templates/configmap-alert.yaml b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/logging/charts/loki/templates/configmap-alert.yaml new file mode 100644 index 000000000..45d72d8aa --- /dev/null +++ b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/logging/charts/loki/templates/configmap-alert.yaml @@ -0,0 +1,14 @@ +{{- if or (.Values.useExistingAlertingGroup.enabled) (gt (len .Values.alerting_groups) 0) }} +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ template "loki.fullname" . }}-alerting-rules + namespace: {{ .Release.Namespace }} + labels: + {{- include "loki.labels" . | nindent 4 }} +data: + {{ template "loki.fullname" . }}-alerting-rules.yaml: |- + groups: + {{- toYaml .Values.alerting_groups | nindent 6 }} +{{- end }} \ No newline at end of file diff --git a/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/logging/charts/loki/templates/ingress.yaml b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/logging/charts/loki/templates/ingress.yaml new file mode 100644 index 000000000..b12551437 --- /dev/null +++ b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/logging/charts/loki/templates/ingress.yaml @@ -0,0 +1,52 @@ +{{- if .Values.ingress.enabled -}} +{{- $fullName := include "loki.fullname" . -}} +{{- $svcPort := .Values.service.port -}} +{{- $apiVersion := include "ingress.apiVersion" . -}} +apiVersion: {{ $apiVersion }} +kind: Ingress +metadata: + name: {{ $fullName }} + namespace: {{ .Release.Namespace }} + labels: + {{- include "loki.labels" . | nindent 4 }} + {{- with .Values.ingress.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +spec: +{{- if .Values.ingress.ingressClassName }} + ingressClassName: {{ .Values.ingress.ingressClassName }} +{{- 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: {{ . }} + {{- if eq $apiVersion "networking.k8s.io/v1" }} + pathType: Prefix + {{- end }} + backend: + {{- if eq $apiVersion "networking.k8s.io/v1" }} + service: + name: {{ $fullName }} + port: + number: {{ $svcPort }} + {{- else }} + serviceName: {{ $fullName }} + servicePort: {{ $svcPort }} + {{- end }} + {{- end }} + {{- end }} +{{- end }} diff --git a/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/logging/charts/loki/templates/networkpolicy.yaml b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/logging/charts/loki/templates/networkpolicy.yaml new file mode 100644 index 000000000..b9349ba9d --- /dev/null +++ b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/logging/charts/loki/templates/networkpolicy.yaml @@ -0,0 +1,23 @@ +{{- if .Values.networkPolicy.enabled }} +apiVersion: networking.k8s.io/v1 +kind: NetworkPolicy +metadata: + name: {{ template "loki.fullname" . }} + namespace: {{ .Release.Namespace }} + labels: + {{- include "loki.labels" . | nindent 4 }} +spec: + podSelector: + matchLabels: + name: {{ template "loki.fullname" . }} + app: {{ template "loki.name" . }} + release: {{ .Release.Name }} + ingress: + - from: + - podSelector: + matchLabels: + app: {{ template "client.name" . }} + release: {{ .Release.Name }} + - ports: + - port: {{ .Values.service.port }} +{{- end }} diff --git a/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/logging/charts/loki/templates/pdb.yaml b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/logging/charts/loki/templates/pdb.yaml new file mode 100644 index 000000000..68dd619b7 --- /dev/null +++ b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/logging/charts/loki/templates/pdb.yaml @@ -0,0 +1,14 @@ +{{- if .Values.podDisruptionBudget -}} +apiVersion: {{ include "loki.podDisruptionBudget.apiVersion" . }} +kind: PodDisruptionBudget +metadata: + name: {{ template "loki.fullname" . }} + namespace: {{ .Release.Namespace }} + labels: + {{- include "loki.labels" . | nindent 4 }} +spec: + selector: + matchLabels: + app: {{ template "loki.name" . }} +{{ toYaml .Values.podDisruptionBudget | indent 2 }} +{{- end }} diff --git a/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/logging/charts/loki/templates/podsecuritypolicy.yaml b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/logging/charts/loki/templates/podsecuritypolicy.yaml new file mode 100644 index 000000000..c30ab49c1 --- /dev/null +++ b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/logging/charts/loki/templates/podsecuritypolicy.yaml @@ -0,0 +1,40 @@ +{{- if .Values.rbac.pspEnabled }} +{{- if .Capabilities.APIVersions.Has "policy/v1beta1/PodSecurityPolicy" }} +apiVersion: policy/v1beta1 +kind: PodSecurityPolicy +metadata: + name: {{ template "loki.fullname" . }} + labels: + {{- include "loki.labels" . | nindent 4 }} +spec: + privileged: false + allowPrivilegeEscalation: false + volumes: + - 'configMap' + - 'emptyDir' + - 'persistentVolumeClaim' + - 'secret' + - 'projected' + - 'downwardAPI' + hostNetwork: false + hostIPC: false + hostPID: false + runAsUser: + rule: 'MustRunAsNonRoot' + seLinux: + rule: 'RunAsAny' + supplementalGroups: + rule: 'MustRunAs' + ranges: + - min: 1 + max: 65535 + fsGroup: + rule: 'MustRunAs' + ranges: + - min: 1 + max: 65535 + readOnlyRootFilesystem: true + requiredDropCapabilities: + - ALL +{{- end }} +{{- end }} diff --git a/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/logging/charts/loki/templates/prometheusrule.yaml b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/logging/charts/loki/templates/prometheusrule.yaml new file mode 100644 index 000000000..d1ed09be5 --- /dev/null +++ b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/logging/charts/loki/templates/prometheusrule.yaml @@ -0,0 +1,20 @@ +{{- if and .Values.serviceMonitor.enabled .Values.serviceMonitor.prometheusRule.enabled -}} +apiVersion: monitoring.coreos.com/v1 +kind: PrometheusRule +metadata: + name: {{ template "loki.fullname" . }} +{{- if .Values.serviceMonitor.prometheusRule.namespace }} + namespace: {{ .Values.serviceMonitor.prometheusRule.namespace | quote }} +{{- end }} + labels: + {{- include "loki.labels" . | nindent 4 }} + {{- if .Values.serviceMonitor.prometheusRule.additionalLabels }} + {{- toYaml .Values.serviceMonitor.prometheusRule.additionalLabels | nindent 4 }} + {{- end }} +spec: +{{- if .Values.serviceMonitor.prometheusRule.rules }} + groups: + - name: {{ template "loki.fullname" . }} + rules: {{- toYaml .Values.serviceMonitor.prometheusRule.rules | nindent 4 }} +{{- end }} +{{- end }} diff --git a/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/logging/charts/loki/templates/role.yaml b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/logging/charts/loki/templates/role.yaml new file mode 100644 index 000000000..03b9da608 --- /dev/null +++ b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/logging/charts/loki/templates/role.yaml @@ -0,0 +1,17 @@ +{{- if .Values.rbac.create }} +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: {{ template "loki.fullname" . }} + namespace: {{ .Release.Namespace }} + labels: + {{- include "loki.labels" . | nindent 4 }} +{{- if .Values.rbac.pspEnabled }} +rules: +- apiGroups: ['extensions'] + resources: ['podsecuritypolicies'] + verbs: ['use'] + resourceNames: [{{ template "loki.fullname" . }}] +{{- end }} +{{- end }} + diff --git a/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/logging/charts/loki/templates/rolebinding.yaml b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/logging/charts/loki/templates/rolebinding.yaml new file mode 100644 index 000000000..099111de3 --- /dev/null +++ b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/logging/charts/loki/templates/rolebinding.yaml @@ -0,0 +1,17 @@ +{{- if .Values.rbac.create }} +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: {{ template "loki.fullname" . }} + namespace: {{ .Release.Namespace }} + labels: + {{- include "loki.labels" . | nindent 4 }} +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: {{ template "loki.fullname" . }} +subjects: +- kind: ServiceAccount + name: {{ template "loki.serviceAccountName" . }} +{{- end }} + diff --git a/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/logging/charts/loki/templates/secret.yaml b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/logging/charts/loki/templates/secret.yaml new file mode 100644 index 000000000..b4bee6a68 --- /dev/null +++ b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/logging/charts/loki/templates/secret.yaml @@ -0,0 +1,11 @@ +{{- if not .Values.config.existingSecret -}} +apiVersion: v1 +kind: Secret +metadata: + name: {{ template "loki.fullname" . }} + namespace: {{ .Release.Namespace }} + labels: + {{- include "loki.labels" . | nindent 4 }} +data: + loki.yaml: {{ tpl (toYaml .Values.config) . | b64enc}} +{{- end -}} diff --git a/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/logging/charts/loki/templates/service-headless.yaml b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/logging/charts/loki/templates/service-headless.yaml new file mode 100644 index 000000000..d97c36a20 --- /dev/null +++ b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/logging/charts/loki/templates/service-headless.yaml @@ -0,0 +1,26 @@ +apiVersion: v1 +kind: Service +metadata: + name: {{ template "loki.fullname" . }}-headless + namespace: {{ .Release.Namespace }} + labels: + {{- include "loki.labels" . | nindent 4 }} + app.kubernetes.io/instance: {{ template "loki.name" . }} + {{- include "k8s-triliovault-operator.observability" . | nindent 4 }} + {{- with .Values.service.labels }} + {{- toYaml . | nindent 4 }} + {{- end }} + variant: headless +spec: + clusterIP: None + ports: + - port: {{ .Values.service.port }} + protocol: TCP + name: http-metrics + targetPort: {{ .Values.service.targetPort }} +{{- if .Values.extraPorts }} +{{ toYaml .Values.extraPorts | indent 4}} +{{- end }} + selector: + app: {{ template "loki.name" . }} + release: {{ .Release.Name }} diff --git a/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/logging/charts/loki/templates/service-memberlist.yaml b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/logging/charts/loki/templates/service-memberlist.yaml new file mode 100644 index 000000000..27a885785 --- /dev/null +++ b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/logging/charts/loki/templates/service-memberlist.yaml @@ -0,0 +1,21 @@ +{{- if .Values.config.memberlist -}} +apiVersion: v1 +kind: Service +metadata: + name: {{ include "loki.fullname" . }}-memberlist + namespace: {{ .Release.Namespace }} + labels: + {{- include "loki.labels" . | nindent 4 }} +spec: + type: ClusterIP + clusterIP: None + publishNotReadyAddresses: true + ports: + - name: http + port: {{ .Values.config.memberlist.bind_port | default 7946 }} + targetPort: memberlist-port + protocol: TCP + selector: + app: {{ template "loki.name" . }} + release: {{ .Release.Name }} +{{- end -}} diff --git a/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/logging/charts/loki/templates/service.yaml b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/logging/charts/loki/templates/service.yaml new file mode 100644 index 000000000..1a8877924 --- /dev/null +++ b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/logging/charts/loki/templates/service.yaml @@ -0,0 +1,43 @@ +apiVersion: v1 +kind: Service +metadata: + name: {{ template "loki.fullname" . }} + namespace: {{ .Release.Namespace }} + labels: + {{- include "loki.labels" . | nindent 4 }} + {{- with .Values.service.labels }} + {{- toYaml . | nindent 4 }} + {{- end }} + annotations: + {{- toYaml .Values.service.annotations | nindent 4 }} +spec: +{{- if .Values.service.loadBalancerSourceRanges }} + externalTrafficPolicy: {{ .Values.service.externalTrafficPolicy }} +{{- end }} + type: {{ .Values.service.type }} +{{- if (and (eq .Values.service.type "ClusterIP") (not (empty .Values.service.clusterIP))) }} + clusterIP: {{ .Values.service.clusterIP }} +{{- end }} +{{- if (and (eq .Values.service.type "LoadBalancer") (not (empty .Values.service.loadBalancerIP))) }} + loadBalancerIP: {{ .Values.service.loadBalancerIP }} +{{- end }} +{{- if .Values.service.loadBalancerSourceRanges }} + loadBalancerSourceRanges: + {{- range $cidr := .Values.service.loadBalancerSourceRanges }} + - {{ $cidr }} + {{- end }} +{{- end }} + ports: + - port: {{ .Values.service.port }} + protocol: TCP + name: http-metrics + targetPort: {{ .Values.service.targetPort }} +{{- if (and (eq .Values.service.type "NodePort") (not (empty .Values.service.nodePort))) }} + nodePort: {{ .Values.service.nodePort }} +{{- end }} +{{- if .Values.extraPorts }} +{{ toYaml .Values.extraPorts | indent 4}} +{{- end }} + selector: + app: {{ template "loki.name" . }} + release: {{ .Release.Name }} diff --git a/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/logging/charts/loki/templates/serviceaccount.yaml b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/logging/charts/loki/templates/serviceaccount.yaml new file mode 100644 index 000000000..6db005d14 --- /dev/null +++ b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/logging/charts/loki/templates/serviceaccount.yaml @@ -0,0 +1,13 @@ +{{- if .Values.serviceAccount.create }} +apiVersion: v1 +kind: ServiceAccount +metadata: + labels: + {{- include "loki.labels" . | nindent 4 }} + annotations: + {{- toYaml .Values.serviceAccount.annotations | nindent 4 }} + name: {{ template "loki.serviceAccountName" . }} + namespace: {{ .Release.Namespace }} +automountServiceAccountToken: {{ .Values.serviceAccount.automountServiceAccountToken }} +{{- end }} + diff --git a/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/logging/charts/loki/templates/servicemonitor.yaml b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/logging/charts/loki/templates/servicemonitor.yaml new file mode 100644 index 000000000..dd84a3a45 --- /dev/null +++ b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/logging/charts/loki/templates/servicemonitor.yaml @@ -0,0 +1,42 @@ +{{- if .Values.serviceMonitor.enabled }} +apiVersion: monitoring.coreos.com/v1 +kind: ServiceMonitor +metadata: + name: {{ template "loki.fullname" . }} + labels: + {{- include "loki.labels" . | nindent 4 }} + {{- if .Values.serviceMonitor.additionalLabels }} +{{ toYaml .Values.serviceMonitor.additionalLabels | indent 4 }} + {{- end }} + {{- if .Values.serviceMonitor.annotations }} + annotations: +{{ toYaml .Values.serviceMonitor.annotations | indent 4 }} + {{- end }} +spec: + selector: + matchLabels: + app: {{ template "loki.name" . }} + release: {{ .Release.Name | quote }} + variant: headless + namespaceSelector: + matchNames: + - {{ .Release.Namespace | quote }} + endpoints: + - port: http-metrics + {{- if .Values.serviceMonitor.interval }} + interval: {{ .Values.serviceMonitor.interval }} + {{- end }} + {{- if .Values.serviceMonitor.scrapeTimeout }} + scrapeTimeout: {{ .Values.serviceMonitor.scrapeTimeout }} + {{- end }} + {{- if .Values.serviceMonitor.path }} + path: {{ .Values.serviceMonitor.path }} + {{- end }} + {{- with .Values.serviceMonitor.scheme }} + scheme: {{ . }} + {{- end }} + {{- with .Values.serviceMonitor.tlsConfig }} + tlsConfig: + {{- toYaml . | nindent 6 }} + {{- end }} +{{- end }} diff --git a/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/logging/charts/loki/templates/statefulset.yaml b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/logging/charts/loki/templates/statefulset.yaml new file mode 100644 index 000000000..9e1a49ca1 --- /dev/null +++ b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/logging/charts/loki/templates/statefulset.yaml @@ -0,0 +1,180 @@ +apiVersion: apps/v1 +kind: StatefulSet +metadata: + name: {{ template "loki.fullname" . }} + namespace: {{ .Release.Namespace }} + labels: + {{- include "loki.labels" . | nindent 4 }} + app.kubernetes.io/instance: {{ .Release.Name }} + {{- include "k8s-triliovault-operator.observability" . | nindent 4 }} + annotations: + {{- toYaml .Values.annotations | nindent 4 }} +spec: + podManagementPolicy: {{ .Values.podManagementPolicy }} + replicas: {{ .Values.replicas }} + selector: + matchLabels: + app: {{ template "loki.name" . }} + release: {{ .Release.Name }} + app.kubernetes.io/instance: {{ .Release.Name }} + {{- include "k8s-triliovault-operator.observability" . | nindent 6 }} + serviceName: {{ template "loki.fullname" . }}-headless + updateStrategy: + {{- toYaml .Values.updateStrategy | nindent 4 }} + template: + metadata: + labels: + app: {{ template "loki.name" . }} + name: {{ template "loki.fullname" . }} + release: {{ .Release.Name }} + app.kubernetes.io/instance: {{ .Release.Name }} + {{- include "k8s-triliovault-operator.observability" . | nindent 8 }} + {{- with .Values.podLabels }} + {{- toYaml . | nindent 8 }} + {{- end }} + annotations: + {{- if not .Values.config.existingSecret }} + checksum/config: {{ include (print $.Template.BasePath "/secret.yaml") . | sha256sum }} + {{- end }} + {{- with .Values.podAnnotations }} + {{- toYaml . | nindent 8 }} + {{- end }} + spec: + serviceAccountName: {{ template "loki.serviceAccountName" . }} + {{- if .Values.priorityClassName }} + priorityClassName: {{ .Values.priorityClassName }} + {{- end }} + securityContext: + {{- toYaml .Values.securityContext | nindent 8 }} + initContainers: + {{- toYaml .Values.initContainers | nindent 8 }} + {{- if .Values.image.pullSecrets }} + imagePullSecrets: + {{- range .Values.image.pullSecrets }} + - name: {{ . }} + {{- end}} + {{- end }} + containers: + - name: {{ .Chart.Name }} + image: "{{ .Values.image.registry }}/{{ .Values.image.repository }}:{{ .Values.image.tag }}" + imagePullPolicy: {{ .Values.image.pullPolicy }} + args: + - "-config.file=/etc/loki/loki.yaml" + {{- range $key, $value := .Values.extraArgs }} + - "-{{ $key }}={{ $value }}" + {{- end }} + volumeMounts: + - name: tmp + mountPath: /tmp + {{- if .Values.extraVolumeMounts }} + {{ toYaml .Values.extraVolumeMounts | nindent 12}} + {{- end }} + - name: config + mountPath: /etc/loki + - name: storage + mountPath: "/data" + subPath: {{ .Values.persistence.subPath }} + {{- if or (.Values.useExistingAlertingGroup.enabled) (gt (len .Values.alerting_groups) 0) }} + - name: rules + mountPath: /rules/fake + {{- end }} + ports: + - name: http-metrics + containerPort: {{ .Values.config.server.http_listen_port | default 3100 }} + protocol: TCP + - name: grpc + containerPort: {{ .Values.config.server.grpc_listen_port | default 9095 }} + protocol: TCP + {{- if .Values.config.memberlist }} + - name: memberlist-port + containerPort: {{ .Values.config.memberlist.bind_port | default 7946 }} + protocol: TCP + {{- end }} + livenessProbe: + {{- toYaml .Values.livenessProbe | nindent 12 }} + readinessProbe: + {{- toYaml .Values.readinessProbe | nindent 12 }} + resources: + {{- toYaml .Values.resources | nindent 12 }} + securityContext: + {{- toYaml .Values.containerSecurityContext | nindent 12 }} + env: + {{- if .Values.env }} + {{- toYaml .Values.env | nindent 12 }} + {{- end }} + {{- if .Values.tracing.jaegerAgentHost }} + - name: JAEGER_AGENT_HOST + value: "{{ .Values.tracing.jaegerAgentHost }}" + {{- end }} + {{- with .Values.extraEnvFrom }} + envFrom: + {{- toYaml . | nindent 12 }} + {{- end }} +{{- if .Values.extraContainers }} +{{ toYaml .Values.extraContainers | indent 8}} +{{- end }} + nodeSelector: + {{- toYaml .Values.nodeSelector | nindent 8 }} + affinity: + {{- toYaml .Values.affinity | nindent 8 }} + tolerations: + {{- toYaml .Values.tolerations | nindent 8 }} + {{- if .Values.topologySpreadConstraints.enabled }} + topologySpreadConstraints: + - maxSkew: {{ .Values.topologySpreadConstraints.maxSkew | default 1 }} + topologyKey: {{ .Values.topologySpreadConstraints.topologyKey | default "topology.kubernetes.io/zone" }} + whenUnsatisfiable: {{ .Values.topologySpreadConstraints.whenUnsatisfiable | default "ScheduleAnyway" }} + matchLabels: + app: {{ template "loki.name" . }} + release: {{ .Release.Name }} + {{- end }} + terminationGracePeriodSeconds: {{ .Values.terminationGracePeriodSeconds }} + volumes: + - name: tmp + emptyDir: {} + {{- if or (.Values.useExistingAlertingGroup.enabled) (gt (len .Values.alerting_groups) 0) }} + - name: rules + configMap: + {{- if .Values.useExistingAlertingGroup.enabled }} + name: {{ .Values.useExistingAlertingGroup.configmapName }} + {{- else }} + name: {{ template "loki.fullname" . }}-alerting-rules + {{- end }} + {{- end }} + - name: config + secret: + {{- if .Values.config.existingSecret }} + secretName: {{ .Values.config.existingSecret }} + {{- else }} + secretName: {{ template "loki.fullname" . }} + {{- end }} +{{- if .Values.extraVolumes }} +{{ toYaml .Values.extraVolumes | indent 8}} +{{- end }} + {{- if not .Values.persistence.enabled }} + - name: storage + emptyDir: {} + {{- else if .Values.persistence.existingClaim }} + - name: storage + persistentVolumeClaim: + claimName: {{ .Values.persistence.existingClaim }} + {{- else }} + volumeClaimTemplates: + - metadata: + name: storage + labels: + {{- toYaml .Values.persistence.labels | nindent 8 }} + annotations: + {{- toYaml .Values.persistence.annotations | nindent 8 }} + spec: + accessModes: + {{- toYaml .Values.persistence.accessModes | nindent 8 }} + resources: + requests: + storage: {{ .Values.persistence.size | quote }} + storageClassName: {{ .Values.persistence.storageClassName }} + {{- if .Values.persistence.selector }} + selector: + {{- toYaml .Values.persistence.selector | nindent 8 }} + {{- end }} + {{- end }} diff --git a/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/logging/charts/loki/values.yaml b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/logging/charts/loki/values.yaml new file mode 100644 index 000000000..731c4d906 --- /dev/null +++ b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/logging/charts/loki/values.yaml @@ -0,0 +1,346 @@ +image: + registry: docker.io + repository: grafana/loki + tag: 2.6.1 + 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 + +ingress: + 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 + annotations: {} + # kubernetes.io/ingress.class: nginx + # kubernetes.io/tls-acme: "true" + hosts: + - host: chart-example.local + paths: [] + tls: [] + # - secretName: chart-example-tls + # hosts: + # - chart-example.local + +## Affinity for pod assignment +## ref: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#affinity-and-anti-affinity +affinity: {} +# podAntiAffinity: +# requiredDuringSchedulingIgnoredDuringExecution: +# - labelSelector: +# matchExpressions: +# - key: app +# operator: In +# values: +# - loki +# topologyKey: "kubernetes.io/hostname" + +## StatefulSet annotations +annotations: {} + +# enable tracing for debug, need install jaeger and specify right jaeger_agent_host +tracing: + jaegerAgentHost: + +config: + # existingSecret: + auth_enabled: false + + memberlist: + join_members: + # the value must be defined as string to be evaluated when secret manifest is being generating + - '{{ include "loki.fullname" . }}-memberlist' + + ingester: + chunk_idle_period: 3m + chunk_block_size: 262144 + chunk_retain_period: 1m + max_transfer_retries: 0 + wal: + dir: /data/loki/wal + lifecycler: + ring: + replication_factor: 1 + + ## Different ring configs can be used. E.g. Consul + # ring: + # store: consul + # replication_factor: 1 + # consul: + # host: "consul:8500" + # prefix: "" + # http_client_timeout: "20s" + # consistent_reads: true + limits_config: + enforce_metric_name: false + reject_old_samples: true + reject_old_samples_max_age: 168h + max_entries_limit_per_query: 5000 + max_query_length: 0h + schema_config: + configs: + - from: 2020-10-24 + store: boltdb-shipper + object_store: filesystem + schema: v11 + index: + prefix: index_ + period: 24h + server: + http_listen_port: 3100 + grpc_listen_port: 9095 + storage_config: + boltdb_shipper: + active_index_directory: /data/loki/boltdb-shipper-active + cache_location: /data/loki/boltdb-shipper-cache + cache_ttl: 24h # Can be increased for faster performance over longer query periods, uses more disk space + shared_store: filesystem + filesystem: + directory: /data/loki/chunks + chunk_store_config: + max_look_back_period: 0s + table_manager: + retention_deletes_enabled: true + retention_period: 168h + compactor: + working_directory: /data/loki/boltdb-shipper-compactor + shared_store: filesystem +# Needed for Alerting: https://grafana.com/docs/loki/latest/rules/ +# This is just a simple example, for more details: https://grafana.com/docs/loki/latest/configuration/#ruler_config +# ruler: +# storage: +# type: local +# local: +# directory: /rules +# rule_path: /tmp/scratch +# alertmanager_url: http://alertmanager.svc.namespace:9093 +# ring: +# kvstore: +# store: inmemory +# enable_api: true + +## Additional Loki container arguments, e.g. log level (debug, info, warn, error) +extraArgs: {} + # log.level: debug + +extraEnvFrom: [] + +livenessProbe: + httpGet: + path: /ready + port: http-metrics + initialDelaySeconds: 45 + +## ref: https://kubernetes.io/docs/concepts/services-networking/network-policies/ +networkPolicy: + enabled: false + +## The app name of loki clients +client: {} + # name: + +## ref: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/ +nodeSelector: {} + +## ref: https://kubernetes.io/docs/concepts/storage/persistent-volumes/ +## If you set enabled as "True", you need : +## - create a pv which above 10Gi and has same namespace with loki +## - keep storageClassName same with below setting +persistence: + enabled: false + accessModes: + - ReadWriteOnce + size: 10Gi + labels: {} + annotations: {} + # selector: + # matchLabels: + # app.kubernetes.io/name: loki + # subPath: "" + # existingClaim: + # storageClassName: + +## Pod Labels +podLabels: {} + +## Pod Annotations +podAnnotations: + prometheus.io/scrape: "true" + prometheus.io/port: "http-metrics" + +podManagementPolicy: OrderedReady + +## Assign a PriorityClassName to pods if set +# priorityClassName: + +rbac: + create: true + pspEnabled: false + +readinessProbe: + httpGet: + path: /ready + port: http-metrics + initialDelaySeconds: 45 + +replicas: 1 + +resources: + limits: + cpu: 400m + memory: 512Mi + requests: + cpu: 100m + memory: 128Mi + +securityContext: + fsGroup: 10001 + runAsGroup: 10001 + runAsNonRoot: true + runAsUser: 10001 + +containerSecurityContext: + readOnlyRootFilesystem: true + +service: + type: ClusterIP + nodePort: + port: 3100 + annotations: {} + labels: {} + targetPort: http-metrics + +serviceAccount: + create: true + name: + annotations: {} + automountServiceAccountToken: true + +terminationGracePeriodSeconds: 4800 + +## Tolerations for pod assignment +## ref: https://kubernetes.io/docs/concepts/configuration/taint-and-toleration/ +tolerations: [] + +## Topology spread constraint for multi-zone clusters +## ref: https://kubernetes.io/docs/concepts/workloads/pods/pod-topology-spread-constraints/ +topologySpreadConstraints: + enabled: false + +# The values to set in the PodDisruptionBudget spec +# If not set then a PodDisruptionBudget will not be created +podDisruptionBudget: {} +# minAvailable: 1 +# maxUnavailable: 1 + +updateStrategy: + type: RollingUpdate + +serviceMonitor: + enabled: false + interval: "" + additionalLabels: {} + annotations: {} + # scrapeTimeout: 10s + # path: /metrics + scheme: null + tlsConfig: {} + prometheusRule: + enabled: false + additionalLabels: {} + # namespace: + rules: [] + # Some examples from https://awesome-prometheus-alerts.grep.to/rules.html#loki + # - alert: LokiProcessTooManyRestarts + # expr: changes(process_start_time_seconds{job=~"loki"}[15m]) > 2 + # for: 0m + # labels: + # severity: warning + # annotations: + # summary: Loki process too many restarts (instance {{ $labels.instance }}) + # description: "A loki process had too many restarts (target {{ $labels.instance }})\n VALUE = {{ $value }}\n LABELS = {{ $labels }}" + # - alert: LokiRequestErrors + # expr: 100 * sum(rate(loki_request_duration_seconds_count{status_code=~"5.."}[1m])) by (namespace, job, route) / sum(rate(loki_request_duration_seconds_count[1m])) by (namespace, job, route) > 10 + # for: 15m + # labels: + # severity: critical + # annotations: + # summary: Loki request errors (instance {{ $labels.instance }}) + # description: "The {{ $labels.job }} and {{ $labels.route }} are experiencing errors\n VALUE = {{ $value }}\n LABELS = {{ $labels }}" + # - alert: LokiRequestPanic + # expr: sum(increase(loki_panic_total[10m])) by (namespace, job) > 0 + # for: 5m + # labels: + # severity: critical + # annotations: + # summary: Loki request panic (instance {{ $labels.instance }}) + # description: "The {{ $labels.job }} is experiencing {{ printf \"%.2f\" $value }}% increase of panics\n VALUE = {{ $value }}\n LABELS = {{ $labels }}" + # - alert: LokiRequestLatency + # expr: (histogram_quantile(0.99, sum(rate(loki_request_duration_seconds_bucket{route!~"(?i).*tail.*"}[5m])) by (le))) > 1 + # for: 5m + # labels: + # severity: critical + # annotations: + # summary: Loki request latency (instance {{ $labels.instance }}) + # description: "The {{ $labels.job }} {{ $labels.route }} is experiencing {{ printf \"%.2f\" $value }}s 99th percentile latency\n VALUE = {{ $value }}\n LABELS = {{ $labels }}" + + +initContainers: [] +## Init containers to be added to the loki pod. +# - name: my-init-container +# image: busybox:latest +# command: ['sh', '-c', 'echo hello'] + +extraContainers: [] +## Additional containers to be added to the loki pod. +# - name: reverse-proxy +# image: angelbarrera92/basic-auth-reverse-proxy:dev +# args: +# - "serve" +# - "--upstream=http://localhost:3100" +# - "--auth-config=/etc/reverse-proxy-conf/authn.yaml" +# ports: +# - name: http +# containerPort: 11811 +# protocol: TCP +# volumeMounts: +# - name: reverse-proxy-auth-config +# mountPath: /etc/reverse-proxy-conf + + +extraVolumes: [] +## Additional volumes to the loki pod. +# - name: reverse-proxy-auth-config +# secret: +# secretName: reverse-proxy-auth-config + +## Extra volume mounts that will be added to the loki container +extraVolumeMounts: [] + +extraPorts: [] +## Additional ports to the loki services. Useful to expose extra container ports. +# - port: 11811 +# protocol: TCP +# name: http +# targetPort: http + +# Extra env variables to pass to the loki container +env: [] + +# Specify Loki Alerting rules based on this documentation: https://grafana.com/docs/loki/latest/rules/ +# When specified, you also need to add a ruler config section above. An example is shown in the rules docs. +alerting_groups: [] +# - name: example +# rules: +# - alert: HighThroughputLogStreams +# expr: sum by(container) (rate({job=~"loki-dev/.*"}[1m])) > 1000 +# for: 2m + +useExistingAlertingGroup: + enabled: false + configmapName: "" diff --git a/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/logging/charts/promtail/Chart.yaml b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/logging/charts/promtail/Chart.yaml new file mode 100644 index 000000000..d69c5a044 --- /dev/null +++ b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/logging/charts/promtail/Chart.yaml @@ -0,0 +1,16 @@ +apiVersion: v2 +appVersion: 2.7.2 +description: Promtail is an agent which ships the contents of local logs to a Loki + instance +home: https://grafana.com/loki +icon: https://raw.githubusercontent.com/grafana/loki/master/docs/sources/logo.png +maintainers: +- email: support@trilio.io + name: Trilio +name: promtail +sources: +- https://github.com/grafana/loki +- https://grafana.com/oss/loki/ +- https://grafana.com/docs/loki/latest/ +type: application +version: 6.8.2 diff --git a/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/logging/charts/promtail/templates/NOTES.txt b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/logging/charts/promtail/templates/NOTES.txt new file mode 100644 index 000000000..01bf66b7f --- /dev/null +++ b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/logging/charts/promtail/templates/NOTES.txt @@ -0,0 +1,15 @@ +*********************************************************************** + Welcome to Grafana Promtail + Chart version: {{ .Chart.Version }} + Promtail version: {{ .Values.image.tag | default .Chart.AppVersion }} +*********************************************************************** + +Verify the application is working by running these commands: + +{{- if .Values.daemonset.enabled }} +* kubectl --namespace {{ .Release.Namespace }} port-forward daemonset/{{ include "promtail.fullname" . }} {{ .Values.config.serverPort }} +{{- end }} +{{- if .Values.deployment.enabled }} +* kubectl --namespace {{ .Release.Namespace }} port-forward deployment/{{ include "promtail.fullname" . }} {{ .Values.config.serverPort }} +{{- end }} +* curl http://127.0.0.1:{{ .Values.config.serverPort }}/metrics diff --git a/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/logging/charts/promtail/templates/_helpers.tpl b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/logging/charts/promtail/templates/_helpers.tpl new file mode 100644 index 000000000..59053c253 --- /dev/null +++ b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/logging/charts/promtail/templates/_helpers.tpl @@ -0,0 +1,102 @@ +{{/* +Expand the name of the chart. +*/}} +{{- define "promtail.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 "promtail.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 "promtail.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Common labels +*/}} +{{- define "promtail.labels" -}} +helm.sh/chart: {{ include "promtail.chart" . }} +{{ include "promtail.selectorLabels" . }} +{{- if .Chart.AppVersion }} +app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} +{{- end }} +app.kubernetes.io/managed-by: {{ .Release.Service }} +{{- end }} + +{{/* +Selector labels +*/}} +{{- define "promtail.selectorLabels" -}} +app.kubernetes.io/name: {{ include "promtail.name" . }} +app.kubernetes.io/instance: {{ .Release.Name }} +{{ include "k8s-triliovault-operator.observability" .}} +{{- end }} + +{{/* +Create the name of the namespace +*/}} +{{- define "promtail.namespaceName" -}} +{{- default .Release.Namespace .Values.namespace }} +{{- end }} + +{{/* +Create the name of the service account +*/}} +{{- define "promtail.serviceAccountName" -}} +{{- if .Values.serviceAccount.create }} +{{- default (include "promtail.fullname" .) .Values.serviceAccount.name }} +{{- else }} +{{- default "default" .Values.serviceAccount.name }} +{{- end }} +{{- end }} + +{{/* +The service name to connect to Loki. Defaults to the same logic as "loki.fullname" +*/}} +{{- define "loki.serviceName" -}} +{{- if .Values.loki.serviceName }} +{{- .Values.loki.serviceName }} +{{- else if .Values.loki.fullnameOverride }} +{{- .Values.loki.fullnameOverride | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- $name := default "loki" .Values.loki.nameOverride }} +{{- if contains $name .Release.Name }} +{{- .Release.Name | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }} +{{- end }} +{{- end }} +{{- end }} + +{{/* +Configure enableServiceLinks in pod +*/}} +{{- define "promtail.enableServiceLinks" -}} +{{- if semverCompare ">=1.13-0" .Capabilities.KubeVersion.GitVersion }} +{{- if or (.Values.enableServiceLinks) (eq (.Values.enableServiceLinks | toString) "") }} +{{- printf "enableServiceLinks: true" }} +{{- else }} +{{- printf "enableServiceLinks: false" }} +{{- end }} +{{- end }} +{{- end }} + diff --git a/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/logging/charts/promtail/templates/_pod.tpl b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/logging/charts/promtail/templates/_pod.tpl new file mode 100644 index 000000000..d5a14b411 --- /dev/null +++ b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/logging/charts/promtail/templates/_pod.tpl @@ -0,0 +1,118 @@ +{{/* +Pod template used in Daemonset and Deployment +*/}} +{{- define "promtail.podTemplate" -}} +metadata: + labels: + {{- include "promtail.selectorLabels" . | nindent 4 }} + {{- with .Values.podLabels }} + {{- toYaml . | nindent 4 }} + {{- end }} + annotations: + checksum/config: {{ include (print .Template.BasePath "/secret.yaml") . | sha256sum }} + {{- with .Values.podAnnotations }} + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + serviceAccountName: {{ include "promtail.serviceAccountName" . }} + {{- include "promtail.enableServiceLinks" . | nindent 2 }} + {{- with .Values.priorityClassName }} + priorityClassName: {{ . }} + {{- end }} + {{- with .Values.initContainer }} + initContainers: + {{- toYaml . | nindent 4 }} + {{- end }} + {{- with .Values.imagePullSecrets }} + imagePullSecrets: + {{- toYaml . | nindent 4 }} + {{- end }} + securityContext: + {{- toYaml .Values.podSecurityContext | nindent 4 }} + containers: + - name: promtail + image: "{{ .Values.image.registry }}/{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}" + imagePullPolicy: {{ .Values.image.pullPolicy }} + args: + - "-config.file=/etc/promtail/promtail.yaml" + {{- with .Values.extraArgs }} + {{- toYaml . | nindent 8 }} + {{- end }} + volumeMounts: + - name: config + mountPath: /etc/promtail + {{- with .Values.defaultVolumeMounts }} + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.extraVolumeMounts }} + {{- toYaml . | nindent 8 }} + {{- end }} + env: + - name: HOSTNAME + valueFrom: + fieldRef: + fieldPath: spec.nodeName + {{- with .Values.extraEnv }} + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.extraEnvFrom }} + envFrom: + {{- toYaml . | nindent 8 }} + {{- end }} + ports: + - name: http-metrics + containerPort: {{ .Values.config.serverPort }} + protocol: TCP + {{- range $key, $values := .Values.extraPorts }} + - name: {{ .name | default $key }} + containerPort: {{ $values.containerPort }} + protocol: {{ $values.protocol | default "TCP" }} + {{- end }} + securityContext: + {{- toYaml .Values.containerSecurityContext | nindent 8 }} + {{- with .Values.livenessProbe }} + livenessProbe: + {{- tpl (toYaml .) $ | nindent 8 }} + {{- end }} + {{- with .Values.readinessProbe }} + readinessProbe: + {{- tpl (toYaml .) $ | nindent 8 }} + {{- end }} + {{- with .Values.resources }} + resources: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- if .Values.extraContainers }} + {{- range $name, $values := .Values.extraContainers }} + - name: {{ $name }} + {{ toYaml $values | nindent 6 }} + {{- end }} + {{- end }} + {{- with .Values.affinity }} + affinity: + {{- toYaml . | nindent 4 }} + {{- end }} + {{- with .Values.nodeSelector }} + nodeSelector: + {{- toYaml . | nindent 4 }} + {{- end }} + {{- with .Values.tolerations }} + tolerations: + {{- toYaml . | nindent 4 }} + {{- end }} + volumes: + - name: config + {{- if .Values.configmap.enabled }} + configMap: + name: {{ include "promtail.fullname" . }} + {{- else }} + secret: + secretName: {{ include "promtail.fullname" . }} + {{- end }} + {{- with .Values.defaultVolumes }} + {{- toYaml . | nindent 4 }} + {{- end }} + {{- with .Values.extraVolumes }} + {{- toYaml . | nindent 4 }} + {{- end }} +{{- end }} diff --git a/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/logging/charts/promtail/templates/clusterrole.yaml b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/logging/charts/promtail/templates/clusterrole.yaml new file mode 100644 index 000000000..4702e60d0 --- /dev/null +++ b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/logging/charts/promtail/templates/clusterrole.yaml @@ -0,0 +1,21 @@ +{{- if .Values.rbac.create }} +kind: ClusterRole +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: {{ include "promtail.fullname" . }} + labels: + {{- include "promtail.labels" . | nindent 4 }} +rules: + - apiGroups: + - "" + resources: + - nodes + - nodes/proxy + - services + - endpoints + - pods + verbs: + - get + - watch + - list +{{- end }} diff --git a/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/logging/charts/promtail/templates/clusterrolebinding.yaml b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/logging/charts/promtail/templates/clusterrolebinding.yaml new file mode 100644 index 000000000..e92bf9a6d --- /dev/null +++ b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/logging/charts/promtail/templates/clusterrolebinding.yaml @@ -0,0 +1,16 @@ +{{- if .Values.rbac.create }} +kind: ClusterRoleBinding +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: {{ include "promtail.fullname" . }} + labels: + {{- include "promtail.labels" . | nindent 4 }} +subjects: + - kind: ServiceAccount + name: {{ include "promtail.serviceAccountName" . }} + namespace: {{ include "promtail.namespaceName" . }} +roleRef: + kind: ClusterRole + name: {{ include "promtail.fullname" . }} + apiGroup: rbac.authorization.k8s.io +{{- end }} diff --git a/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/logging/charts/promtail/templates/configmap.yaml b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/logging/charts/promtail/templates/configmap.yaml new file mode 100644 index 000000000..0785b1a60 --- /dev/null +++ b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/logging/charts/promtail/templates/configmap.yaml @@ -0,0 +1,12 @@ +{{- if .Values.configmap.enabled }} +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ include "promtail.fullname" . }} + namespace: {{ include "promtail.namespaceName" . }} + labels: + {{- include "promtail.labels" . | nindent 4 }} +data: + promtail.yaml: | + {{- tpl .Values.config.file . | nindent 4 }} +{{- end }} diff --git a/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/logging/charts/promtail/templates/daemonset.yaml b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/logging/charts/promtail/templates/daemonset.yaml new file mode 100644 index 000000000..85a8aa031 --- /dev/null +++ b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/logging/charts/promtail/templates/daemonset.yaml @@ -0,0 +1,21 @@ +{{- if .Values.daemonset.enabled }} +apiVersion: apps/v1 +kind: DaemonSet +metadata: + name: {{ include "promtail.fullname" . }} + namespace: {{ include "promtail.namespaceName" . }} + labels: + {{- include "promtail.labels" . | nindent 4 }} + {{- with .Values.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + selector: + matchLabels: + {{- include "promtail.selectorLabels" . | nindent 6 }} + updateStrategy: + {{- toYaml .Values.updateStrategy | nindent 4 }} + template: + {{- include "promtail.podTemplate" . | nindent 4 }} +{{- end }} diff --git a/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/logging/charts/promtail/templates/deployment.yaml b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/logging/charts/promtail/templates/deployment.yaml new file mode 100644 index 000000000..26e7381a3 --- /dev/null +++ b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/logging/charts/promtail/templates/deployment.yaml @@ -0,0 +1,22 @@ +{{- if .Values.deployment.enabled }} +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ include "promtail.fullname" . }} + namespace: {{ include "promtail.namespaceName" . }} + labels: + {{- include "promtail.labels" . | nindent 4 }} + {{- with .Values.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + {{- if not .Values.deployment.autoscaling.enabled }} + replicas: {{ .Values.deployment.replicaCount }} + {{- end }} + selector: + matchLabels: + {{- include "promtail.selectorLabels" . | nindent 6 }} + template: + {{- include "promtail.podTemplate" . | nindent 4 }} +{{- end }} diff --git a/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/logging/charts/promtail/templates/extra-manifests.yaml b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/logging/charts/promtail/templates/extra-manifests.yaml new file mode 100644 index 000000000..a9bb3b6ba --- /dev/null +++ b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/logging/charts/promtail/templates/extra-manifests.yaml @@ -0,0 +1,4 @@ +{{ range .Values.extraObjects }} +--- +{{ tpl (toYaml .) $ }} +{{ end }} diff --git a/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/logging/charts/promtail/templates/hpa.yaml b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/logging/charts/promtail/templates/hpa.yaml new file mode 100644 index 000000000..8a205fde9 --- /dev/null +++ b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/logging/charts/promtail/templates/hpa.yaml @@ -0,0 +1,31 @@ +{{- if and .Values.deployment.enabled .Values.deployment.autoscaling.enabled }} +apiVersion: autoscaling/v2beta1 +kind: HorizontalPodAutoscaler +metadata: + name: {{ include "promtail.fullname" . }} + namespace: {{ include "promtail.namespaceName" . }} + labels: + {{- include "promtail.labels" . | nindent 4 }} +spec: + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: {{ include "promtail.fullname" . }} + {{- with .Values.deployment.autoscaling }} + minReplicas: {{ .minReplicas }} + maxReplicas: {{ .maxReplicas }} + metrics: + {{- with .targetCPUUtilizationPercentage }} + - type: Resource + resource: + name: cpu + targetAverageUtilization: {{ . }} + {{- end }} + {{- with .targetMemoryUtilizationPercentage }} + - type: Resource + resource: + name: memory + targetAverageUtilization: {{ . }} + {{- end }} + {{- end }} +{{- end }} diff --git a/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/logging/charts/promtail/templates/networkpolicy.yaml b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/logging/charts/promtail/templates/networkpolicy.yaml new file mode 100644 index 000000000..9467df5de --- /dev/null +++ b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/logging/charts/promtail/templates/networkpolicy.yaml @@ -0,0 +1,123 @@ +{{- if .Values.networkPolicy.enabled }} +--- +apiVersion: networking.k8s.io/v1 +kind: NetworkPolicy +metadata: + name: {{ template "promtail.name" . }}-namespace-only + namespace: {{ include "promtail.namespaceName" . }} + labels: + {{- include "promtail.labels" . | nindent 4 }} +spec: + podSelector: {} + policyTypes: + - Ingress + - Egress + egress: + - to: + - podSelector: {} + ingress: + - from: + - podSelector: {} +--- +apiVersion: networking.k8s.io/v1 +kind: NetworkPolicy +metadata: + name: {{ template "promtail.name" . }}-egress-dns + namespace: {{ include "promtail.namespaceName" . }} + labels: + {{- include "promtail.labels" . | nindent 4 }} +spec: + podSelector: + matchLabels: + {{- include "promtail.selectorLabels" . | nindent 6 }} + policyTypes: + - Egress + egress: + - ports: + - port: 53 + protocol: UDP + to: + - namespaceSelector: {} +--- +apiVersion: networking.k8s.io/v1 +kind: NetworkPolicy +metadata: + name: {{ template "promtail.name" . }}-egress-k8s-api + namespace: {{ include "promtail.namespaceName" . }} + labels: + {{- include "promtail.labels" . | nindent 4 }} +spec: + podSelector: + matchLabels: + {{- include "promtail.selectorLabels" . | nindent 6 }} + policyTypes: + - Egress + egress: + - ports: + - port: {{ .Values.networkPolicy.k8sApi.port }} + protocol: TCP + {{- if len .Values.networkPolicy.k8sApi.cidrs }} + to: + {{- range $cidr := .Values.networkPolicy.k8sApi.cidrs }} + - ipBlock: + cidr: {{ $cidr }} + {{- end }} + {{- end }} + +--- +apiVersion: networking.k8s.io/v1 +kind: NetworkPolicy +metadata: + name: {{ template "promtail.name" . }}-ingress-metrics + namespace: {{ include "promtail.namespaceName" . }} + labels: + {{- include "promtail.labels" . | nindent 4 }} +spec: + podSelector: + matchLabels: + {{- include "promtail.selectorLabels" . | nindent 6 }} + policyTypes: + - Ingress + ingress: + - ports: + - port: http-metrics + protocol: TCP + {{- if len .Values.networkPolicy.metrics.cidrs }} + from: + {{- range $cidr := .Values.networkPolicy.metrics.cidrs }} + - ipBlock: + cidr: {{ $cidr }} + {{- end }} + {{- if .Values.networkPolicy.metrics.namespaceSelector }} + - namespaceSelector: + {{- toYaml .Values.networkPolicy.metrics.namespaceSelector | nindent 12 }} + {{- if .Values.networkPolicy.metrics.podSelector }} + podSelector: + {{- toYaml .Values.networkPolicy.metrics.podSelector | nindent 12 }} + {{- end }} + {{- end }} + {{- end }} + +{{- if .Values.extraPorts }} +--- +apiVersion: networking.k8s.io/v1 +kind: NetworkPolicy +metadata: + name: {{ template "promtail.name" . }}-egress-extra-ports + namespace: {{ include "promtail.namespaceName" . }} + labels: + {{- include "promtail.labels" . | nindent 4 }} +spec: + podSelector: + matchLabels: + {{- include "promtail.selectorLabels" . | nindent 6 }} + policyTypes: + - Egress + egress: + - ports: + {{- range $extraPortConfig := .Values.extraPorts }} + - port: {{ $extraPortConfig.containerPort }} + protocol: {{ $extraPortConfig.protocol }} + {{- end }} +{{- end }} +{{- end }} diff --git a/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/logging/charts/promtail/templates/podsecuritypolicy.yaml b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/logging/charts/promtail/templates/podsecuritypolicy.yaml new file mode 100644 index 000000000..a22938826 --- /dev/null +++ b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/logging/charts/promtail/templates/podsecuritypolicy.yaml @@ -0,0 +1,10 @@ +{{- if and (.Capabilities.APIVersions.Has "policy/v1beta1/PodSecurityPolicy") .Values.rbac.create .Values.rbac.pspEnabled }} +apiVersion: policy/v1beta1 +kind: PodSecurityPolicy +metadata: + name: {{ include "promtail.fullname" . }} + labels: + {{- include "promtail.labels" . | nindent 4 }} +spec: + {{- toYaml .Values.podSecurityPolicy | nindent 2 }} +{{- end }} diff --git a/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/logging/charts/promtail/templates/prometheus-rules.yaml b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/logging/charts/promtail/templates/prometheus-rules.yaml new file mode 100644 index 000000000..6ec3f26a9 --- /dev/null +++ b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/logging/charts/promtail/templates/prometheus-rules.yaml @@ -0,0 +1,21 @@ +{{- if and .Values.serviceMonitor.enabled .Values.serviceMonitor.prometheusRule.enabled -}} +apiVersion: monitoring.coreos.com/v1 +kind: PrometheusRule +metadata: + name: {{ include "promtail.fullname" . }} + {{- with .Values.serviceMonitor.prometheusRule.namespace }} + namespace: {{ . | quote }} + {{- end }} + labels: + {{- include "promtail.labels" . | nindent 4 }} + {{- with .Values.serviceMonitor.prometheusRule.additionalLabels }} + {{- toYaml . | nindent 4 }} + {{- end }} +spec: +{{- if .Values.serviceMonitor.prometheusRule.rules }} + groups: + - name: {{ template "promtail.fullname" . }} + rules: + {{- toYaml .Values.serviceMonitor.prometheusRule.rules | nindent 4 }} +{{- end }} +{{- end }} diff --git a/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/logging/charts/promtail/templates/role.yaml b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/logging/charts/promtail/templates/role.yaml new file mode 100644 index 000000000..a193b3f5b --- /dev/null +++ b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/logging/charts/promtail/templates/role.yaml @@ -0,0 +1,18 @@ +{{- if and (.Capabilities.APIVersions.Has "policy/v1beta1/PodSecurityPolicy") .Values.rbac.create .Values.rbac.pspEnabled }} +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: {{ include "promtail.fullname" . }}-psp + namespace: {{ include "promtail.namespaceName" . }} + labels: + {{- include "promtail.labels" . | nindent 4 }} +rules: + - apiGroups: + - policy + resources: + - podsecuritypolicies + verbs: + - use + resourceNames: + - {{ include "promtail.fullname" . }} +{{- end }} diff --git a/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/logging/charts/promtail/templates/rolebinding.yaml b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/logging/charts/promtail/templates/rolebinding.yaml new file mode 100644 index 000000000..0527fdc55 --- /dev/null +++ b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/logging/charts/promtail/templates/rolebinding.yaml @@ -0,0 +1,16 @@ +{{- if and (.Capabilities.APIVersions.Has "policy/v1beta1/PodSecurityPolicy") .Values.rbac.create .Values.rbac.pspEnabled }} +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: {{ include "promtail.fullname" . }}-psp + namespace: {{ include "promtail.namespaceName" . }} + labels: + {{- include "promtail.labels" . | nindent 4 }} +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: {{ include "promtail.fullname" . }}-psp +subjects: + - kind: ServiceAccount + name: {{ include "promtail.serviceAccountName" . }} +{{- end }} diff --git a/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/logging/charts/promtail/templates/secret.yaml b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/logging/charts/promtail/templates/secret.yaml new file mode 100644 index 000000000..f5d61ace3 --- /dev/null +++ b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/logging/charts/promtail/templates/secret.yaml @@ -0,0 +1,19 @@ +{{- if not .Values.configmap.enabled }} +apiVersion: v1 +kind: Secret +metadata: + name: {{ include "promtail.fullname" . }} + namespace: {{ include "promtail.namespaceName" . }} + labels: + {{- include "promtail.labels" . | nindent 4 }} + {{- with .Values.secret.labels }} + {{- toYaml . | nindent 4 }} + {{- end }} + {{- with .Values.secret.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +stringData: + promtail.yaml: | + {{- tpl .Values.config.file . | nindent 4 }} +{{- end }} diff --git a/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/logging/charts/promtail/templates/service-extra.yaml b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/logging/charts/promtail/templates/service-extra.yaml new file mode 100644 index 000000000..7257e6894 --- /dev/null +++ b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/logging/charts/promtail/templates/service-extra.yaml @@ -0,0 +1,52 @@ +{{- range $key, $values := .Values.extraPorts }} +--- +apiVersion: v1 +kind: Service +metadata: + name: {{ include "promtail.fullname" $ }}-{{ $key | lower }} + namespace: {{ include "promtail.namespaceName" $ }} + labels: + {{- include "promtail.labels" $ | nindent 4 }} + {{- with .labels }} + {{- toYaml $ | nindent 4 }} + {{- end }} + {{- with .annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + {{- with $values.service }} + type: {{ .type | default "ClusterIP" }} + {{- with .clusterIP }} + clusterIP: {{ . }} + {{- end }} + {{- with .loadBalancerIP }} + loadBalancerIP: {{ . }} + {{- end }} + {{- with .loadBalancerSourceRanges }} + loadBalancerSourceRanges: + {{- toYaml . | nindent 4 }} + {{- end }} + {{- with .externalIPs }} + externalIPs: + {{- toYaml . | nindent 4 }} + {{- end }} + {{- with .externalTrafficPolicy }} + externalTrafficPolicy: {{ . }} + {{- end }} + {{- end }} + ports: + - name: {{ .name | default $key }} + targetPort: {{ .name | default $key }} + protocol: {{ $values.protocol | default "TCP" }} + {{- if $values.service }} + port: {{ $values.service.port | default $values.containerPort }} + {{- if $values.service.nodePort }} + nodePort: {{ $values.service.nodePort }} + {{- end }} + {{- else }} + port: {{ $values.containerPort }} + {{- end }} + selector: + {{- include "promtail.selectorLabels" $ | nindent 4 }} +{{- end }} diff --git a/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/logging/charts/promtail/templates/service-metrics.yaml b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/logging/charts/promtail/templates/service-metrics.yaml new file mode 100644 index 000000000..4948ceecf --- /dev/null +++ b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/logging/charts/promtail/templates/service-metrics.yaml @@ -0,0 +1,18 @@ +{{- if .Values.serviceMonitor.enabled }} +apiVersion: v1 +kind: Service +metadata: + name: {{ include "promtail.fullname" . }}-metrics + namespace: {{ include "promtail.namespaceName" . }} + labels: + {{- include "promtail.labels" . | nindent 4 }} +spec: + clusterIP: None + ports: + - name: http-metrics + port: {{ .Values.config.serverPort }} + targetPort: http-metrics + protocol: TCP + selector: + {{- include "promtail.selectorLabels" . | nindent 4 }} +{{- end }} diff --git a/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/logging/charts/promtail/templates/serviceaccount.yaml b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/logging/charts/promtail/templates/serviceaccount.yaml new file mode 100644 index 000000000..658c2012f --- /dev/null +++ b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/logging/charts/promtail/templates/serviceaccount.yaml @@ -0,0 +1,17 @@ +{{- if .Values.serviceAccount.create }} +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ include "promtail.serviceAccountName" . }} + namespace: {{ include "promtail.namespaceName" . }} + labels: + {{- include "promtail.labels" . | nindent 4 }} + {{- with .Values.serviceAccount.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +{{- with .Values.serviceAccount.imagePullSecrets }} +imagePullSecrets: + {{- toYaml . | nindent 2 }} +{{- end }} +{{- end }} diff --git a/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/logging/charts/promtail/templates/servicemonitor.yaml b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/logging/charts/promtail/templates/servicemonitor.yaml new file mode 100644 index 000000000..f43964931 --- /dev/null +++ b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/logging/charts/promtail/templates/servicemonitor.yaml @@ -0,0 +1,58 @@ +{{- with .Values.serviceMonitor }} +{{- if .enabled }} +apiVersion: monitoring.coreos.com/v1 +kind: ServiceMonitor +metadata: + name: {{ include "promtail.fullname" $ }} + {{- with .namespace }} + namespace: {{ . }} + {{- end }} + {{- with .annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} + labels: + {{- include "promtail.labels" $ | nindent 4 }} + {{- with .labels }} + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + {{- with .namespaceSelector }} + namespaceSelector: + {{- toYaml . | nindent 4 }} + {{- end }} + selector: + matchLabels: + {{- include "promtail.selectorLabels" $ | nindent 6 }} + endpoints: + - port: http-metrics + {{- with $.Values.httpPathPrefix }} + path: {{ printf "%s/metrics" . }} + {{- end }} + {{- with .interval }} + interval: {{ . }} + {{- end }} + {{- with .scrapeTimeout }} + scrapeTimeout: {{ . }} + {{- end }} + {{- with .relabelings }} + relabelings: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .metricRelabelings }} + metricRelabelings: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .scheme }} + scheme: {{ . }} + {{- end }} + {{- with .tlsConfig }} + tlsConfig: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .targetLabels }} + targetLabels: + {{- toYaml . | nindent 4 }} + {{- end }} +{{- end }} +{{- end }} diff --git a/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/logging/charts/promtail/values.yaml b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/logging/charts/promtail/values.yaml new file mode 100644 index 000000000..58d7752ea --- /dev/null +++ b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/logging/charts/promtail/values.yaml @@ -0,0 +1,534 @@ +# -- Overrides the chart's name +nameOverride: null + +# -- Overrides the chart's computed fullname +fullnameOverride: null + +daemonset: + # -- Deploys Promtail as a DaemonSet + enabled: true + +deployment: + # -- Deploys Promtail as a Deployment + enabled: false + replicaCount: 1 + autoscaling: + # -- Creates a HorizontalPodAutoscaler for the deployment + enabled: false + minReplicas: 1 + maxReplicas: 10 + targetCPUUtilizationPercentage: 80 + targetMemoryUtilizationPercentage: + +secret: + # -- Labels for the Secret + labels: {} + # -- Annotations for the Secret + annotations: {} + +configmap: + # -- If enabled, promtail config will be created as a ConfigMap instead of a secret + enabled: false + +initContainer: [] + # # -- Specifies whether the init container for setting inotify max user instances is to be enabled + # - name: init + # # -- Docker registry, image and tag for the init container image + # image: docker.io/busybox:1.33 + # # -- Docker image pull policy for the init container image + # imagePullPolicy: IfNotPresent + # # -- The inotify max user instances to configure + # command: + # - sh + # - -c + # - sysctl -w fs.inotify.max_user_instances=128 + # securityContext: + # privileged: true + +image: + # -- The Docker registry + registry: docker.io + # -- Docker image repository + repository: grafana/promtail + # -- Overrides the image tag whose default is the chart's appVersion + tag: null + # -- Docker image pull policy + pullPolicy: IfNotPresent + +# -- Image pull secrets for Docker images +imagePullSecrets: [] + +# -- Annotations for the DaemonSet +annotations: + ignore-check.kube-linter.io/run-as-non-root: "This deployment needs to run as root user to modify log files" + ignore-check.kube-linter.io/writable-host-mount: "This deployment needs writable volume mount on host to capture logs" + +# -- The update strategy for the DaemonSet +updateStrategy: + type: RollingUpdate + +# -- Pod labels +podLabels: {} + +# -- Pod annotations +podAnnotations: {} +# prometheus.io/scrape: "true" +# prometheus.io/port: "http-metrics" + +# -- The name of the PriorityClass +priorityClassName: null + +# -- Liveness probe +livenessProbe: {} + +# -- Readiness probe +# @default -- See `values.yaml` +readinessProbe: + failureThreshold: 5 + httpGet: + path: "{{ printf `%s/ready` .Values.httpPathPrefix }}" + port: http-metrics + initialDelaySeconds: 10 + periodSeconds: 10 + successThreshold: 1 + timeoutSeconds: 1 + +# -- Resource requests and limits +resources: + limits: + cpu: 200m + memory: 128Mi + requests: + cpu: 100m + memory: 128Mi + +# -- The security context for pods +podSecurityContext: + runAsUser: 0 + runAsGroup: 0 + +# -- The security context for containers +containerSecurityContext: + readOnlyRootFilesystem: true + capabilities: + drop: + - ALL + allowPrivilegeEscalation: false + +rbac: + # -- Specifies whether RBAC resources are to be created + create: true + # -- Specifies whether a PodSecurityPolicy is to be created + pspEnabled: false + +# -- The name of the Namespace to deploy +# If not set, `.Release.Namespace` is used +namespace: 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: null + # -- Image pull secrets for the service account + imagePullSecrets: [] + # -- Annotations for the service account + annotations: {} + +# -- Node selector for pods +nodeSelector: {} + +# -- Affinity configuration for pods +affinity: {} + +# -- Tolerations for pods. By default, pods will be scheduled on master/control-plane nodes. +tolerations: + - key: node-role.kubernetes.io/master + operator: Exists + effect: NoSchedule + - key: node-role.kubernetes.io/control-plane + operator: Exists + effect: NoSchedule + +# -- Default volumes that are mounted into pods. In most cases, these should not be changed. +# Use `extraVolumes`/`extraVolumeMounts` for additional custom volumes. +# @default -- See `values.yaml` +defaultVolumes: + - name: run + hostPath: + path: /run/promtail + - name: containers + hostPath: + path: /var/lib/docker/containers + - name: pods + hostPath: + path: /var/log/pods + +# -- Default volume mounts. Corresponds to `volumes`. +# @default -- See `values.yaml` +defaultVolumeMounts: + - name: run + mountPath: /run/promtail + - name: containers + mountPath: /var/lib/docker/containers + readOnly: true + - name: pods + mountPath: /var/log/pods + readOnly: true + +# Extra volumes to be added in addition to those specified under `defaultVolumes`. +extraVolumes: [] + +# Extra volume mounts together. Corresponds to `extraVolumes`. +extraVolumeMounts: [] + +# Extra args for the Promtail container. +extraArgs: [] +# -- Example: +# -- extraArgs: +# -- - -client.external-labels=hostname=$(HOSTNAME) + +# -- Extra environment variables +extraEnv: [] + +# -- Extra environment variables from secrets or configmaps +extraEnvFrom: [] + +# -- Configure enableServiceLinks in pod +enableServiceLinks: true + +# ServiceMonitor configuration +serviceMonitor: + # -- If enabled, ServiceMonitor resources for Prometheus Operator are created + enabled: false + # -- Alternative namespace for ServiceMonitor resources + namespace: null + # -- Namespace selector for ServiceMonitor resources + namespaceSelector: {} + # -- ServiceMonitor annotations + annotations: {} + # -- Additional ServiceMonitor labels + labels: {} + # -- ServiceMonitor scrape interval + interval: null + # -- ServiceMonitor scrape timeout in Go duration format (e.g. 15s) + scrapeTimeout: null + # -- ServiceMonitor relabel configs to apply to samples before scraping + # https://github.com/prometheus-operator/prometheus-operator/blob/master/Documentation/api.md#relabelconfig + # (defines `relabel_configs`) + relabelings: [] + # -- ServiceMonitor relabel configs to apply to samples as the last + # step before ingestion + # https://github.com/prometheus-operator/prometheus-operator/blob/master/Documentation/api.md#relabelconfig + # (defines `metric_relabel_configs`) + metricRelabelings: [] + # --ServiceMonitor will add labels from the service to the Prometheus metric + # https://github.com/prometheus-operator/prometheus-operator/blob/main/Documentation/api.md#servicemonitorspec + targetLabels: [] + # -- ServiceMonitor will use http by default, but you can pick https as well + scheme: http + # -- ServiceMonitor will use these tlsConfig settings to make the health check requests + tlsConfig: null + # -- Prometheus rules will be deployed for alerting purposes + prometheusRule: + enabled: false + additionalLabels: {} + # namespace: + rules: [] + # - alert: PromtailRequestErrors + # expr: 100 * sum(rate(promtail_request_duration_seconds_count{status_code=~"5..|failed"}[1m])) by (namespace, job, route, instance) / sum(rate(promtail_request_duration_seconds_count[1m])) by (namespace, job, route, instance) > 10 + # for: 5m + # labels: + # severity: critical + # annotations: + # description: | + # The {{ $labels.job }} {{ $labels.route }} is experiencing + # {{ printf \"%.2f\" $value }} errors. + # VALUE = {{ $value }} + # LABELS = {{ $labels }} + # summary: Promtail request errors (instance {{ $labels.instance }}) + # - alert: PromtailRequestLatency + # expr: histogram_quantile(0.99, sum(rate(promtail_request_duration_seconds_bucket[5m])) by (le)) > 1 + # for: 5m + # labels: + # severity: critical + # annotations: + # summary: Promtail request latency (instance {{ $labels.instance }}) + # description: | + # The {{ $labels.job }} {{ $labels.route }} is experiencing + # {{ printf \"%.2f\" $value }}s 99th percentile latency. + # VALUE = {{ $value }} + # LABELS = {{ $labels }} + +# Extra containers created as part of a Promtail Deployment resource +# - spec for Container: +# https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.23/#container-v1-core +# +# Note that the key is used as the `name` field, i.e. below will create a +# container named `promtail-proxy`. +extraContainers: {} + # promtail-proxy: + # image: nginx + # ... + +# -- Configure additional ports and services. For each configured port, a corresponding service is created. +# See values.yaml for details +extraPorts: {} +# syslog: +# name: tcp-syslog +# containerPort: 1514 +# protocol: TCP +# service: +# type: ClusterIP +# clusterIP: null +# port: 1514 +# externalIPs: [] +# nodePort: null +# annotations: {} +# labels: {} +# loadBalancerIP: null +# loadBalancerSourceRanges: [] +# externalTrafficPolicy: null + +# -- PodSecurityPolicy configuration. +# @default -- See `values.yaml` +podSecurityPolicy: + privileged: true + allowPrivilegeEscalation: true + volumes: + - 'secret' + - 'hostPath' + - 'downwardAPI' + hostNetwork: false + hostIPC: false + hostPID: false + runAsUser: + rule: 'RunAsAny' + seLinux: + rule: 'RunAsAny' + supplementalGroups: + rule: 'RunAsAny' + fsGroup: + rule: 'RunAsAny' + readOnlyRootFilesystem: true + requiredDropCapabilities: + - ALL + +# -- Section for crafting Promtails config file. The only directly relevant value is `config.file` +# which is a templated string that references the other values and snippets below this key. +# @default -- See `values.yaml` +config: + # -- The log level of the Promtail server + # Must be reference in `config.file` to configure `server.log_level` + # See default config in `values.yaml` + logLevel: info + # -- The port of the Promtail server + # Must be reference in `config.file` to configure `server.http_listen_port` + # See default config in `values.yaml` + serverPort: 3101 + # -- The config of clients of the Promtail server + # Must be reference in `config.file` to configure `clients` + # @default -- See `values.yaml` + clients: + - url: http://{{ .Release.Name }}-loki:3100/loki/api/v1/push + # -- A section of reusable snippets that can be reference in `config.file`. + # Custom snippets may be added in order to reduce redundancy. + # This is especially helpful when multiple `kubernetes_sd_configs` are use which usually have large parts in common. + # @default -- See `values.yaml` + snippets: + pipelineStages: + - cri: {} + - match: + selector: '{app="k8s-triliovault"}' + stages: + - json: + expressions: + level: level + service_type: service_type + pvc_name: pvc_name + transaction_type: transaction_type + transaction_resource_name: transaction_resource_name + transaction_resource_namespace: transaction_resource_namespace + child_transaction_type: child_transaction_type + child_transaction_resource_name: child_transaction_resource_name + child_transaction_resource_namespace: child_transaction_resource_namespace + tvk_instance_id: tvk_instance_id + - labels: + level: + service_type: + pvc_name: + transaction_type: + transaction_resource_name: + transaction_resource_namespace: + child_transaction_type: + child_transaction_resource_name: + child_transaction_resource_namespace: + tvk_instance_id: + common: + - action: replace + source_labels: + - __meta_kubernetes_pod_node_name + target_label: node_name + - action: replace + source_labels: + - __meta_kubernetes_namespace + target_label: namespace + - action: replace + replacement: $1 + separator: / + source_labels: + - namespace + - app + target_label: job + - action: replace + source_labels: + - __meta_kubernetes_pod_name + target_label: pod + - action: replace + source_labels: + - __meta_kubernetes_pod_container_name + target_label: container + - action: replace + replacement: /var/log/pods/*$1/*.log + separator: / + source_labels: + - __meta_kubernetes_pod_uid + - __meta_kubernetes_pod_container_name + target_label: __path__ + - action: replace + replacement: /var/log/pods/*$1/*.log + regex: true/(.*) + separator: / + source_labels: + - __meta_kubernetes_pod_annotationpresent_kubernetes_io_config_hash + - __meta_kubernetes_pod_annotation_kubernetes_io_config_hash + - __meta_kubernetes_pod_container_name + target_label: __path__ + + # If set to true, adds an additional label for the scrape job. + # This helps debug the Promtail config. + addScrapeJobLabel: false + + # -- You can put here any keys that will be directly added to the config file's 'limits_config' block. + # @default -- empty + extraLimitsConfig: "" + + # -- You can put here any keys that will be directly added to the config file's 'server' block. + # @default -- empty + extraServerConfigs: "" + + # -- You can put here any additional scrape configs you want to add to the config file. + # @default -- empty + extraScrapeConfigs: "" + + # -- You can put here any additional relabel_configs to "kubernetes-pods" job + extraRelabelConfigs: [] + + scrapeConfigs: | + # See also https://github.com/grafana/loki/blob/master/production/ksonnet/promtail/scrape_config.libsonnet for reference + - job_name: kubernetes-pods + pipeline_stages: + {{- toYaml .Values.config.snippets.pipelineStages | nindent 4 }} + kubernetes_sd_configs: + - role: pod + relabel_configs: + - source_labels: + - __meta_kubernetes_pod_controller_name + regex: ([0-9a-z-.]+?)(-[0-9a-f]{8,10})? + action: replace + target_label: __tmp_controller_name + - source_labels: + - __meta_kubernetes_pod_label_app_kubernetes_io_name + - __meta_kubernetes_pod_label_app + - __tmp_controller_name + - __meta_kubernetes_pod_name + regex: ^;*([^;]+)(;.*)?$ + action: replace + target_label: app + - source_labels: + - __meta_kubernetes_pod_label_app_kubernetes_io_instance + - __meta_kubernetes_pod_label_release + regex: ^;*([^;]+)(;.*)?$ + action: replace + target_label: instance + - source_labels: + - __meta_kubernetes_pod_label_app_kubernetes_io_component + - __meta_kubernetes_pod_label_component + regex: ^;*([^;]+)(;.*)?$ + action: replace + target_label: component + - action: keep + source_labels: + - app + - instance + - component + regex: '(k8s-trilio).*' + {{- if .Values.config.snippets.addScrapeJobLabel }} + - replacement: kubernetes-pods + target_label: scrape_job + {{- end }} + {{- toYaml .Values.config.snippets.common | nindent 4 }} + {{- with .Values.config.snippets.extraRelabelConfigs }} + {{- toYaml . | nindent 4 }} + {{- end }} + + # -- Config file contents for Promtail. + # Must be configured as string. + # It is templated so it can be assembled from reusable snippets in order to avoid redundancy. + # @default -- See `values.yaml` + file: | + server: + log_level: {{ .Values.config.logLevel }} + http_listen_port: {{ .Values.config.serverPort }} + {{- with .Values.httpPathPrefix }} + http_path_prefix: {{ . }} + {{- end }} + {{- tpl .Values.config.snippets.extraServerConfigs . | nindent 2 }} + + clients: + {{- tpl (toYaml .Values.config.clients) . | nindent 2 }} + + positions: + filename: /run/promtail/positions.yaml + + scrape_configs: + {{- tpl .Values.config.snippets.scrapeConfigs . | nindent 2 }} + {{- tpl .Values.config.snippets.extraScrapeConfigs . | nindent 2 }} + + limits_config: + {{- tpl .Values.config.snippets.extraLimitsConfig . | nindent 2 }} + +networkPolicy: + # -- Specifies whether Network Policies should be created + enabled: false + metrics: + # -- Specifies the Pods which are allowed to access the metrics port. + # As this is cross-namespace communication, you also neeed the namespaceSelector. + podSelector: {} + # -- Specifies the namespaces which are allowed to access the metrics port + namespaceSelector: {} + # -- Specifies specific network CIDRs which are allowed to access the metrics port. + # In case you use namespaceSelector, you also have to specify your kubelet networks here. + # The metrics ports are also used for probes. + cidrs: [] + k8sApi: + # -- Specify the k8s API endpoint port + port: 8443 + # -- Specifies specific network CIDRs you want to limit access to + cidrs: [] + +# -- Base path to server all API routes fro +httpPathPrefix: "" + +# -- Extra K8s manifests to deploy +extraObjects: [] + # - apiVersion: "kubernetes-client.io/v1" + # kind: ExternalSecret + # metadata: + # name: promtail-secrets + # spec: + # backendType: gcpSecretsManager + # data: + # - key: promtail-oauth2-creds + # name: client_secret diff --git a/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/logging/templates/_helpers.tpl b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/logging/templates/_helpers.tpl new file mode 100644 index 000000000..9fb468f83 --- /dev/null +++ b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/logging/templates/_helpers.tpl @@ -0,0 +1,50 @@ +{{/* vim: set filetype=mustache: */}} +{{/* +Expand the name of the chart. +*/}} +{{- define "logging.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 "logging.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 "logging.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}} +{{- end -}} + +{{/* +The service name to connect to Loki. Defaults to the same logic as "loki.fullname" +*/}} +{{- define "loki.serviceName" -}} +{{- if .Values.loki.serviceName -}} +{{- .Values.loki.serviceName -}} +{{- else if .Values.loki.fullnameOverride -}} +{{- .Values.loki.fullnameOverride | trunc 63 | trimSuffix "-" -}} +{{- else -}} +{{- $name := default "loki" .Values.loki.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/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/logging/templates/datasources.yaml b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/logging/templates/datasources.yaml new file mode 100644 index 000000000..f5ca78f23 --- /dev/null +++ b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/logging/templates/datasources.yaml @@ -0,0 +1,24 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ template "logging.fullname" . }} + namespace: {{ .Release.Namespace }} + labels: + app: {{ template "logging.name" . }} + chart: {{ template "logging.chart" . }} + release: {{ .Release.Name }} + heritage: {{ .Release.Service }} + app.kubernetes.io/instance: {{ template "logging.name" . }} + {{- include "k8s-triliovault-operator.observability" . | nindent 4 }} + grafana_datasource: "1" +data: + logging-datasource.yaml: |- + apiVersion: 1 + datasources: +{{- if .Values.loki.enabled }} + - name: Loki + type: loki + access: proxy + url: http://{{(include "loki.serviceName" .)}}:{{ .Values.loki.service.port }} + version: 1 +{{- end }} diff --git a/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/Chart.yaml b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/Chart.yaml new file mode 100644 index 000000000..13ffa6d15 --- /dev/null +++ b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/Chart.yaml @@ -0,0 +1,16 @@ +apiVersion: v2 +appVersion: 0.1.0 +dependencies: +- condition: prometheus.enabled + name: prometheus + repository: https://prometheus-community.github.io/helm-charts + version: ^15.8.7 +description: Monitoring Stack designed to manage the K8s-TrilioVault Application's + Monitoring. +icon: https://www.trilio.io/wp-content/uploads/2021/01/Trilio-2020-logo-RGB-gray-green.png +kubeVersion: '>=1.19.0-0' +maintainers: +- email: support@trilio.io + name: Trilio +name: monitoring +version: 0.1.0 diff --git a/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/Chart.yaml b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/Chart.yaml new file mode 100644 index 000000000..609fb7386 --- /dev/null +++ b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/Chart.yaml @@ -0,0 +1,22 @@ +apiVersion: v2 +appVersion: 2.34.0 +dependencies: +- condition: kubeStateMetrics.enabled + name: kube-state-metrics + repository: https://prometheus-community.github.io/helm-charts + version: 4.7.* +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 +maintainers: +- email: support@trilio.io + name: Trilio +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: 15.8.7 diff --git a/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/charts/kube-state-metrics/Chart.yaml b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/charts/kube-state-metrics/Chart.yaml new file mode 100644 index 000000000..83d0685a1 --- /dev/null +++ b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/charts/kube-state-metrics/Chart.yaml @@ -0,0 +1,17 @@ +apiVersion: v2 +appVersion: 2.4.1 +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: support@trilio.io + name: Trilio +name: kube-state-metrics +sources: +- https://github.com/kubernetes/kube-state-metrics/ +type: application +version: 4.7.0 diff --git a/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/charts/kube-state-metrics/templates/NOTES.txt b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/charts/kube-state-metrics/templates/NOTES.txt new file mode 100644 index 000000000..5a646e0cc --- /dev/null +++ b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/charts/kube-state-metrics/templates/NOTES.txt @@ -0,0 +1,10 @@ +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. + diff --git a/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/charts/kube-state-metrics/templates/_helpers.tpl b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/charts/kube-state-metrics/templates/_helpers.tpl new file mode 100644 index 000000000..976b27337 --- /dev/null +++ b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/charts/kube-state-metrics/templates/_helpers.tpl @@ -0,0 +1,82 @@ +{{/* 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 }} +{{ toYaml .Values.customLabels }} +{{- end }} +{{- if .Values.releaseLabel }} +release: {{ .Release.Name }} +{{- end }} +{{- end }} + +{{/* +Selector labels +*/}} +{{- define "kube-state-metrics.selectorLabels" }} +app.kubernetes.io/name: {{ include "kube-state-metrics.name" . }} +app.kubernetes.io/instance: {{ .Release.Name }} +{{- end }} diff --git a/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/charts/kube-state-metrics/templates/clusterrolebinding.yaml b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/charts/kube-state-metrics/templates/clusterrolebinding.yaml new file mode 100644 index 000000000..cf9f628d0 --- /dev/null +++ b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/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/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/charts/kube-state-metrics/templates/deployment.yaml b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/charts/kube-state-metrics/templates/deployment.yaml new file mode 100644 index 000000000..ac5387f10 --- /dev/null +++ b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/charts/kube-state-metrics/templates/deployment.yaml @@ -0,0 +1,156 @@ +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 }} +spec: + selector: + matchLabels: + {{- include "kube-state-metrics.selectorLabels" . | indent 6 }} + replicas: {{ .Values.replicas }} + {{- if .Values.autosharding.enabled }} + serviceName: {{ template "kube-state-metrics.fullname" . }} + updateStrategy: + type: RollingUpdate + volumeClaimTemplates: [] + {{- else }} + strategy: + type: RollingUpdate + {{- end }} + template: + metadata: + labels: + {{- include "kube-state-metrics.labels" . | indent 8 }} + {{- if .Values.podAnnotations }} + annotations: +{{ toYaml .Values.podAnnotations | indent 8 }} + {{- end }} + spec: + hostNetwork: {{ .Values.hostNetwork }} + serviceAccountName: {{ template "kube-state-metrics.serviceAccountName" . }} + {{- if .Values.securityContext.enabled }} + securityContext: + fsGroup: {{ .Values.securityContext.fsGroup }} + runAsGroup: {{ .Values.securityContext.runAsGroup }} + runAsUser: {{ .Values.securityContext.runAsUser }} + {{- end }} + {{- if .Values.priorityClassName }} + priorityClassName: {{ .Values.priorityClassName }} + {{- end }} + containers: + - name: {{ .Chart.Name }} + {{- if .Values.autosharding.enabled }} + env: + - name: POD_NAME + valueFrom: + fieldRef: + fieldPath: metadata.name + - name: POD_NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace + {{- end }} + args: + {{- if .Values.extraArgs }} + {{- range .Values.extraArgs }} + - {{ . }} + {{- end }} + {{- end }} + {{- if .Values.service.port }} + - --port={{ .Values.service.port | default 8080}} + {{- end }} + {{- 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 }} + {{- if .Values.namespaces }} + - --namespaces={{ tpl (.Values.namespaces | 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.selfMonitor.telemetryHost }} + - --telemetry-host={{ .Values.selfMonitor.telemetryHost }} + {{- end }} + - --telemetry-port={{ .Values.selfMonitor.telemetryPort | default 8081 }} + {{- if .Values.kubeconfig.enabled }} + volumeMounts: + - name: kubeconfig + mountPath: /opt/k8s/.kube/ + readOnly: true + {{- end }} + imagePullPolicy: {{ .Values.image.pullPolicy }} + image: "{{ .Values.image.registry }}/{{ .Values.image.repository }}:{{ .Values.image.tag }}" + ports: + - containerPort: {{ .Values.service.port | default 8080}} + name: "http" + {{- if .Values.selfMonitor.enabled }} + - containerPort: {{ .Values.selfMonitor.telemetryPort | default 8081 }} + name: "metrics" + {{- end }} + livenessProbe: + httpGet: + path: /healthz + port: {{ .Values.service.port | default 8080}} + initialDelaySeconds: 5 + timeoutSeconds: 5 + readinessProbe: + httpGet: + path: / + port: {{ .Values.service.port | default 8080}} + initialDelaySeconds: 5 + timeoutSeconds: 5 + {{- if .Values.resources }} + resources: +{{ toYaml .Values.resources | indent 10 }} +{{- end }} +{{- if .Values.containerSecurityContext }} + securityContext: +{{ toYaml .Values.containerSecurityContext | indent 10 }} +{{- end }} +{{- if .Values.imagePullSecrets }} + imagePullSecrets: +{{ toYaml .Values.imagePullSecrets | indent 8 }} + {{- end }} + {{- if .Values.affinity }} + affinity: +{{ toYaml .Values.affinity | indent 8 }} + {{- end }} + {{- if .Values.nodeSelector }} + nodeSelector: +{{ toYaml .Values.nodeSelector | indent 8 }} + {{- end }} + {{- if .Values.tolerations }} + tolerations: +{{ toYaml .Values.tolerations | indent 8 }} + {{- end }} + {{- if .Values.kubeconfig.enabled}} + volumes: + - name: kubeconfig + secret: + secretName: {{ template "kube-state-metrics.fullname" . }}-kubeconfig + {{- end }} diff --git a/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/charts/kube-state-metrics/templates/kubeconfig-secret.yaml b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/charts/kube-state-metrics/templates/kubeconfig-secret.yaml new file mode 100644 index 000000000..6af008450 --- /dev/null +++ b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/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/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/charts/kube-state-metrics/templates/pdb.yaml b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/charts/kube-state-metrics/templates/pdb.yaml new file mode 100644 index 000000000..cbcf3a37e --- /dev/null +++ b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/charts/kube-state-metrics/templates/pdb.yaml @@ -0,0 +1,14 @@ +{{- if .Values.podDisruptionBudget -}} +apiVersion: policy/v1beta1 +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/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/charts/kube-state-metrics/templates/podsecuritypolicy.yaml b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/charts/kube-state-metrics/templates/podsecuritypolicy.yaml new file mode 100644 index 000000000..3299056ab --- /dev/null +++ b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/charts/kube-state-metrics/templates/podsecuritypolicy.yaml @@ -0,0 +1,39 @@ +{{- if .Values.podSecurityPolicy.enabled }} +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/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/charts/kube-state-metrics/templates/psp-clusterrole.yaml b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/charts/kube-state-metrics/templates/psp-clusterrole.yaml new file mode 100644 index 000000000..69047d4ff --- /dev/null +++ b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/charts/kube-state-metrics/templates/psp-clusterrole.yaml @@ -0,0 +1,19 @@ +{{- if and .Values.podSecurityPolicy.enabled .Values.rbac.create -}} +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/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/charts/kube-state-metrics/templates/psp-clusterrolebinding.yaml b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/charts/kube-state-metrics/templates/psp-clusterrolebinding.yaml new file mode 100644 index 000000000..03c56d575 --- /dev/null +++ b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/charts/kube-state-metrics/templates/psp-clusterrolebinding.yaml @@ -0,0 +1,16 @@ +{{- if and .Values.podSecurityPolicy.enabled .Values.rbac.create -}} +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/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/charts/kube-state-metrics/templates/role.yaml b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/charts/kube-state-metrics/templates/role.yaml new file mode 100644 index 000000000..e514e3c01 --- /dev/null +++ b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/charts/kube-state-metrics/templates/role.yaml @@ -0,0 +1,187 @@ +{{- if and (eq .Values.rbac.create true) (not .Values.rbac.useExistingRole) -}} +{{- range (ternary (split "," .Values.namespaces) (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 "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 "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 has "verticalpodautoscalers" $.Values.collectors }} +- apiGroups: ["autoscaling.k8s.io"] + resources: + - verticalpodautoscalers + verbs: ["list", "watch"] +{{ end -}} +{{- end -}} +{{- end -}} diff --git a/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/charts/kube-state-metrics/templates/rolebinding.yaml b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/charts/kube-state-metrics/templates/rolebinding.yaml new file mode 100644 index 000000000..135094f7b --- /dev/null +++ b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/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 (split "," $.Values.namespaces) }} +--- +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/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/charts/kube-state-metrics/templates/service.yaml b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/charts/kube-state-metrics/templates/service.yaml new file mode 100644 index 000000000..5a2d8eab0 --- /dev/null +++ b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/charts/kube-state-metrics/templates/service.yaml @@ -0,0 +1,38 @@ +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 }}" + 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 }} + {{ end }} +{{- if .Values.service.loadBalancerIP }} + loadBalancerIP: "{{ .Values.service.loadBalancerIP }}" +{{- end }} +{{- if .Values.service.clusterIP }} + clusterIP: "{{ .Values.service.clusterIP }}" +{{- end }} + selector: + {{- include "kube-state-metrics.selectorLabels" . | indent 4 }} diff --git a/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/charts/kube-state-metrics/templates/serviceaccount.yaml b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/charts/kube-state-metrics/templates/serviceaccount.yaml new file mode 100644 index 000000000..e1229eb95 --- /dev/null +++ b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/charts/kube-state-metrics/templates/serviceaccount.yaml @@ -0,0 +1,15 @@ +{{- if .Values.serviceAccount.create -}} +apiVersion: v1 +kind: ServiceAccount +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 }} +imagePullSecrets: +{{ toYaml .Values.serviceAccount.imagePullSecrets | indent 2 }} +{{- end -}} diff --git a/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/charts/kube-state-metrics/templates/servicemonitor.yaml b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/charts/kube-state-metrics/templates/servicemonitor.yaml new file mode 100644 index 000000000..93a5870f6 --- /dev/null +++ b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/charts/kube-state-metrics/templates/servicemonitor.yaml @@ -0,0 +1,66 @@ +{{- 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 }} + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + jobLabel: {{ default "app.kubernetes.io/name" .Values.prometheus.monitor.jobLabel }} + selector: + matchLabels: + {{- if .Values.prometheus.monitor.selectorOverride -}} + {{ toYaml .Values.prometheus.monitor.selectorOverride | nindent 6 }} + {{ else }} + {{- include "kube-state-metrics.selectorLabels" . | indent 6 }} + {{- end }} + endpoints: + - port: http + {{- if .Values.prometheus.monitor.interval }} + interval: {{ .Values.prometheus.monitor.interval }} + {{- end }} + {{- if .Values.prometheus.monitor.scrapeTimeout }} + scrapeTimeout: {{ .Values.prometheus.monitor.scrapeTimeout }} + {{- end }} + {{- if .Values.prometheus.monitor.proxyUrl }} + proxyUrl: {{ .Values.prometheus.monitor.proxyUrl}} + {{- end }} + {{- if .Values.prometheus.monitor.honorLabels }} + honorLabels: true + {{- end }} + {{- if .Values.prometheus.monitor.metricRelabelings }} + metricRelabelings: + {{- toYaml .Values.prometheus.monitor.metricRelabelings | nindent 8 }} + {{- end }} + {{- if .Values.prometheus.monitor.relabelings }} + relabelings: + {{- toYaml .Values.prometheus.monitor.relabelings | nindent 8 }} + {{- end }} + {{- if .Values.selfMonitor.enabled }} + - port: metrics + {{- if .Values.prometheus.monitor.interval }} + interval: {{ .Values.prometheus.monitor.interval }} + {{- end }} + {{- if .Values.prometheus.monitor.scrapeTimeout }} + scrapeTimeout: {{ .Values.prometheus.monitor.scrapeTimeout }} + {{- end }} + {{- if .Values.prometheus.monitor.proxyUrl }} + proxyUrl: {{ .Values.prometheus.monitor.proxyUrl}} + {{- end }} + {{- if .Values.prometheus.monitor.honorLabels }} + honorLabels: true + {{- end }} + {{- if .Values.prometheus.monitor.metricRelabelings }} + metricRelabelings: + {{- toYaml .Values.prometheus.monitor.metricRelabelings | nindent 8 }} + {{- end }} + {{- if .Values.prometheus.monitor.relabelings }} + relabelings: + {{- toYaml .Values.prometheus.monitor.relabelings | nindent 8 }} + {{- end }} + {{- end }} +{{- end }} diff --git a/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/charts/kube-state-metrics/templates/stsdiscovery-role.yaml b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/charts/kube-state-metrics/templates/stsdiscovery-role.yaml new file mode 100644 index 000000000..489de147c --- /dev/null +++ b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/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/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/charts/kube-state-metrics/templates/stsdiscovery-rolebinding.yaml b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/charts/kube-state-metrics/templates/stsdiscovery-rolebinding.yaml new file mode 100644 index 000000000..73b37a4f6 --- /dev/null +++ b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/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/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/charts/kube-state-metrics/values.yaml b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/charts/kube-state-metrics/values.yaml new file mode 100644 index 000000000..3feb6b501 --- /dev/null +++ b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/charts/kube-state-metrics/values.yaml @@ -0,0 +1,233 @@ +# Default values for kube-state-metrics. +prometheusScrape: true +image: + registry: k8s.gcr.io + repository: kube-state-metrics/kube-state-metrics + tag: v2.4.1 + pullPolicy: IfNotPresent + +imagePullSecrets: [] +# - name: "image-pull-secret" + +# 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 + +# 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: [] + +service: + port: 8080 + # Default to clusterIP for backward compatibility + type: ClusterIP + nodePort: 0 + loadBalancerIP: "" + clusterIP: "" + annotations: {} + +## Additional labels to add to all resources +customLabels: {} + # app: kube-state-metrics + +## 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 + +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: {} + +prometheus: + monitor: + enabled: false + additionalLabels: {} + namespace: "" + jobLabel: "" + interval: "" + scrapeTimeout: "" + proxyUrl: "" + selectorOverride: {} + honorLabels: false + metricRelabelings: [] + relabelings: [] + +## 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: [] + +securityContext: + enabled: true + runAsGroup: 65534 + runAsUser: 65534 + fsGroup: 65534 + +## 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: {} + +## 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: [] + +# Annotations to be added to the pod +podAnnotations: {} + +## Assign a PriorityClassName to pods if set +# priorityClassName: "" + +# Ref: https://kubernetes.io/docs/tasks/run-application/configure-pdb/ +podDisruptionBudget: {} + +updateStrategy: + type: RollingUpdate + +# 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 + - limitranges + - mutatingwebhookconfigurations + - namespaces + - networkpolicies + - nodes + - persistentvolumeclaims + - persistentvolumes + - poddisruptionbudgets + - pods + - replicasets + - replicationcontrollers + - resourcequotas + - secrets + - services + - statefulsets + - storageclasses + - validatingwebhookconfigurations + - volumeattachments + # - verticalpodautoscalers # not a default resource, see also: https://github.com/kubernetes/kube-state-metrics#enabling-verticalpodautoscalers + +# Enabling kubeconfig will pass the --kubeconfig argument to the container +kubeconfig: + enabled: false + # base64 encoded kube-config file + secret: + +# Comma-separated 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: 500m + memory: 128Mi + requests: + cpu: 100m + memory: 64Mi + +## 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 +selfMonitor: + enabled: false + # telemetryHost: 0.0.0.0 + # telemetryPort: 8081 diff --git a/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/templates/NOTES.txt b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/templates/NOTES.txt new file mode 100644 index 000000000..0e8868f0b --- /dev/null +++ b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/templates/NOTES.txt @@ -0,0 +1,112 @@ +{{- if .Values.server.enabled -}} +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://{{ . }} +{{- 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={{ template "prometheus.name" . }},component={{ .Values.server.name }}" -o jsonpath="{.items[0].metadata.name}") + kubectl --namespace {{ .Release.Namespace }} port-forward $POD_NAME 9090 +{{- end }} +{{- 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.servicePort }} 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={{ template "prometheus.name" . }},component={{ .Values.alertmanager.name }}" -o jsonpath="{.items[0].metadata.name}") + kubectl --namespace {{ .Release.Namespace }} port-forward $POD_NAME 9093 +{{- end }} +{{- end }} + +{{- if .Values.alertmanager.persistentVolume.enabled }} +{{- else }} +################################################################################# +###### WARNING: Persistence is disabled!!! You will lose your data when ##### +###### the AlertManager pod is terminated. ##### +################################################################################# +{{- end }} +{{- end }} + +{{- if .Values.nodeExporter.podSecurityPolicy.enabled }} +{{- else }} +################################################################################# +###### WARNING: Pod Security Policy has been moved to a global property. ##### +###### use .Values.podSecurityPolicy.enabled with pod-based ##### +###### annotations ##### +###### (e.g. .Values.nodeExporter.podSecurityPolicy.annotations) ##### +################################################################################# +{{- end }} + +{{ if .Values.pushgateway.enabled }} +The Prometheus PushGateway can be accessed via port {{ .Values.pushgateway.service.servicePort }} on the following DNS name from within your cluster: +{{ template "prometheus.pushgateway.fullname" . }}.{{ .Release.Namespace }}.svc.cluster.local + +{{ if .Values.pushgateway.ingress.enabled -}} +From outside the cluster, the pushgateway URL(s) are: +{{- range .Values.pushgateway.ingress.hosts }} +http://{{ . }} +{{- end }} +{{- else }} +Get the PushGateway URL by running these commands in the same shell: +{{- if contains "NodePort" .Values.pushgateway.service.type }} + export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ template "prometheus.pushgateway.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.pushgateway.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.pushgateway.fullname" . }}' + + export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ template "prometheus.pushgateway.fullname" . }} -o jsonpath='{.status.loadBalancer.ingress[0].ip}') + echo http://$SERVICE_IP:{{ .Values.pushgateway.service.servicePort }} +{{- else if contains "ClusterIP" .Values.pushgateway.service.type }} + export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app={{ template "prometheus.name" . }},component={{ .Values.pushgateway.name }}" -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/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/templates/_helpers.tpl b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/templates/_helpers.tpl new file mode 100644 index 000000000..2d93181bb --- /dev/null +++ b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/templates/_helpers.tpl @@ -0,0 +1,288 @@ +{{/* 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 unified labels for prometheus components +*/}} +{{- define "prometheus.common.matchLabels" -}} +app: {{ template "prometheus.name" . }} +release: {{ .Release.Name }} +{{ include "k8s-triliovault-operator.observability" . }} +{{- end -}} + +{{- define "prometheus.common.metaLabels" -}} +chart: {{ template "prometheus.chart" . }} +heritage: {{ .Release.Service }} +{{ include "k8s-triliovault-operator.observability" . }} +{{- end -}} + +{{- define "prometheus.alertmanager.labels" -}} +{{ include "prometheus.alertmanager.matchLabels" . }} +{{ include "prometheus.common.metaLabels" . }} +{{- end -}} + +{{- define "prometheus.alertmanager.matchLabels" -}} +component: {{ .Values.alertmanager.name | quote }} +app.kubernetes.io/instance: {{ .Values.alertmanager.name | quote }} +{{ include "prometheus.common.matchLabels" . }} +{{- end -}} + +{{- define "prometheus.nodeExporter.labels" -}} +{{ include "prometheus.nodeExporter.matchLabels" . }} +{{ include "prometheus.common.metaLabels" . }} +{{- end -}} + +{{- define "prometheus.nodeExporter.matchLabels" -}} +component: {{ .Values.nodeExporter.name | quote }} +app.kubernetes.io/instance: {{ .Values.nodeExporter.name | quote }} +{{ include "prometheus.common.matchLabels" . }} +{{- end -}} + +{{- define "prometheus.pushgateway.labels" -}} +{{ include "prometheus.pushgateway.matchLabels" . }} +{{ include "prometheus.common.metaLabels" . }} +{{- end -}} + +{{- define "prometheus.pushgateway.matchLabels" -}} +component: {{ .Values.pushgateway.name | quote }} +app.kubernetes.io/instance: {{ .Values.pushgateway.name | quote }} +{{ include "prometheus.common.matchLabels" . }} +{{- end -}} + +{{- define "prometheus.server.labels" -}} +{{ include "prometheus.server.matchLabels" . }} +{{ include "prometheus.common.metaLabels" . }} +{{- end -}} + +{{- define "prometheus.server.matchLabels" -}} +component: {{ .Values.server.name | quote }} +app.kubernetes.io/instance: {{ .Values.server.name | quote }} +{{ 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 alertmanager name. +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +*/}} + +{{- define "prometheus.alertmanager.fullname" -}} +{{- if .Values.alertmanager.fullnameOverride -}} +{{- .Values.alertmanager.fullnameOverride | trunc 63 | trimSuffix "-" -}} +{{- else -}} +{{- $name := default .Chart.Name .Values.nameOverride -}} +{{- if contains $name .Release.Name -}} +{{- printf "%s-%s" .Release.Name .Values.alertmanager.name | trunc 63 | trimSuffix "-" -}} +{{- else -}} +{{- printf "%s-%s-%s" .Release.Name $name .Values.alertmanager.name | trunc 63 | trimSuffix "-" -}} +{{- end -}} +{{- end -}} +{{- end -}} + +{{/* +Create a fully qualified node-exporter name. +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +*/}} +{{- define "prometheus.nodeExporter.fullname" -}} +{{- if .Values.nodeExporter.fullnameOverride -}} +{{- .Values.nodeExporter.fullnameOverride | trunc 63 | trimSuffix "-" -}} +{{- else -}} +{{- $name := default .Chart.Name .Values.nameOverride -}} +{{- if contains $name .Release.Name -}} +{{- printf "%s-%s" .Release.Name .Values.nodeExporter.name | trunc 63 | trimSuffix "-" -}} +{{- else -}} +{{- printf "%s-%s-%s" .Release.Name $name .Values.nodeExporter.name | trunc 63 | trimSuffix "-" -}} +{{- end -}} +{{- 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 -}} + +{{/* +Create a fully qualified pushgateway name. +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +*/}} +{{- define "prometheus.pushgateway.fullname" -}} +{{- if .Values.pushgateway.fullnameOverride -}} +{{- .Values.pushgateway.fullnameOverride | trunc 63 | trimSuffix "-" -}} +{{- else -}} +{{- $name := default .Chart.Name .Values.nameOverride -}} +{{- if contains $name .Release.Name -}} +{{- printf "%s-%s" .Release.Name .Values.pushgateway.name | trunc 63 | trimSuffix "-" -}} +{{- else -}} +{{- printf "%s-%s-%s" .Release.Name $name .Values.pushgateway.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 daemonset. +*/}} +{{- define "prometheus.daemonset.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 podsecuritypolicy. +*/}} +{{- define "prometheus.podSecurityPolicy.apiVersion" -}} +{{- print "policy/v1beta1" -}} +{{- 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 alertmanager component +*/}} +{{- define "prometheus.serviceAccountName.alertmanager" -}} +{{- if .Values.serviceAccounts.alertmanager.create -}} + {{ default (include "prometheus.alertmanager.fullname" .) .Values.serviceAccounts.alertmanager.name }} +{{- else -}} + {{ default "default" .Values.serviceAccounts.alertmanager.name }} +{{- end -}} +{{- end -}} + +{{/* +Create the name of the service account to use for the nodeExporter component +*/}} +{{- define "prometheus.serviceAccountName.nodeExporter" -}} +{{- if .Values.serviceAccounts.nodeExporter.create -}} + {{ default (include "prometheus.nodeExporter.fullname" .) .Values.serviceAccounts.nodeExporter.name }} +{{- else -}} + {{ default "default" .Values.serviceAccounts.nodeExporter.name }} +{{- end -}} +{{- end -}} + +{{/* +Create the name of the service account to use for the pushgateway component +*/}} +{{- define "prometheus.serviceAccountName.pushgateway" -}} +{{- if .Values.serviceAccounts.pushgateway.create -}} + {{ default (include "prometheus.pushgateway.fullname" .) .Values.serviceAccounts.pushgateway.name }} +{{- else -}} + {{ default "default" .Values.serviceAccounts.pushgateway.name }} +{{- end -}} +{{- 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" -}} +{{- if .Values.forceNamespace -}} +{{ printf "namespace: %s" .Values.forceNamespace }} +{{- else -}} +{{ printf "namespace: %s" .Release.Namespace }} +{{- end -}} +{{- end -}} diff --git a/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/templates/alertmanager/clusterrole.yaml b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/templates/alertmanager/clusterrole.yaml new file mode 100644 index 000000000..c732ff4e5 --- /dev/null +++ b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/templates/alertmanager/clusterrole.yaml @@ -0,0 +1,21 @@ +{{- if and .Values.alertmanager.enabled .Values.rbac.create .Values.alertmanager.useClusterRole (not .Values.alertmanager.useExistingRole) -}} +apiVersion: {{ template "rbac.apiVersion" . }} +kind: ClusterRole +metadata: + labels: + {{- include "prometheus.alertmanager.labels" . | nindent 4 }} + name: {{ template "prometheus.alertmanager.fullname" . }} +rules: +{{- if .Values.podSecurityPolicy.enabled }} + - apiGroups: + - extensions + resources: + - podsecuritypolicies + verbs: + - use + resourceNames: + - {{ template "prometheus.alertmanager.fullname" . }} +{{- else }} + [] +{{- end }} +{{- end }} diff --git a/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/templates/alertmanager/clusterrolebinding.yaml b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/templates/alertmanager/clusterrolebinding.yaml new file mode 100644 index 000000000..6f13e98b5 --- /dev/null +++ b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/templates/alertmanager/clusterrolebinding.yaml @@ -0,0 +1,20 @@ +{{- if and .Values.alertmanager.enabled .Values.rbac.create .Values.alertmanager.useClusterRole -}} +apiVersion: {{ template "rbac.apiVersion" . }} +kind: ClusterRoleBinding +metadata: + labels: + {{- include "prometheus.alertmanager.labels" . | nindent 4 }} + name: {{ template "prometheus.alertmanager.fullname" . }} +subjects: + - kind: ServiceAccount + name: {{ template "prometheus.serviceAccountName.alertmanager" . }} +{{ include "prometheus.namespace" . | indent 4 }} +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole +{{- if (not .Values.alertmanager.useExistingRole) }} + name: {{ template "prometheus.alertmanager.fullname" . }} +{{- else }} + name: {{ .Values.alertmanager.useExistingRole }} +{{- end }} +{{- end }} diff --git a/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/templates/alertmanager/cm.yaml b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/templates/alertmanager/cm.yaml new file mode 100644 index 000000000..cb09bf067 --- /dev/null +++ b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/templates/alertmanager/cm.yaml @@ -0,0 +1,19 @@ +{{- if and .Values.alertmanager.enabled (and (empty .Values.alertmanager.configMapOverrideName) (empty .Values.alertmanager.configFromSecret)) -}} +apiVersion: v1 +kind: ConfigMap +metadata: + labels: + {{- include "prometheus.alertmanager.labels" . | nindent 4 }} + name: {{ template "prometheus.alertmanager.fullname" . }} +{{ include "prometheus.namespace" . | indent 2 }} +data: +{{- $root := . -}} +{{- range $key, $value := .Values.alertmanagerFiles }} + {{- if $key | regexMatch ".*\\.ya?ml$" }} + {{ $key }}: | +{{ toYaml $value | default "{}" | indent 4 }} + {{- else }} + {{ $key }}: {{ toYaml $value | indent 4 }} + {{- end }} +{{- end -}} +{{- end -}} diff --git a/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/templates/alertmanager/deploy.yaml b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/templates/alertmanager/deploy.yaml new file mode 100644 index 000000000..8a51d250a --- /dev/null +++ b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/templates/alertmanager/deploy.yaml @@ -0,0 +1,208 @@ +{{- if and .Values.alertmanager.enabled (not .Values.alertmanager.statefulSet.enabled) -}} +apiVersion: {{ template "prometheus.deployment.apiVersion" . }} +kind: Deployment +metadata: +{{- if .Values.alertmanager.deploymentAnnotations }} + annotations: + {{ toYaml .Values.alertmanager.deploymentAnnotations | nindent 4 }} +{{- end }} + labels: + {{- include "prometheus.alertmanager.labels" . | nindent 4 }} + name: {{ template "prometheus.alertmanager.fullname" . }} +{{ include "prometheus.namespace" . | indent 2 }} +spec: + selector: + matchLabels: + {{- include "prometheus.alertmanager.matchLabels" . | nindent 6 }} + replicas: {{ .Values.alertmanager.replicaCount }} + {{- if .Values.alertmanager.strategy }} + strategy: +{{ toYaml .Values.alertmanager.strategy | trim | indent 4 }} + {{ if eq .Values.alertmanager.strategy.type "Recreate" }}rollingUpdate: null{{ end }} +{{- end }} + template: + metadata: + {{- if .Values.alertmanager.podAnnotations }} + annotations: + {{ toYaml .Values.alertmanager.podAnnotations | nindent 8 }} + {{- end }} + labels: + {{- include "prometheus.alertmanager.labels" . | nindent 8 }} + {{- if .Values.alertmanager.podLabels}} + {{ toYaml .Values.alertmanager.podLabels | nindent 8 }} + {{- end}} + spec: +{{- if .Values.alertmanager.schedulerName }} + schedulerName: "{{ .Values.alertmanager.schedulerName }}" +{{- end }} + serviceAccountName: {{ template "prometheus.serviceAccountName.alertmanager" . }} + {{- if .Values.alertmanager.extraInitContainers }} + initContainers: +{{ toYaml .Values.alertmanager.extraInitContainers | indent 8 }} + {{- end }} +{{- if .Values.alertmanager.priorityClassName }} + priorityClassName: "{{ .Values.alertmanager.priorityClassName }}" +{{- end }} + containers: + - name: {{ template "prometheus.name" . }}-{{ .Values.alertmanager.name }} + image: "{{ .Values.alertmanager.image.registry }}/{{ .Values.alertmanager.image.repository }}:{{ .Values.alertmanager.image.tag }}" + imagePullPolicy: "{{ .Values.alertmanager.image.pullPolicy }}" + env: + {{- range $key, $value := .Values.alertmanager.extraEnv }} + - name: {{ $key }} + value: {{ $value }} + {{- end }} + - name: POD_IP + valueFrom: + fieldRef: + apiVersion: v1 + fieldPath: status.podIP + args: + - --config.file=/etc/config/{{ .Values.alertmanager.configFileName }} + - --storage.path={{ .Values.alertmanager.persistentVolume.mountPath }} + {{- if .Values.alertmanager.service.enableMeshPeer }} + - --cluster.listen-address=0.0.0.0:6783 + - --cluster.advertise-address=[$(POD_IP)]:6783 + {{- else }} + - --cluster.listen-address= + {{- end }} + {{- range $key, $value := .Values.alertmanager.extraArgs }} + - --{{ $key }}={{ $value }} + {{- end }} + {{- if .Values.alertmanager.baseURL }} + - --web.external-url={{ .Values.alertmanager.baseURL }} + {{- end }} + {{- range .Values.alertmanager.clusterPeers }} + - --cluster.peer={{ . }} + {{- end }} + + ports: + - containerPort: 9093 + readinessProbe: + httpGet: + path: {{ .Values.alertmanager.prefixURL }}/-/ready + port: 9093 + {{- if .Values.alertmanager.probeHeaders }} + httpHeaders: + {{- range .Values.alertmanager.probeHeaders }} + - name: {{ .name }} + value: {{ .value }} + {{- end }} + {{- end }} + initialDelaySeconds: 30 + timeoutSeconds: 30 + resources: +{{ toYaml .Values.alertmanager.resources | indent 12 }} + volumeMounts: + - name: config-volume + mountPath: /etc/config + - name: storage-volume + mountPath: "{{ .Values.alertmanager.persistentVolume.mountPath }}" + subPath: "{{ .Values.alertmanager.persistentVolume.subPath }}" + {{- range .Values.alertmanager.extraSecretMounts }} + - name: {{ .name }} + mountPath: {{ .mountPath }} + subPath: {{ .subPath }} + readOnly: {{ .readOnly }} + {{- end }} + {{- range .Values.alertmanager.extraConfigmapMounts }} + - name: {{ .name }} + mountPath: {{ .mountPath }} + subPath: {{ .subPath }} + readOnly: {{ .readOnly }} + {{- end }} + + {{- if .Values.configmapReload.alertmanager.enabled }} + - name: {{ template "prometheus.name" . }}-{{ .Values.alertmanager.name }}-{{ .Values.configmapReload.alertmanager.name }} + image: "{{ .Values.configmapReload.alertmanager.image.registry }}/{{ .Values.configmapReload.alertmanager.image.repository }}:{{ .Values.configmapReload.alertmanager.image.tag }}" + imagePullPolicy: "{{ .Values.configmapReload.alertmanager.image.pullPolicy }}" + args: + - --volume-dir=/etc/config + - --webhook-url=http://127.0.0.1:9093{{ .Values.alertmanager.prefixURL }}/-/reload + {{- range $key, $value := .Values.configmapReload.alertmanager.extraArgs }} + - --{{ $key }}={{ $value }} + {{- end }} + {{- range .Values.configmapReload.alertmanager.extraVolumeDirs }} + - --volume-dir={{ . }} + {{- end }} + {{- if .Values.configmapReload.alertmanager.containerPort }} + ports: + - containerPort: {{ .Values.configmapReload.alertmanager.containerPort }} + {{- end }} + resources: +{{ toYaml .Values.configmapReload.alertmanager.resources | indent 12 }} + volumeMounts: + - name: config-volume + mountPath: /etc/config + readOnly: true + {{- range .Values.configmapReload.alertmanager.extraConfigmapMounts }} + - name: {{ $.Values.configmapReload.alertmanager.name }}-{{ .name }} + mountPath: {{ .mountPath }} + subPath: {{ .subPath }} + readOnly: {{ .readOnly }} + {{- end }} + {{- end }} + {{- if .Values.imagePullSecrets }} + imagePullSecrets: +{{ toYaml .Values.imagePullSecrets | indent 8 }} + {{- end }} + {{- if .Values.alertmanager.nodeSelector }} + nodeSelector: +{{ toYaml .Values.alertmanager.nodeSelector | indent 8 }} + {{- end }} + {{- with .Values.alertmanager.dnsConfig }} + dnsConfig: +{{ toYaml . | indent 8 }} + {{- end }} + {{- if .Values.alertmanager.securityContext }} + securityContext: +{{ toYaml .Values.alertmanager.securityContext | indent 8 }} + {{- end }} + {{- if .Values.alertmanager.tolerations }} + tolerations: +{{ toYaml .Values.alertmanager.tolerations | indent 8 }} + {{- end }} + {{- if .Values.alertmanager.affinity }} + affinity: +{{ toYaml .Values.alertmanager.affinity | indent 8 }} + {{- end }} + volumes: + - name: config-volume + {{- if empty .Values.alertmanager.configFromSecret }} + configMap: + name: {{ if .Values.alertmanager.configMapOverrideName }}{{ .Release.Name }}-{{ .Values.alertmanager.configMapOverrideName }}{{- else }}{{ template "prometheus.alertmanager.fullname" . }}{{- end }} + {{- else }} + secret: + secretName: {{ .Values.alertmanager.configFromSecret }} + {{- end }} + {{- range .Values.alertmanager.extraSecretMounts }} + - name: {{ .name }} + secret: + secretName: {{ .secretName }} + {{- with .optional }} + optional: {{ . }} + {{- end }} + {{- end }} + {{- range .Values.alertmanager.extraConfigmapMounts }} + - name: {{ .name }} + configMap: + name: {{ .configMap }} + {{- end }} + {{- range .Values.configmapReload.alertmanager.extraConfigmapMounts }} + - name: {{ $.Values.configmapReload.alertmanager.name }}-{{ .name }} + configMap: + name: {{ .configMap }} + {{- end }} + - name: storage-volume + {{- if .Values.alertmanager.persistentVolume.enabled }} + persistentVolumeClaim: + claimName: {{ if .Values.alertmanager.persistentVolume.existingClaim }}{{ .Values.alertmanager.persistentVolume.existingClaim }}{{- else }}{{ template "prometheus.alertmanager.fullname" . }}{{- end }} + {{- else }} + emptyDir: + {{- if .Values.alertmanager.emptyDir.sizeLimit }} + sizeLimit: {{ .Values.alertmanager.emptyDir.sizeLimit }} + {{- else }} + {} + {{- end -}} + {{- end -}} +{{- end }} diff --git a/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/templates/alertmanager/headless-svc.yaml b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/templates/alertmanager/headless-svc.yaml new file mode 100644 index 000000000..8c402c408 --- /dev/null +++ b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/templates/alertmanager/headless-svc.yaml @@ -0,0 +1,31 @@ +{{- if and .Values.alertmanager.enabled .Values.alertmanager.statefulSet.enabled -}} +apiVersion: v1 +kind: Service +metadata: +{{- if .Values.alertmanager.statefulSet.headless.annotations }} + annotations: +{{ toYaml .Values.alertmanager.statefulSet.headless.annotations | indent 4 }} +{{- end }} + labels: + {{- include "prometheus.alertmanager.labels" . | nindent 4 }} +{{- if .Values.alertmanager.statefulSet.headless.labels }} +{{ toYaml .Values.alertmanager.statefulSet.headless.labels | indent 4 }} +{{- end }} + name: {{ template "prometheus.alertmanager.fullname" . }}-headless +{{ include "prometheus.namespace" . | indent 2 }} +spec: + clusterIP: None + ports: + - name: http + port: {{ .Values.alertmanager.statefulSet.headless.servicePort }} + protocol: TCP + targetPort: 9093 +{{- if .Values.alertmanager.statefulSet.headless.enableMeshPeer }} + - name: meshpeer + port: 6783 + protocol: TCP + targetPort: 6783 +{{- end }} + selector: + {{- include "prometheus.alertmanager.matchLabels" . | nindent 4 }} +{{- end }} diff --git a/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/templates/alertmanager/ingress.yaml b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/templates/alertmanager/ingress.yaml new file mode 100644 index 000000000..2a7b67c08 --- /dev/null +++ b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/templates/alertmanager/ingress.yaml @@ -0,0 +1,57 @@ +{{- if and .Values.alertmanager.enabled .Values.alertmanager.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.alertmanager.fullname" . }} +{{- $servicePort := .Values.alertmanager.service.servicePort -}} +{{- $ingressPath := .Values.alertmanager.ingress.path -}} +{{- $ingressPathType := .Values.alertmanager.ingress.pathType -}} +{{- $extraPaths := .Values.alertmanager.ingress.extraPaths -}} +apiVersion: {{ template "ingress.apiVersion" . }} +kind: Ingress +metadata: +{{- if .Values.alertmanager.ingress.annotations }} + annotations: +{{ toYaml .Values.alertmanager.ingress.annotations | indent 4 }} +{{- end }} + labels: + {{- include "prometheus.alertmanager.labels" . | nindent 4 }} +{{- range $key, $value := .Values.alertmanager.ingress.extraLabels }} + {{ $key }}: {{ $value }} +{{- end }} + name: {{ template "prometheus.alertmanager.fullname" . }} +{{ include "prometheus.namespace" . | indent 2 }} +spec: + {{- if and $ingressSupportsIngressClassName .Values.alertmanager.ingress.ingressClassName }} + ingressClassName: {{ .Values.alertmanager.ingress.ingressClassName }} + {{- end }} + rules: + {{- range .Values.alertmanager.ingress.hosts }} + {{- $url := splitList "/" . }} + - host: {{ first $url }} + http: + paths: +{{ if $extraPaths }} +{{ toYaml $extraPaths | indent 10 }} +{{- end }} + - path: {{ $ingressPath }} + {{- if $ingressSupportsPathType }} + pathType: {{ $ingressPathType }} + {{- end }} + backend: + {{- if $ingressApiIsStable }} + service: + name: {{ $serviceName }} + port: + number: {{ $servicePort }} + {{- else }} + serviceName: {{ $serviceName }} + servicePort: {{ $servicePort }} + {{- end }} + {{- end -}} +{{- if .Values.alertmanager.ingress.tls }} + tls: +{{ toYaml .Values.alertmanager.ingress.tls | indent 4 }} + {{- end -}} +{{- end -}} \ No newline at end of file diff --git a/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/templates/alertmanager/netpol.yaml b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/templates/alertmanager/netpol.yaml new file mode 100644 index 000000000..e44ade60e --- /dev/null +++ b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/templates/alertmanager/netpol.yaml @@ -0,0 +1,20 @@ +{{- if and .Values.alertmanager.enabled .Values.networkPolicy.enabled -}} +apiVersion: {{ template "prometheus.networkPolicy.apiVersion" . }} +kind: NetworkPolicy +metadata: + name: {{ template "prometheus.alertmanager.fullname" . }} +{{ include "prometheus.namespace" . | indent 2 }} + labels: + {{- include "prometheus.alertmanager.labels" . | nindent 4 }} +spec: + podSelector: + matchLabels: + {{- include "prometheus.alertmanager.matchLabels" . | nindent 6 }} + ingress: + - from: + - podSelector: + matchLabels: + {{- include "prometheus.server.matchLabels" . | nindent 12 }} + - ports: + - port: 9093 +{{- end -}} diff --git a/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/templates/alertmanager/pdb.yaml b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/templates/alertmanager/pdb.yaml new file mode 100644 index 000000000..41a92f364 --- /dev/null +++ b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/templates/alertmanager/pdb.yaml @@ -0,0 +1,14 @@ +{{- if .Values.alertmanager.podDisruptionBudget.enabled }} +apiVersion: policy/v1beta1 +kind: PodDisruptionBudget +metadata: + name: {{ template "prometheus.alertmanager.fullname" . }} +{{ include "prometheus.namespace" . | indent 2 }} + labels: + {{- include "prometheus.alertmanager.labels" . | nindent 4 }} +spec: + maxUnavailable: {{ .Values.alertmanager.podDisruptionBudget.maxUnavailable }} + selector: + matchLabels: + {{- include "prometheus.alertmanager.labels" . | nindent 6 }} +{{- end }} diff --git a/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/templates/alertmanager/psp.yaml b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/templates/alertmanager/psp.yaml new file mode 100644 index 000000000..64fb13003 --- /dev/null +++ b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/templates/alertmanager/psp.yaml @@ -0,0 +1,46 @@ +{{- if and .Values.alertmanager.enabled .Values.rbac.create .Values.podSecurityPolicy.enabled }} +apiVersion: {{ template "prometheus.podSecurityPolicy.apiVersion" . }} +kind: PodSecurityPolicy +metadata: + name: {{ template "prometheus.alertmanager.fullname" . }} + labels: + {{- include "prometheus.alertmanager.labels" . | nindent 4 }} + annotations: +{{- if .Values.alertmanager.podSecurityPolicy.annotations }} +{{ toYaml .Values.alertmanager.podSecurityPolicy.annotations | indent 4 }} +{{- end }} +spec: + privileged: false + allowPrivilegeEscalation: false + requiredDropCapabilities: + - ALL + volumes: + - 'configMap' + - 'persistentVolumeClaim' + - 'emptyDir' + - 'secret' + allowedHostPaths: + - pathPrefix: /etc + readOnly: true + - pathPrefix: {{ .Values.alertmanager.persistentVolume.mountPath }} + 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: true +{{- end }} diff --git a/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/templates/alertmanager/pvc.yaml b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/templates/alertmanager/pvc.yaml new file mode 100644 index 000000000..160e296a5 --- /dev/null +++ b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/templates/alertmanager/pvc.yaml @@ -0,0 +1,37 @@ +{{- if not .Values.alertmanager.statefulSet.enabled -}} +{{- if and .Values.alertmanager.enabled .Values.alertmanager.persistentVolume.enabled -}} +{{- if not .Values.alertmanager.persistentVolume.existingClaim -}} +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + {{- if .Values.alertmanager.persistentVolume.annotations }} + annotations: +{{ toYaml .Values.alertmanager.persistentVolume.annotations | indent 4 }} + {{- end }} + labels: + {{- include "prometheus.alertmanager.labels" . | nindent 4 }} + name: {{ template "prometheus.alertmanager.fullname" . }} +{{ include "prometheus.namespace" . | indent 2 }} +spec: + accessModes: +{{ toYaml .Values.alertmanager.persistentVolume.accessModes | indent 4 }} +{{- if .Values.alertmanager.persistentVolume.storageClass }} +{{- if (eq "-" .Values.alertmanager.persistentVolume.storageClass) }} + storageClassName: "" +{{- else }} + storageClassName: "{{ .Values.alertmanager.persistentVolume.storageClass }}" +{{- end }} +{{- end }} +{{- if .Values.alertmanager.persistentVolume.volumeBindingMode }} + volumeBindingMode: "{{ .Values.alertmanager.persistentVolume.volumeBindingMode }}" +{{- end }} + resources: + requests: + storage: "{{ .Values.alertmanager.persistentVolume.size }}" +{{- if .Values.alertmanager.persistentVolume.selector }} + selector: + {{- toYaml .Values.alertmanager.persistentVolume.selector | nindent 4 }} +{{- end -}} +{{- end -}} +{{- end -}} +{{- end -}} diff --git a/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/templates/alertmanager/role.yaml b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/templates/alertmanager/role.yaml new file mode 100644 index 000000000..ce60eaf0a --- /dev/null +++ b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/templates/alertmanager/role.yaml @@ -0,0 +1,24 @@ +{{- if and .Values.alertmanager.enabled .Values.rbac.create (eq .Values.alertmanager.useClusterRole false) (not .Values.alertmanager.useExistingRole) -}} +{{- range $.Values.alertmanager.namespaces }} +apiVersion: {{ template "rbac.apiVersion" . }} +kind: Role +metadata: + labels: + {{- include "prometheus.alertmanager.labels" $ | nindent 4 }} + name: {{ template "prometheus.alertmanager.fullname" $ }} + namespace: {{ . }} +rules: +{{- if $.Values.podSecurityPolicy.enabled }} + - apiGroups: + - extensions + resources: + - podsecuritypolicies + verbs: + - use + resourceNames: + - {{ template "prometheus.alertmanager.fullname" $ }} +{{- else }} + [] +{{- end }} +{{- end }} +{{- end }} diff --git a/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/templates/alertmanager/rolebinding.yaml b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/templates/alertmanager/rolebinding.yaml new file mode 100644 index 000000000..906d6522d --- /dev/null +++ b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/templates/alertmanager/rolebinding.yaml @@ -0,0 +1,23 @@ +{{- if and .Values.alertmanager.enabled .Values.rbac.create (eq .Values.alertmanager.useClusterRole false) -}} +{{ range $.Values.alertmanager.namespaces }} +apiVersion: {{ template "rbac.apiVersion" . }} +kind: RoleBinding +metadata: + labels: + {{- include "prometheus.alertmanager.labels" $ | nindent 4 }} + name: {{ template "prometheus.alertmanager.fullname" $ }} + namespace: {{ . }} +subjects: + - kind: ServiceAccount + name: {{ template "prometheus.serviceAccountName.alertmanager" $ }} +{{ include "prometheus.namespace" $ | indent 4 }} +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role +{{- if (not $.Values.alertmanager.useExistingRole) }} + name: {{ template "prometheus.alertmanager.fullname" $ }} +{{- else }} + name: {{ $.Values.alertmanager.useExistingRole }} +{{- end }} +{{- end }} +{{ end }} diff --git a/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/templates/alertmanager/service.yaml b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/templates/alertmanager/service.yaml new file mode 100644 index 000000000..9edc9ac65 --- /dev/null +++ b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/templates/alertmanager/service.yaml @@ -0,0 +1,53 @@ +{{- if .Values.alertmanager.enabled -}} +apiVersion: v1 +kind: Service +metadata: +{{- if .Values.alertmanager.service.annotations }} + annotations: +{{ toYaml .Values.alertmanager.service.annotations | indent 4 }} +{{- end }} + labels: + {{- include "prometheus.alertmanager.labels" . | nindent 4 }} +{{- if .Values.alertmanager.service.labels }} +{{ toYaml .Values.alertmanager.service.labels | indent 4 }} +{{- end }} + name: {{ template "prometheus.alertmanager.fullname" . }} +{{ include "prometheus.namespace" . | indent 2 }} +spec: +{{- if .Values.alertmanager.service.clusterIP }} + clusterIP: {{ .Values.alertmanager.service.clusterIP }} +{{- end }} +{{- if .Values.alertmanager.service.externalIPs }} + externalIPs: +{{ toYaml .Values.alertmanager.service.externalIPs | indent 4 }} +{{- end }} +{{- if .Values.alertmanager.service.loadBalancerIP }} + loadBalancerIP: {{ .Values.alertmanager.service.loadBalancerIP }} +{{- end }} +{{- if .Values.alertmanager.service.loadBalancerSourceRanges }} + loadBalancerSourceRanges: + {{- range $cidr := .Values.alertmanager.service.loadBalancerSourceRanges }} + - {{ $cidr }} + {{- end }} +{{- end }} + ports: + - name: http + port: {{ .Values.alertmanager.service.servicePort }} + protocol: TCP + targetPort: 9093 + {{- if .Values.alertmanager.service.nodePort }} + nodePort: {{ .Values.alertmanager.service.nodePort }} + {{- end }} +{{- if .Values.alertmanager.service.enableMeshPeer }} + - name: meshpeer + port: 6783 + protocol: TCP + targetPort: 6783 +{{- end }} + selector: + {{- include "prometheus.alertmanager.matchLabels" . | nindent 4 }} +{{- if .Values.alertmanager.service.sessionAffinity }} + sessionAffinity: {{ .Values.alertmanager.service.sessionAffinity }} +{{- end }} + type: "{{ .Values.alertmanager.service.type }}" +{{- end }} diff --git a/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/templates/alertmanager/serviceaccount.yaml b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/templates/alertmanager/serviceaccount.yaml new file mode 100644 index 000000000..a5d996a85 --- /dev/null +++ b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/templates/alertmanager/serviceaccount.yaml @@ -0,0 +1,11 @@ +{{- if and .Values.alertmanager.enabled .Values.serviceAccounts.alertmanager.create -}} +apiVersion: v1 +kind: ServiceAccount +metadata: + labels: + {{- include "prometheus.alertmanager.labels" . | nindent 4 }} + name: {{ template "prometheus.serviceAccountName.alertmanager" . }} +{{ include "prometheus.namespace" . | indent 2 }} + annotations: +{{ toYaml .Values.serviceAccounts.alertmanager.annotations | indent 4 }} +{{- end -}} diff --git a/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/templates/alertmanager/sts.yaml b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/templates/alertmanager/sts.yaml new file mode 100644 index 000000000..b978108ac --- /dev/null +++ b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/templates/alertmanager/sts.yaml @@ -0,0 +1,188 @@ +{{- if and .Values.alertmanager.enabled .Values.alertmanager.statefulSet.enabled -}} +apiVersion: apps/v1 +kind: StatefulSet +metadata: +{{- if .Values.alertmanager.statefulSet.annotations }} + annotations: + {{ toYaml .Values.alertmanager.statefulSet.annotations | nindent 4 }} +{{- end }} + labels: + {{- include "prometheus.alertmanager.labels" . | nindent 4 }} + {{- if .Values.alertmanager.statefulSet.labels}} + {{ toYaml .Values.alertmanager.statefulSet.labels | nindent 4 }} + {{- end}} + name: {{ template "prometheus.alertmanager.fullname" . }} +{{ include "prometheus.namespace" . | indent 2 }} +spec: + serviceName: {{ template "prometheus.alertmanager.fullname" . }}-headless + selector: + matchLabels: + {{- include "prometheus.alertmanager.matchLabels" . | nindent 6 }} + replicas: {{ .Values.alertmanager.replicaCount }} + podManagementPolicy: {{ .Values.alertmanager.statefulSet.podManagementPolicy }} + template: + metadata: + {{- if .Values.alertmanager.podAnnotations }} + annotations: + {{ toYaml .Values.alertmanager.podAnnotations | nindent 8 }} + {{- end }} + labels: + {{- include "prometheus.alertmanager.labels" . | nindent 8 }} + {{- if .Values.alertmanager.podLabels}} + {{ toYaml .Values.alertmanager.podLabels | nindent 8 }} + {{- end}} + spec: +{{- if .Values.alertmanager.affinity }} + affinity: +{{ toYaml .Values.alertmanager.affinity | indent 8 }} +{{- end }} +{{- if .Values.alertmanager.schedulerName }} + schedulerName: "{{ .Values.alertmanager.schedulerName }}" +{{- end }} + serviceAccountName: {{ template "prometheus.serviceAccountName.alertmanager" . }} +{{- if .Values.alertmanager.priorityClassName }} + priorityClassName: "{{ .Values.alertmanager.priorityClassName }}" +{{- end }} + containers: + - name: {{ template "prometheus.name" . }}-{{ .Values.alertmanager.name }} + image: "{{ .Values.alertmanager.image.registry }}/{{ .Values.alertmanager.image.repository }}:{{ .Values.alertmanager.image.tag }}" + imagePullPolicy: "{{ .Values.alertmanager.image.pullPolicy }}" + env: + {{- range $key, $value := .Values.alertmanager.extraEnv }} + - name: {{ $key }} + value: {{ $value }} + {{- end }} + - name: POD_IP + valueFrom: + fieldRef: + apiVersion: v1 + fieldPath: status.podIP + args: + - --config.file=/etc/config/alertmanager.yml + - --storage.path={{ .Values.alertmanager.persistentVolume.mountPath }} + {{- if .Values.alertmanager.statefulSet.headless.enableMeshPeer }} + - --cluster.advertise-address=[$(POD_IP)]:6783 + - --cluster.listen-address=0.0.0.0:6783 + {{- range $n := until (.Values.alertmanager.replicaCount | int) }} + - --cluster.peer={{ template "prometheus.alertmanager.fullname" $ }}-{{ $n }}.{{ template "prometheus.alertmanager.fullname" $ }}-headless:6783 + {{- end }} + {{- else }} + - --cluster.listen-address= + {{- end }} + {{- range $key, $value := .Values.alertmanager.extraArgs }} + - --{{ $key }}={{ $value }} + {{- end }} + {{- if .Values.alertmanager.baseURL }} + - --web.external-url={{ .Values.alertmanager.baseURL }} + {{- end }} + + ports: + - containerPort: 9093 + {{- if .Values.alertmanager.statefulSet.headless.enableMeshPeer }} + - containerPort: 6783 + {{- end }} + readinessProbe: + httpGet: + path: {{ .Values.alertmanager.prefixURL }}/#/status + port: 9093 + initialDelaySeconds: 30 + timeoutSeconds: 30 + resources: +{{ toYaml .Values.alertmanager.resources | indent 12 }} + volumeMounts: + - name: config-volume + mountPath: /etc/config + - name: storage-volume + mountPath: "{{ .Values.alertmanager.persistentVolume.mountPath }}" + subPath: "{{ .Values.alertmanager.persistentVolume.subPath }}" + {{- range .Values.alertmanager.extraSecretMounts }} + - name: {{ .name }} + mountPath: {{ .mountPath }} + subPath: {{ .subPath }} + readOnly: {{ .readOnly }} + {{- end }} + {{- if .Values.configmapReload.alertmanager.enabled }} + - name: {{ template "prometheus.name" . }}-{{ .Values.alertmanager.name }}-{{ .Values.configmapReload.alertmanager.name }} + image: "{{ .Values.configmapReload.alertmanager.image.registry }}/{{ .Values.configmapReload.alertmanager.image.repository }}:{{ .Values.configmapReload.alertmanager.image.tag }}" + imagePullPolicy: "{{ .Values.configmapReload.alertmanager.image.pullPolicy }}" + args: + - --volume-dir=/etc/config + - --webhook-url=http://localhost:9093{{ .Values.alertmanager.prefixURL }}/-/reload + {{- range $key, $value := .Values.configmapReload.alertmanager.extraArgs }} + - --{{ $key }}={{ $value }} + {{- end }} + {{- if .Values.configmapReload.alertmanager.port }} + ports: + - containerPort: {{ .Values.configmapReload.alertmanager.port }} + {{- end }} + resources: +{{ toYaml .Values.configmapReload.alertmanager.resources | indent 12 }} + volumeMounts: + - name: config-volume + mountPath: /etc/config + readOnly: true + {{- end }} + {{- if .Values.imagePullSecrets }} + imagePullSecrets: +{{ toYaml .Values.imagePullSecrets | indent 8 }} + {{- end }} + {{- if .Values.alertmanager.nodeSelector }} + nodeSelector: +{{ toYaml .Values.alertmanager.nodeSelector | indent 8 }} + {{- end }} + {{- if .Values.alertmanager.securityContext }} + securityContext: +{{ toYaml .Values.alertmanager.securityContext | indent 8 }} + {{- end }} + {{- if .Values.alertmanager.tolerations }} + tolerations: +{{ toYaml .Values.alertmanager.tolerations | indent 8 }} + {{- end }} + volumes: + - name: config-volume + {{- if empty .Values.alertmanager.configFromSecret }} + configMap: + name: {{ if .Values.alertmanager.configMapOverrideName }}{{ .Release.Name }}-{{ .Values.alertmanager.configMapOverrideName }}{{- else }}{{ template "prometheus.alertmanager.fullname" . }}{{- end }} + {{- else }} + secret: + secretName: {{ .Values.alertmanager.configFromSecret }} + {{- end }} + {{- range .Values.alertmanager.extraSecretMounts }} + - name: {{ .name }} + secret: + secretName: {{ .secretName }} + {{- with .optional }} + optional: {{ . }} + {{- end }} + {{- end }} +{{- if .Values.alertmanager.persistentVolume.enabled }} + volumeClaimTemplates: + - metadata: + name: storage-volume + {{- if .Values.alertmanager.persistentVolume.annotations }} + annotations: +{{ toYaml .Values.alertmanager.persistentVolume.annotations | indent 10 }} + {{- end }} + spec: + accessModes: +{{ toYaml .Values.alertmanager.persistentVolume.accessModes | indent 10 }} + resources: + requests: + storage: "{{ .Values.alertmanager.persistentVolume.size }}" + {{- if .Values.server.persistentVolume.storageClass }} + {{- if (eq "-" .Values.server.persistentVolume.storageClass) }} + storageClassName: "" + {{- else }} + storageClassName: "{{ .Values.alertmanager.persistentVolume.storageClass }}" + {{- end }} + {{- end }} +{{- else }} + - name: storage-volume + emptyDir: + {{- if .Values.alertmanager.emptyDir.sizeLimit }} + sizeLimit: {{ .Values.alertmanager.emptyDir.sizeLimit }} + {{- else }} + {} + {{- end -}} +{{- end }} +{{- end }} diff --git a/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/templates/node-exporter/daemonset.yaml b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/templates/node-exporter/daemonset.yaml new file mode 100644 index 000000000..010f790a9 --- /dev/null +++ b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/templates/node-exporter/daemonset.yaml @@ -0,0 +1,150 @@ +{{- if .Values.nodeExporter.enabled -}} +apiVersion: {{ template "prometheus.daemonset.apiVersion" . }} +kind: DaemonSet +metadata: +{{- if .Values.nodeExporter.deploymentAnnotations }} + annotations: +{{ toYaml .Values.nodeExporter.deploymentAnnotations | indent 4 }} +{{- end }} + labels: + {{- include "prometheus.nodeExporter.labels" . | nindent 4 }} + name: {{ template "prometheus.nodeExporter.fullname" . }} +{{ include "prometheus.namespace" . | indent 2 }} +spec: + selector: + matchLabels: + {{- include "prometheus.nodeExporter.matchLabels" . | nindent 6 }} + {{- if .Values.nodeExporter.updateStrategy }} + updateStrategy: +{{ toYaml .Values.nodeExporter.updateStrategy | indent 4 }} + {{- end }} + template: + metadata: + {{- if .Values.nodeExporter.podAnnotations }} + annotations: +{{ toYaml .Values.nodeExporter.podAnnotations | indent 8 }} + {{- end }} + labels: + {{- include "prometheus.nodeExporter.labels" . | nindent 8 }} +{{- if .Values.nodeExporter.pod.labels }} +{{ toYaml .Values.nodeExporter.pod.labels | indent 8 }} +{{- end }} + spec: + serviceAccountName: {{ template "prometheus.serviceAccountName.nodeExporter" . }} + {{- if .Values.nodeExporter.extraInitContainers }} + initContainers: +{{ toYaml .Values.nodeExporter.extraInitContainers | indent 8 }} + {{- end }} +{{- if .Values.nodeExporter.priorityClassName }} + priorityClassName: "{{ .Values.nodeExporter.priorityClassName }}" +{{- end }} + containers: + - name: {{ template "prometheus.name" . }}-{{ .Values.nodeExporter.name }} + image: "{{ .Values.nodeExporter.image.registry }}/{{ .Values.nodeExporter.image.repository }}:{{ .Values.nodeExporter.image.tag }}" + imagePullPolicy: "{{ .Values.nodeExporter.image.pullPolicy }}" + args: + - --path.procfs=/host/proc + - --path.sysfs=/host/sys + {{- if .Values.nodeExporter.hostRootfs }} + - --path.rootfs=/host/root + {{- end }} + {{- if .Values.nodeExporter.hostNetwork }} + - --web.listen-address=:{{ .Values.nodeExporter.service.hostPort }} + {{- end }} + {{- range $key, $value := .Values.nodeExporter.extraArgs }} + {{- if $value }} + - --{{ $key }}={{ $value }} + {{- else }} + - --{{ $key }} + {{- end }} + {{- end }} + ports: + - name: metrics + {{- if .Values.nodeExporter.hostNetwork }} + containerPort: {{ .Values.nodeExporter.service.hostPort }} + {{- else }} + containerPort: 9100 + {{- end }} + hostPort: {{ .Values.nodeExporter.service.hostPort }} + resources: +{{ toYaml .Values.nodeExporter.resources | indent 12 }} + {{- if .Values.nodeExporter.container.securityContext }} + securityContext: +{{ toYaml .Values.nodeExporter.container.securityContext | indent 12 }} + {{- end }} + volumeMounts: + - name: proc + mountPath: /host/proc + readOnly: true + - name: sys + mountPath: /host/sys + readOnly: true + {{- if .Values.nodeExporter.hostRootfs }} + - name: root + mountPath: /host/root + mountPropagation: HostToContainer + readOnly: true + {{- end }} + {{- range .Values.nodeExporter.extraHostPathMounts }} + - name: {{ .name }} + mountPath: {{ .mountPath }} + readOnly: {{ .readOnly }} + {{- if .mountPropagation }} + mountPropagation: {{ .mountPropagation }} + {{- end }} + {{- end }} + {{- range .Values.nodeExporter.extraConfigmapMounts }} + - name: {{ .name }} + mountPath: {{ .mountPath }} + readOnly: {{ .readOnly }} + {{- end }} + {{- if .Values.imagePullSecrets }} + imagePullSecrets: +{{ toYaml .Values.imagePullSecrets | indent 8 }} + {{- end }} + {{- if .Values.nodeExporter.hostNetwork }} + hostNetwork: true + {{- end }} + {{- if .Values.nodeExporter.hostPID }} + hostPID: true + {{- end }} + {{- if .Values.nodeExporter.tolerations }} + tolerations: +{{ toYaml .Values.nodeExporter.tolerations | indent 8 }} + {{- end }} + {{- if .Values.nodeExporter.nodeSelector }} + nodeSelector: +{{ toYaml .Values.nodeExporter.nodeSelector | indent 8 }} + {{- end }} + {{- with .Values.nodeExporter.dnsConfig }} + dnsConfig: +{{ toYaml . | indent 8 }} + {{- end }} + {{- if .Values.nodeExporter.securityContext }} + securityContext: +{{ toYaml .Values.nodeExporter.securityContext | indent 8 }} + {{- end }} + volumes: + - name: proc + hostPath: + path: /proc + - name: sys + hostPath: + path: /sys + {{- if .Values.nodeExporter.hostRootfs }} + - name: root + hostPath: + path: / + {{- end }} + {{- range .Values.nodeExporter.extraHostPathMounts }} + - name: {{ .name }} + hostPath: + path: {{ .hostPath }} + {{- end }} + {{- range .Values.nodeExporter.extraConfigmapMounts }} + - name: {{ .name }} + configMap: + name: {{ .configMap }} + {{- end }} + +{{- end -}} diff --git a/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/templates/node-exporter/psp.yaml b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/templates/node-exporter/psp.yaml new file mode 100644 index 000000000..bd9c73bee --- /dev/null +++ b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/templates/node-exporter/psp.yaml @@ -0,0 +1,55 @@ +{{- if and .Values.nodeExporter.enabled .Values.rbac.create .Values.podSecurityPolicy.enabled }} +apiVersion: {{ template "prometheus.podSecurityPolicy.apiVersion" . }} +kind: PodSecurityPolicy +metadata: + name: {{ template "prometheus.nodeExporter.fullname" . }} + labels: + {{- include "prometheus.nodeExporter.labels" . | nindent 4 }} + annotations: +{{- if .Values.nodeExporter.podSecurityPolicy.annotations }} +{{ toYaml .Values.nodeExporter.podSecurityPolicy.annotations | indent 4 }} +{{- end }} +spec: + privileged: false + allowPrivilegeEscalation: false + requiredDropCapabilities: + - ALL + volumes: + - 'configMap' + - 'hostPath' + - 'secret' + allowedHostPaths: + - pathPrefix: /proc + readOnly: true + - pathPrefix: /sys + readOnly: true + - pathPrefix: / + readOnly: true + {{- range .Values.nodeExporter.extraHostPathMounts }} + - pathPrefix: {{ .hostPath }} + readOnly: {{ .readOnly }} + {{- end }} + hostNetwork: {{ .Values.nodeExporter.hostNetwork }} + hostPID: {{ .Values.nodeExporter.hostPID }} + 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 + hostPorts: + - min: 1 + max: 65535 +{{- end }} diff --git a/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/templates/node-exporter/role.yaml b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/templates/node-exporter/role.yaml new file mode 100644 index 000000000..d8ef3ed90 --- /dev/null +++ b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/templates/node-exporter/role.yaml @@ -0,0 +1,17 @@ +{{- if and .Values.nodeExporter.enabled .Values.rbac.create }} +{{- if or (default .Values.nodeExporter.podSecurityPolicy.enabled false) (.Values.podSecurityPolicy.enabled) }} +apiVersion: {{ template "rbac.apiVersion" . }} +kind: Role +metadata: + name: {{ template "prometheus.nodeExporter.fullname" . }} + labels: + {{- include "prometheus.nodeExporter.labels" . | nindent 4 }} +{{ include "prometheus.namespace" . | indent 2 }} +rules: +- apiGroups: ['extensions'] + resources: ['podsecuritypolicies'] + verbs: ['use'] + resourceNames: + - {{ template "prometheus.nodeExporter.fullname" . }} +{{- end }} +{{- end }} diff --git a/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/templates/node-exporter/rolebinding.yaml b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/templates/node-exporter/rolebinding.yaml new file mode 100644 index 000000000..06914b70a --- /dev/null +++ b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/templates/node-exporter/rolebinding.yaml @@ -0,0 +1,19 @@ +{{- if and .Values.nodeExporter.enabled .Values.rbac.create }} +{{- if .Values.podSecurityPolicy.enabled }} +apiVersion: {{ template "rbac.apiVersion" . }} +kind: RoleBinding +metadata: + name: {{ template "prometheus.nodeExporter.fullname" . }} + labels: + {{- include "prometheus.nodeExporter.labels" . | nindent 4 }} +{{ include "prometheus.namespace" . | indent 2 }} +roleRef: + kind: Role + name: {{ template "prometheus.nodeExporter.fullname" . }} + apiGroup: rbac.authorization.k8s.io +subjects: +- kind: ServiceAccount + name: {{ template "prometheus.serviceAccountName.nodeExporter" . }} +{{ include "prometheus.namespace" . | indent 2 }} +{{- end }} +{{- end }} diff --git a/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/templates/node-exporter/serviceaccount.yaml b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/templates/node-exporter/serviceaccount.yaml new file mode 100644 index 000000000..0cf91afba --- /dev/null +++ b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/templates/node-exporter/serviceaccount.yaml @@ -0,0 +1,11 @@ +{{- if and .Values.nodeExporter.enabled .Values.serviceAccounts.nodeExporter.create -}} +apiVersion: v1 +kind: ServiceAccount +metadata: + labels: + {{- include "prometheus.nodeExporter.labels" . | nindent 4 }} + name: {{ template "prometheus.serviceAccountName.nodeExporter" . }} +{{ include "prometheus.namespace" . | indent 2 }} + annotations: +{{ toYaml .Values.serviceAccounts.nodeExporter.annotations | indent 4 }} +{{- end -}} diff --git a/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/templates/node-exporter/svc.yaml b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/templates/node-exporter/svc.yaml new file mode 100644 index 000000000..26d1eaa21 --- /dev/null +++ b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/templates/node-exporter/svc.yaml @@ -0,0 +1,47 @@ +{{- if .Values.nodeExporter.enabled -}} +apiVersion: v1 +kind: Service +metadata: +{{- if .Values.nodeExporter.service.annotations }} + annotations: +{{ toYaml .Values.nodeExporter.service.annotations | indent 4 }} +{{- end }} + labels: + {{- include "prometheus.nodeExporter.labels" . | nindent 4 }} +{{- if .Values.nodeExporter.service.labels }} +{{ toYaml .Values.nodeExporter.service.labels | indent 4 }} +{{- end }} + name: {{ template "prometheus.nodeExporter.fullname" . }} +{{ include "prometheus.namespace" . | indent 2 }} +spec: +{{- if .Values.nodeExporter.service.clusterIP }} + clusterIP: {{ .Values.nodeExporter.service.clusterIP }} +{{- end }} +{{- if .Values.nodeExporter.service.externalIPs }} + externalIPs: +{{ toYaml .Values.nodeExporter.service.externalIPs | indent 4 }} +{{- end }} +{{- if .Values.nodeExporter.service.loadBalancerIP }} + loadBalancerIP: {{ .Values.nodeExporter.service.loadBalancerIP }} +{{- end }} +{{- if .Values.nodeExporter.service.loadBalancerSourceRanges }} + loadBalancerSourceRanges: + {{- range $cidr := .Values.nodeExporter.service.loadBalancerSourceRanges }} + - {{ $cidr }} + {{- end }} +{{- end }} + ports: + - name: metrics + {{- if .Values.nodeExporter.hostNetwork }} + port: {{ .Values.nodeExporter.service.hostPort }} + protocol: TCP + targetPort: {{ .Values.nodeExporter.service.hostPort }} + {{- else }} + port: {{ .Values.nodeExporter.service.servicePort }} + protocol: TCP + targetPort: 9100 + {{- end }} + selector: + {{- include "prometheus.nodeExporter.matchLabels" . | nindent 4 }} + type: "{{ .Values.nodeExporter.service.type }}" +{{- end -}} diff --git a/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/templates/pushgateway/clusterrole.yaml b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/templates/pushgateway/clusterrole.yaml new file mode 100644 index 000000000..76ecf053f --- /dev/null +++ b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/templates/pushgateway/clusterrole.yaml @@ -0,0 +1,21 @@ +{{- if and .Values.pushgateway.enabled .Values.rbac.create -}} +apiVersion: {{ template "rbac.apiVersion" . }} +kind: ClusterRole +metadata: + labels: + {{- include "prometheus.pushgateway.labels" . | nindent 4 }} + name: {{ template "prometheus.pushgateway.fullname" . }} +rules: +{{- if .Values.podSecurityPolicy.enabled }} + - apiGroups: + - extensions + resources: + - podsecuritypolicies + verbs: + - use + resourceNames: + - {{ template "prometheus.pushgateway.fullname" . }} +{{- else }} + [] +{{- end }} +{{- end }} diff --git a/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/templates/pushgateway/clusterrolebinding.yaml b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/templates/pushgateway/clusterrolebinding.yaml new file mode 100644 index 000000000..15770ee50 --- /dev/null +++ b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/templates/pushgateway/clusterrolebinding.yaml @@ -0,0 +1,16 @@ +{{- if and .Values.pushgateway.enabled .Values.rbac.create -}} +apiVersion: {{ template "rbac.apiVersion" . }} +kind: ClusterRoleBinding +metadata: + labels: + {{- include "prometheus.pushgateway.labels" . | nindent 4 }} + name: {{ template "prometheus.pushgateway.fullname" . }} +subjects: + - kind: ServiceAccount + name: {{ template "prometheus.serviceAccountName.pushgateway" . }} +{{ include "prometheus.namespace" . | indent 4 }} +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: {{ template "prometheus.pushgateway.fullname" . }} +{{- end }} diff --git a/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/templates/pushgateway/deploy.yaml b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/templates/pushgateway/deploy.yaml new file mode 100644 index 000000000..9314a1f45 --- /dev/null +++ b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/templates/pushgateway/deploy.yaml @@ -0,0 +1,119 @@ +{{- if .Values.pushgateway.enabled -}} +apiVersion: {{ template "prometheus.deployment.apiVersion" . }} +kind: Deployment +metadata: +{{- if .Values.pushgateway.deploymentAnnotations }} + annotations: + {{ toYaml .Values.pushgateway.deploymentAnnotations | nindent 4 }} +{{- end }} + labels: + {{- include "prometheus.pushgateway.labels" . | nindent 4 }} + name: {{ template "prometheus.pushgateway.fullname" . }} +{{ include "prometheus.namespace" . | indent 2 }} +spec: + selector: + {{- if .Values.schedulerName }} + schedulerName: "{{ .Values.schedulerName }}" + {{- end }} + matchLabels: + {{- include "prometheus.pushgateway.matchLabels" . | nindent 6 }} + replicas: {{ .Values.pushgateway.replicaCount }} + {{- if .Values.pushgateway.strategy }} + strategy: +{{ toYaml .Values.pushgateway.strategy | trim | indent 4 }} + {{ if eq .Values.pushgateway.strategy.type "Recreate" }}rollingUpdate: null{{ end }} +{{- end }} + template: + metadata: + {{- if .Values.pushgateway.podAnnotations }} + annotations: + {{ toYaml .Values.pushgateway.podAnnotations | nindent 8 }} + {{- end }} + labels: + {{- include "prometheus.pushgateway.labels" . | nindent 8 }} + {{- if .Values.pushgateway.podLabels }} + {{ toYaml .Values.pushgateway.podLabels | nindent 8 }} + {{- end }} + spec: + serviceAccountName: {{ template "prometheus.serviceAccountName.pushgateway" . }} + {{- if .Values.pushgateway.extraInitContainers }} + initContainers: +{{ toYaml .Values.pushgateway.extraInitContainers | indent 8 }} + {{- end }} +{{- if .Values.pushgateway.priorityClassName }} + priorityClassName: "{{ .Values.pushgateway.priorityClassName }}" +{{- end }} + containers: + - name: {{ template "prometheus.name" . }}-{{ .Values.pushgateway.name }} + image: "{{ .Values.pushgateway.image.registry }}/{{ .Values.pushgateway.image.repository }}:{{ .Values.pushgateway.image.tag }}" + imagePullPolicy: "{{ .Values.pushgateway.image.pullPolicy }}" + args: + {{- range $key, $value := .Values.pushgateway.extraArgs }} + {{- $stringvalue := toString $value }} + {{- if eq $stringvalue "true" }} + - --{{ $key }} + {{- else }} + - --{{ $key }}={{ $value }} + {{- end }} + {{- end }} + ports: + - containerPort: 9091 + livenessProbe: + httpGet: + {{- if (index .Values "pushgateway" "extraArgs" "web.route-prefix") }} + path: /{{ index .Values "pushgateway" "extraArgs" "web.route-prefix" }}/-/healthy + {{- else }} + path: /-/healthy + {{- end }} + port: 9091 + initialDelaySeconds: 10 + timeoutSeconds: 10 + readinessProbe: + httpGet: + {{- if (index .Values "pushgateway" "extraArgs" "web.route-prefix") }} + path: /{{ index .Values "pushgateway" "extraArgs" "web.route-prefix" }}/-/ready + {{- else }} + path: /-/ready + {{- end }} + port: 9091 + initialDelaySeconds: 10 + timeoutSeconds: 10 + resources: +{{ toYaml .Values.pushgateway.resources | indent 12 }} + {{- if .Values.pushgateway.persistentVolume.enabled }} + volumeMounts: + - name: storage-volume + mountPath: "{{ .Values.pushgateway.persistentVolume.mountPath }}" + subPath: "{{ .Values.pushgateway.persistentVolume.subPath }}" + {{- end }} + {{- if .Values.imagePullSecrets }} + imagePullSecrets: +{{ toYaml .Values.imagePullSecrets | indent 8 }} + {{- end }} + {{- if .Values.pushgateway.nodeSelector }} + nodeSelector: +{{ toYaml .Values.pushgateway.nodeSelector | indent 8 }} + {{- end }} + {{- with .Values.pushgateway.dnsConfig }} + dnsConfig: +{{ toYaml . | indent 8 }} + {{- end }} + {{- if .Values.pushgateway.securityContext }} + securityContext: +{{ toYaml .Values.pushgateway.securityContext | indent 8 }} + {{- end }} + {{- if .Values.pushgateway.tolerations }} + tolerations: +{{ toYaml .Values.pushgateway.tolerations | indent 8 }} + {{- end }} + {{- if .Values.pushgateway.affinity }} + affinity: +{{ toYaml .Values.pushgateway.affinity | indent 8 }} + {{- end }} + {{- if .Values.pushgateway.persistentVolume.enabled }} + volumes: + - name: storage-volume + persistentVolumeClaim: + claimName: {{ if .Values.pushgateway.persistentVolume.existingClaim }}{{ .Values.pushgateway.persistentVolume.existingClaim }}{{- else }}{{ template "prometheus.pushgateway.fullname" . }}{{- end }} + {{- end -}} +{{- end }} diff --git a/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/templates/pushgateway/ingress.yaml b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/templates/pushgateway/ingress.yaml new file mode 100644 index 000000000..2ff72abd5 --- /dev/null +++ b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/templates/pushgateway/ingress.yaml @@ -0,0 +1,54 @@ +{{- if and .Values.pushgateway.enabled .Values.pushgateway.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.pushgateway.fullname" . }} +{{- $servicePort := .Values.pushgateway.service.servicePort -}} +{{- $ingressPath := .Values.pushgateway.ingress.path -}} +{{- $ingressPathType := .Values.pushgateway.ingress.pathType -}} +{{- $extraPaths := .Values.pushgateway.ingress.extraPaths -}} +apiVersion: {{ template "ingress.apiVersion" . }} +kind: Ingress +metadata: +{{- if .Values.pushgateway.ingress.annotations }} + annotations: +{{ toYaml .Values.pushgateway.ingress.annotations | indent 4}} +{{- end }} + labels: + {{- include "prometheus.pushgateway.labels" . | nindent 4 }} + name: {{ template "prometheus.pushgateway.fullname" . }} +{{ include "prometheus.namespace" . | indent 2 }} +spec: + {{- if and $ingressSupportsIngressClassName .Values.pushgateway.ingress.ingressClassName }} + ingressClassName: {{ .Values.pushgateway.ingress.ingressClassName }} + {{- end }} + rules: + {{- range .Values.pushgateway.ingress.hosts }} + {{- $url := splitList "/" . }} + - host: {{ first $url }} + http: + paths: +{{ if $extraPaths }} +{{ toYaml $extraPaths | indent 10 }} +{{- end }} + - path: {{ $ingressPath }} + {{- if $ingressSupportsPathType }} + pathType: {{ $ingressPathType }} + {{- end }} + backend: + {{- if $ingressApiIsStable }} + service: + name: {{ $serviceName }} + port: + number: {{ $servicePort }} + {{- else }} + serviceName: {{ $serviceName }} + servicePort: {{ $servicePort }} + {{- end }} + {{- end -}} +{{- if .Values.pushgateway.ingress.tls }} + tls: +{{ toYaml .Values.pushgateway.ingress.tls | indent 4 }} + {{- end -}} +{{- end -}} \ No newline at end of file diff --git a/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/templates/pushgateway/netpol.yaml b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/templates/pushgateway/netpol.yaml new file mode 100644 index 000000000..c8d1fb37e --- /dev/null +++ b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/templates/pushgateway/netpol.yaml @@ -0,0 +1,20 @@ +{{- if and .Values.pushgateway.enabled .Values.networkPolicy.enabled -}} +apiVersion: {{ template "prometheus.networkPolicy.apiVersion" . }} +kind: NetworkPolicy +metadata: + name: {{ template "prometheus.pushgateway.fullname" . }} +{{ include "prometheus.namespace" . | indent 2 }} + labels: + {{- include "prometheus.pushgateway.labels" . | nindent 4 }} +spec: + podSelector: + matchLabels: + {{- include "prometheus.pushgateway.matchLabels" . | nindent 6 }} + ingress: + - from: + - podSelector: + matchLabels: + {{- include "prometheus.server.matchLabels" . | nindent 12 }} + - ports: + - port: 9091 +{{- end -}} diff --git a/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/templates/pushgateway/pdb.yaml b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/templates/pushgateway/pdb.yaml new file mode 100644 index 000000000..50beb486d --- /dev/null +++ b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/templates/pushgateway/pdb.yaml @@ -0,0 +1,14 @@ +{{- if .Values.pushgateway.podDisruptionBudget.enabled }} +apiVersion: policy/v1beta1 +kind: PodDisruptionBudget +metadata: + name: {{ template "prometheus.pushgateway.fullname" . }} +{{ include "prometheus.namespace" . | indent 2 }} + labels: + {{- include "prometheus.pushgateway.labels" . | nindent 4 }} +spec: + maxUnavailable: {{ .Values.pushgateway.podDisruptionBudget.maxUnavailable }} + selector: + matchLabels: + {{- include "prometheus.pushgateway.labels" . | nindent 6 }} +{{- end }} diff --git a/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/templates/pushgateway/psp.yaml b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/templates/pushgateway/psp.yaml new file mode 100644 index 000000000..1ca3267f8 --- /dev/null +++ b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/templates/pushgateway/psp.yaml @@ -0,0 +1,42 @@ +{{- if and .Values.pushgateway.enabled .Values.rbac.create .Values.podSecurityPolicy.enabled }} +apiVersion: {{ template "prometheus.podSecurityPolicy.apiVersion" . }} +kind: PodSecurityPolicy +metadata: + name: {{ template "prometheus.pushgateway.fullname" . }} + labels: + {{- include "prometheus.pushgateway.labels" . | nindent 4 }} + annotations: +{{- if .Values.pushgateway.podSecurityPolicy.annotations }} +{{ toYaml .Values.pushgateway.podSecurityPolicy.annotations | indent 4 }} +{{- end }} +spec: + privileged: false + allowPrivilegeEscalation: false + requiredDropCapabilities: + - ALL + volumes: + - 'persistentVolumeClaim' + - 'secret' + allowedHostPaths: + - pathPrefix: {{ .Values.pushgateway.persistentVolume.mountPath }} + 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: true +{{- end }} diff --git a/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/templates/pushgateway/pvc.yaml b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/templates/pushgateway/pvc.yaml new file mode 100644 index 000000000..d5d64ddcc --- /dev/null +++ b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/templates/pushgateway/pvc.yaml @@ -0,0 +1,31 @@ +{{- if .Values.pushgateway.persistentVolume.enabled -}} +{{- if not .Values.pushgateway.persistentVolume.existingClaim -}} +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + {{- if .Values.pushgateway.persistentVolume.annotations }} + annotations: +{{ toYaml .Values.pushgateway.persistentVolume.annotations | indent 4 }} + {{- end }} + labels: + {{- include "prometheus.pushgateway.labels" . | nindent 4 }} + name: {{ template "prometheus.pushgateway.fullname" . }} +{{ include "prometheus.namespace" . | indent 2 }} +spec: + accessModes: +{{ toYaml .Values.pushgateway.persistentVolume.accessModes | indent 4 }} +{{- if .Values.pushgateway.persistentVolume.storageClass }} +{{- if (eq "-" .Values.pushgateway.persistentVolume.storageClass) }} + storageClassName: "" +{{- else }} + storageClassName: "{{ .Values.pushgateway.persistentVolume.storageClass }}" +{{- end }} +{{- end }} +{{- if .Values.pushgateway.persistentVolume.volumeBindingMode }} + volumeBindingMode: "{{ .Values.pushgateway.persistentVolume.volumeBindingMode }}" +{{- end }} + resources: + requests: + storage: "{{ .Values.pushgateway.persistentVolume.size }}" +{{- end -}} +{{- end -}} diff --git a/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/templates/pushgateway/service.yaml b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/templates/pushgateway/service.yaml new file mode 100644 index 000000000..f05f17c42 --- /dev/null +++ b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/templates/pushgateway/service.yaml @@ -0,0 +1,41 @@ +{{- if .Values.pushgateway.enabled -}} +apiVersion: v1 +kind: Service +metadata: +{{- if .Values.pushgateway.service.annotations }} + annotations: +{{ toYaml .Values.pushgateway.service.annotations | indent 4}} +{{- end }} + labels: + {{- include "prometheus.pushgateway.labels" . | nindent 4 }} +{{- if .Values.pushgateway.service.labels }} +{{ toYaml .Values.pushgateway.service.labels | indent 4}} +{{- end }} + name: {{ template "prometheus.pushgateway.fullname" . }} +{{ include "prometheus.namespace" . | indent 2 }} +spec: +{{- if .Values.pushgateway.service.clusterIP }} + clusterIP: {{ .Values.pushgateway.service.clusterIP }} +{{- end }} +{{- if .Values.pushgateway.service.externalIPs }} + externalIPs: +{{ toYaml .Values.pushgateway.service.externalIPs | indent 4 }} +{{- end }} +{{- if .Values.pushgateway.service.loadBalancerIP }} + loadBalancerIP: {{ .Values.pushgateway.service.loadBalancerIP }} +{{- end }} +{{- if .Values.pushgateway.service.loadBalancerSourceRanges }} + loadBalancerSourceRanges: + {{- range $cidr := .Values.pushgateway.service.loadBalancerSourceRanges }} + - {{ $cidr }} + {{- end }} +{{- end }} + ports: + - name: http + port: {{ .Values.pushgateway.service.servicePort }} + protocol: TCP + targetPort: 9091 + selector: + {{- include "prometheus.pushgateway.matchLabels" . | nindent 4 }} + type: "{{ .Values.pushgateway.service.type }}" +{{- end }} diff --git a/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/templates/pushgateway/serviceaccount.yaml b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/templates/pushgateway/serviceaccount.yaml new file mode 100644 index 000000000..8c0b876f3 --- /dev/null +++ b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/templates/pushgateway/serviceaccount.yaml @@ -0,0 +1,11 @@ +{{- if and .Values.pushgateway.enabled .Values.serviceAccounts.pushgateway.create -}} +apiVersion: v1 +kind: ServiceAccount +metadata: + labels: + {{- include "prometheus.pushgateway.labels" . | nindent 4 }} + name: {{ template "prometheus.serviceAccountName.pushgateway" . }} +{{ include "prometheus.namespace" . | indent 2 }} + annotations: +{{ toYaml .Values.serviceAccounts.pushgateway.annotations | indent 4 }} +{{- end -}} diff --git a/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/templates/pushgateway/vpa.yaml b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/templates/pushgateway/vpa.yaml new file mode 100644 index 000000000..0ac54f9fe --- /dev/null +++ b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/templates/pushgateway/vpa.yaml @@ -0,0 +1,20 @@ +{{- if .Values.pushgateway.enabled -}} +{{- if .Values.pushgateway.verticalAutoscaler.enabled -}} +apiVersion: autoscaling.k8s.io/v1beta2 +kind: VerticalPodAutoscaler +metadata: + labels: + {{- include "prometheus.pushgateway.labels" . | nindent 4 }} + name: {{ template "prometheus.pushgateway.fullname" . }}-vpa +{{ include "prometheus.namespace" . | indent 2 }} +spec: + targetRef: + apiVersion: "apps/v1" + kind: Deployment + name: {{ template "prometheus.pushgateway.fullname" . }} + updatePolicy: + updateMode: {{ .Values.pushgateway.verticalAutoscaler.updateMode | default "Off" | quote }} + resourcePolicy: + containerPolicies: {{ .Values.pushgateway.verticalAutoscaler.containerPolicies | default list | toYaml | trim | nindent 4 }} +{{- end -}} {{/* if .Values.pushgateway.verticalAutoscaler.enabled */}} +{{- end -}} {{/* .Values.pushgateway.enabled */}} diff --git a/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/templates/server/clusterrole.yaml b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/templates/server/clusterrole.yaml new file mode 100644 index 000000000..2520235ab --- /dev/null +++ b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/templates/server/clusterrole.yaml @@ -0,0 +1,48 @@ +{{- if and .Values.server.enabled .Values.rbac.create (empty .Values.server.useExistingClusterRoleName) -}} +apiVersion: {{ template "rbac.apiVersion" . }} +kind: ClusterRole +metadata: + labels: + {{- include "prometheus.server.labels" . | nindent 4 }} + name: {{ template "prometheus.server.fullname" . }} +rules: +{{- if .Values.podSecurityPolicy.enabled }} + - 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 + - nonResourceURLs: + - "/metrics" + verbs: + - get +{{- end }} diff --git a/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/templates/server/clusterrolebinding.yaml b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/templates/server/clusterrolebinding.yaml new file mode 100644 index 000000000..5a79611ff --- /dev/null +++ b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/templates/server/clusterrolebinding.yaml @@ -0,0 +1,16 @@ +{{- if and .Values.server.enabled .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: {{ template "prometheus.server.fullname" . }} +subjects: + - kind: ServiceAccount + name: {{ template "prometheus.serviceAccountName.server" . }} +{{ include "prometheus.namespace" . | indent 4 }} +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: {{ template "prometheus.server.fullname" . }} +{{- end }} diff --git a/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/templates/server/cm.yaml b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/templates/server/cm.yaml new file mode 100644 index 000000000..a0a813ae2 --- /dev/null +++ b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/templates/server/cm.yaml @@ -0,0 +1,85 @@ +{{- if .Values.server.enabled -}} +{{- if (empty .Values.server.configMapOverrideName) -}} +apiVersion: v1 +kind: ConfigMap +metadata: + labels: + {{- include "prometheus.server.labels" . | nindent 4 }} + name: {{ template "prometheus.server.fullname" . }} +{{ include "prometheus.namespace" . | indent 2 }} +data: +{{- $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: +{{ $root.Values.server.remoteWrite | toYaml | indent 4 }} +{{- end }} +{{- if $root.Values.server.remoteRead }} + remote_read: +{{ $root.Values.server.remoteRead | toYaml | 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] + regex: {{ template "prometheus.name" $root }} + action: keep + - source_labels: [__meta_kubernetes_pod_label_component] + regex: alertmanager + action: keep + - source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_probe] + regex: {{ index $root.Values.alertmanager.podAnnotations "prometheus.io/probe" | default ".*" }} + action: keep + - source_labels: [__meta_kubernetes_pod_container_port_number] + regex: "9093" + action: keep +{{- end -}} +{{- end -}} +{{- end -}} +{{- end -}} +{{- end -}} +{{- end -}} diff --git a/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/templates/server/deploy.yaml b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/templates/server/deploy.yaml new file mode 100644 index 000000000..22808929e --- /dev/null +++ b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/templates/server/deploy.yaml @@ -0,0 +1,324 @@ +{{- if .Values.server.enabled -}} +{{- 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" . }} +{{ include "prometheus.namespace" . | indent 2 }} +spec: + selector: + matchLabels: + {{- include "prometheus.server.matchLabels" . | nindent 6 }} + replicas: {{ .Values.server.replicaCount }} + {{- 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}} + 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 .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 }} + image: "{{ .Values.configmapReload.prometheus.image.registry }}/{{ .Values.configmapReload.prometheus.image.repository }}:{{ .Values.configmapReload.prometheus.image.tag }}" + imagePullPolicy: "{{ .Values.configmapReload.prometheus.image.pullPolicy }}" + args: + - --volume-dir=/etc/config + - --webhook-url=http://127.0.0.1:9090{{ .Values.server.prefixURL }}/-/reload + {{- range $key, $value := .Values.configmapReload.prometheus.extraArgs }} + - --{{ $key }}={{ $value }} + {{- end }} + {{- range .Values.configmapReload.prometheus.extraVolumeDirs }} + - --volume-dir={{ . }} + {{- end }} + {{- if .Values.configmapReload.prometheus.containerPort }} + ports: + - containerPort: {{ .Values.configmapReload.prometheus.containerPort }} + {{- end }} + resources: +{{ toYaml .Values.configmapReload.prometheus.resources | indent 12 }} + 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 }} + {{- end }} + + - name: {{ template "prometheus.name" . }}-{{ .Values.server.name }} + image: "{{ .Values.server.image.registry }}/{{ .Values.server.image.repository }}:{{ .Values.server.image.tag }}" + imagePullPolicy: "{{ .Values.server.image.pullPolicy }}" + {{- 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 }} + - --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 }} + - --{{ $key }}={{ $value }} + {{- 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.hostPort }} + hostPort: {{ .Values.server.hostPort }} + {{- end }} + readinessProbe: + {{- if not .Values.server.tcpSocketProbeEnabled }} + httpGet: + path: {{ .Values.server.prefixURL }}/-/ready + 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 }} + 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 }} + {{- if .Values.server.probeHeaders }} + httpHeaders: + {{- range .Values.server.probeHeaders}} + - name: {{ .name }} + value: {{ .value }} + {{- end }} + {{- 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 }} + resources: +{{ toYaml .Values.server.resources | indent 12 }} + 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 }} + {{- if .Values.server.containerSecurityContext }} + securityContext: + {{- toYaml .Values.server.containerSecurityContext | 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 }} + {{- if .Values.server.securityContext }} + securityContext: +{{ toYaml .Values.server.securityContext | indent 8 }} + {{- end }} + {{- if .Values.server.tolerations }} + tolerations: +{{ toYaml .Values.server.tolerations | indent 8 }} + {{- end }} + {{- if .Values.server.affinity }} + affinity: +{{ toYaml .Values.server.affinity | indent 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 -}} +{{- end -}} diff --git a/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/templates/server/extra-manifests.yaml b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/templates/server/extra-manifests.yaml new file mode 100644 index 000000000..e46d4d8b9 --- /dev/null +++ b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/templates/server/extra-manifests.yaml @@ -0,0 +1,4 @@ +{{ range .Values.server.extraObjects }} +--- +{{ tpl (toYaml .) $ }} +{{ end }} diff --git a/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/templates/server/headless-svc.yaml b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/templates/server/headless-svc.yaml new file mode 100644 index 000000000..d519f4e0e --- /dev/null +++ b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/templates/server/headless-svc.yaml @@ -0,0 +1,37 @@ +{{- if .Values.server.enabled -}} +{{- 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 +{{ include "prometheus.namespace" . | indent 2 }} +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 -}} +{{- end -}} diff --git a/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/templates/server/ingress.yaml b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/templates/server/ingress.yaml new file mode 100644 index 000000000..000f39cab --- /dev/null +++ b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/templates/server/ingress.yaml @@ -0,0 +1,59 @@ +{{- if .Values.server.enabled -}} +{{- 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.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" . }} +{{ include "prometheus.namespace" . | indent 2 }} +spec: + {{- if and $ingressSupportsIngressClassName .Values.server.ingress.ingressClassName }} + ingressClassName: {{ .Values.server.ingress.ingressClassName }} + {{- end }} + rules: + {{- range .Values.server.ingress.hosts }} + {{- $url := splitList "/" . }} + - host: {{ first $url }} + http: + paths: +{{ if $extraPaths }} +{{ toYaml $extraPaths | indent 10 }} +{{- end }} + - path: {{ $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: +{{ toYaml .Values.server.ingress.tls | indent 4 }} + {{- end -}} +{{- end -}} +{{- end -}} diff --git a/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/templates/server/netpol.yaml b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/templates/server/netpol.yaml new file mode 100644 index 000000000..c8870e9ff --- /dev/null +++ b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/templates/server/netpol.yaml @@ -0,0 +1,18 @@ +{{- if .Values.server.enabled -}} +{{- if .Values.networkPolicy.enabled }} +apiVersion: {{ template "prometheus.networkPolicy.apiVersion" . }} +kind: NetworkPolicy +metadata: + name: {{ template "prometheus.server.fullname" . }} +{{ include "prometheus.namespace" . | indent 2 }} + labels: + {{- include "prometheus.server.labels" . | nindent 4 }} +spec: + podSelector: + matchLabels: + {{- include "prometheus.server.matchLabels" . | nindent 6 }} + ingress: + - ports: + - port: 9090 +{{- end }} +{{- end }} diff --git a/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/templates/server/pdb.yaml b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/templates/server/pdb.yaml new file mode 100644 index 000000000..364cb5b49 --- /dev/null +++ b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/templates/server/pdb.yaml @@ -0,0 +1,14 @@ +{{- if .Values.server.podDisruptionBudget.enabled }} +apiVersion: policy/v1beta1 +kind: PodDisruptionBudget +metadata: + name: {{ template "prometheus.server.fullname" . }} +{{ include "prometheus.namespace" . | indent 2 }} + labels: + {{- include "prometheus.server.labels" . | nindent 4 }} +spec: + maxUnavailable: {{ .Values.server.podDisruptionBudget.maxUnavailable }} + selector: + matchLabels: + {{- include "prometheus.server.labels" . | nindent 6 }} +{{- end }} diff --git a/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/templates/server/psp.yaml b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/templates/server/psp.yaml new file mode 100644 index 000000000..e2b885f16 --- /dev/null +++ b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/templates/server/psp.yaml @@ -0,0 +1,51 @@ +{{- if and .Values.server.enabled .Values.rbac.create .Values.podSecurityPolicy.enabled }} +apiVersion: {{ template "prometheus.podSecurityPolicy.apiVersion" . }} +kind: PodSecurityPolicy +metadata: + name: {{ template "prometheus.server.fullname" . }} + labels: + {{- include "prometheus.server.labels" . | nindent 4 }} + annotations: +{{- if .Values.server.podSecurityPolicy.annotations }} +{{ toYaml .Values.server.podSecurityPolicy.annotations | indent 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 }} diff --git a/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/templates/server/pvc.yaml b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/templates/server/pvc.yaml new file mode 100644 index 000000000..a7355365c --- /dev/null +++ b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/templates/server/pvc.yaml @@ -0,0 +1,39 @@ +{{- if .Values.server.enabled -}} +{{- 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 }} + name: {{ template "prometheus.server.fullname" . }} +{{ include "prometheus.namespace" . | indent 2 }} +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 -}} +{{- end -}} +{{- end -}} +{{- end -}} +{{- end -}} diff --git a/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/templates/server/rolebinding.yaml b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/templates/server/rolebinding.yaml new file mode 100644 index 000000000..93ce3ee13 --- /dev/null +++ b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/templates/server/rolebinding.yaml @@ -0,0 +1,20 @@ +{{- if and .Values.server.enabled .Values.rbac.create .Values.server.useExistingClusterRoleName .Values.server.namespaces -}} +{{ range $.Values.server.namespaces -}} +--- +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" $ }} +{{ include "prometheus.namespace" $ | indent 4 }} +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: {{ $.Values.server.useExistingClusterRoleName }} +{{ end -}} +{{ end -}} diff --git a/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/templates/server/service.yaml b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/templates/server/service.yaml new file mode 100644 index 000000000..01c5a4a8a --- /dev/null +++ b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/templates/server/service.yaml @@ -0,0 +1,60 @@ +{{- if and .Values.server.enabled .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" . }} +{{ include "prometheus.namespace" . | indent 2 }} +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 }} + 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/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/templates/server/serviceaccount.yaml b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/templates/server/serviceaccount.yaml new file mode 100644 index 000000000..9c0502ab7 --- /dev/null +++ b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/templates/server/serviceaccount.yaml @@ -0,0 +1,13 @@ +{{- if .Values.server.enabled -}} +{{- if .Values.serviceAccounts.server.create }} +apiVersion: v1 +kind: ServiceAccount +metadata: + labels: + {{- include "prometheus.server.labels" . | nindent 4 }} + name: {{ template "prometheus.serviceAccountName.server" . }} +{{ include "prometheus.namespace" . | indent 2 }} + annotations: +{{ toYaml .Values.serviceAccounts.server.annotations | indent 4 }} +{{- end }} +{{- end }} diff --git a/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/templates/server/sts.yaml b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/templates/server/sts.yaml new file mode 100644 index 000000000..3f76fa9ba --- /dev/null +++ b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/templates/server/sts.yaml @@ -0,0 +1,302 @@ +{{- if .Values.server.enabled -}} +{{- 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" . }} +{{ include "prometheus.namespace" . | indent 2 }} +spec: + serviceName: {{ template "prometheus.server.fullname" . }}-headless + selector: + matchLabels: + {{- include "prometheus.server.matchLabels" . | nindent 6 }} + replicas: {{ .Values.server.replicaCount }} + 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 .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 }} + image: "{{ .Values.configmapReload.prometheus.image.registry }}/{{ .Values.configmapReload.prometheus.image.repository }}:{{ .Values.configmapReload.prometheus.image.tag }}" + imagePullPolicy: "{{ .Values.configmapReload.prometheus.image.pullPolicy }}" + args: + - --volume-dir=/etc/config + - --webhook-url=http://127.0.0.1:9090{{ .Values.server.prefixURL }}/-/reload + {{- range $key, $value := .Values.configmapReload.prometheus.extraArgs }} + - --{{ $key }}={{ $value }} + {{- end }} + {{- range .Values.configmapReload.prometheus.extraVolumeDirs }} + - --volume-dir={{ . }} + {{- end }} + {{- if .Values.configmapReload.prometheus.containerPort }} + ports: + - containerPort: {{ .Values.configmapReload.prometheus.containerPort }} + {{- end }} + resources: +{{ toYaml .Values.configmapReload.prometheus.resources | indent 12 }} + 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 }} + {{- end }} + + - name: {{ template "prometheus.name" . }}-{{ .Values.server.name }} + image: "{{ .Values.server.image.registry }}/{{ .Values.server.image.repository }}:{{ .Values.server.image.tag }}" + imagePullPolicy: "{{ .Values.server.image.pullPolicy }}" + {{- 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 }} + - --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 }} + - --{{ $key }}={{ $value }} + {{- end }} + {{- if .Values.server.baseURL }} + - --web.external-url={{ .Values.server.baseURL }} + {{- end }} + {{- end }} + ports: + - containerPort: 9090 + {{- 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 }} + {{- 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 }} + {{- 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 }} + resources: +{{ toYaml .Values.server.resources | indent 12 }} + 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 }} + {{- 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 }} + {{- if .Values.server.securityContext }} + securityContext: +{{ toYaml .Values.server.securityContext | indent 8 }} + {{- end }} + {{- if .Values.server.tolerations }} + tolerations: +{{ toYaml .Values.server.tolerations | indent 8 }} + {{- end }} + {{- if .Values.server.affinity }} + affinity: +{{ toYaml .Values.server.affinity | indent 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: + - metadata: + name: storage-volume + {{- if .Values.server.persistentVolume.annotations }} + annotations: +{{ toYaml .Values.server.persistentVolume.annotations | 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 }} +{{- end }} diff --git a/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/templates/server/vpa.yaml b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/templates/server/vpa.yaml new file mode 100644 index 000000000..981a9b485 --- /dev/null +++ b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/templates/server/vpa.yaml @@ -0,0 +1,24 @@ +{{- if .Values.server.enabled -}} +{{- if .Values.server.verticalAutoscaler.enabled -}} +apiVersion: autoscaling.k8s.io/v1beta2 +kind: VerticalPodAutoscaler +metadata: + labels: + {{- include "prometheus.server.labels" . | nindent 4 }} + name: {{ template "prometheus.server.fullname" . }}-vpa +{{ include "prometheus.namespace" . | indent 2 }} +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 -}} {{/* if .Values.server.verticalAutoscaler.enabled */}} +{{- end -}} {{/* .Values.server.enabled */}} diff --git a/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/values.yaml b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/values.yaml new file mode 100644 index 000000000..680def88e --- /dev/null +++ b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/charts/prometheus/values.yaml @@ -0,0 +1,1861 @@ +rbac: + create: true + +podSecurityPolicy: + enabled: false + +imagePullSecrets: +# - name: "image-pull-secret" + +## Define serviceAccount names for components. Defaults to component's fully qualified name. +## +serviceAccounts: + alertmanager: + create: false + name: + annotations: {} + nodeExporter: + create: false + name: + annotations: {} + pushgateway: + create: false + name: + annotations: {} + server: + create: true + name: + annotations: {} + +alertmanager: + ## If false, alertmanager will not be installed + ## + enabled: true + + ## Use a ClusterRole (and ClusterRoleBinding) + ## - If set to false - we define a Role and RoleBinding in the defined namespaces ONLY + ## This makes alertmanager work - for users who do not have ClusterAdmin privs, but wants alertmanager to operate on their own namespaces, instead of clusterwide. + useClusterRole: true + + ## Set to a rolename to use existing role - skipping role creating - but still doing serviceaccount and rolebinding to the rolename set here. + useExistingRole: false + + ## alertmanager container name + ## + name: alertmanager + + ## alertmanager container image + ## + image: + registry: quay.io + repository: prometheus/alertmanager + tag: v0.23.0 + pullPolicy: IfNotPresent + + ## alertmanager priorityClassName + ## + priorityClassName: "" + + ## Custom HTTP headers for Readiness Probe + ## + ## Useful for providing HTTP Basic Auth to healthchecks + probeHeaders: [] + + ## Additional alertmanager container arguments + ## + extraArgs: {} + + ## Additional InitContainers to initialize the pod + ## + extraInitContainers: [] + + ## 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 alertmanager + baseURL: "http://localhost:9093" + + ## Additional alertmanager container environment variable + ## For instance to add a http_proxy + ## + extraEnv: {} + + ## 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 + + ## Additional alertmanager Configmap mounts + extraConfigmapMounts: [] + # - name: template-files + # mountPath: /etc/config/templates.d + # configMap: alertmanager-template-files + # readOnly: true + + ## ConfigMap override where fullname is {{.Release.Name}}-{{.Values.alertmanager.configMapOverrideName}} + ## Defining configMapOverrideName will cause templates/alertmanager-configmap.yaml + ## to NOT generate a ConfigMap resource + ## + configMapOverrideName: "" + + ## The name of a secret in the same kubernetes namespace which contains the Alertmanager config + ## Defining configFromSecret will cause templates/alertmanager-configmap.yaml + ## to NOT generate a ConfigMap resource + ## + configFromSecret: "" + + ## The configuration file name to be loaded to alertmanager + ## Must match the key within configuration loaded from ConfigMap/Secret + ## + configFileName: alertmanager.yml + + ingress: + ## If true, alertmanager 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 + + ## alertmanager Ingress annotations + ## + annotations: {} + # kubernetes.io/ingress.class: nginx + # kubernetes.io/tls-acme: 'true' + + ## alertmanager Ingress additional labels + ## + extraLabels: {} + + ## alertmanager Ingress hostnames with optional path + ## Must be provided if Ingress is enabled + ## + hosts: [] + # - alertmanager.domain.com + # - domain.com/alertmanager + + 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. + extraPaths: [] + # - path: /* + # backend: + # serviceName: ssl-redirect + # servicePort: use-annotation + + ## alertmanager Ingress TLS configuration + ## Secrets must be manually created in the namespace + ## + tls: [] + # - secretName: prometheus-alerts-tls + # hosts: + # - alertmanager.domain.com + + ## Alertmanager Deployment Strategy type + # strategy: + # type: Recreate + + ## Node tolerations for alertmanager scheduling to nodes with taints + ## Ref: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/ + ## + tolerations: [] + # - key: "key" + # operator: "Equal|Exists" + # value: "value" + # effect: "NoSchedule|PreferNoSchedule|NoExecute(1.6 only)" + + ## Node labels for alertmanager pod assignment + ## Ref: https://kubernetes.io/docs/user-guide/node-selection/ + ## + nodeSelector: {} + + ## Pod affinity + ## + affinity: {} + + ## PodDisruptionBudget settings + ## ref: https://kubernetes.io/docs/concepts/workloads/pods/disruptions/ + ## + podDisruptionBudget: + enabled: false + maxUnavailable: 1 + + ## Use an alternate scheduler, e.g. "stork". + ## ref: https://kubernetes.io/docs/tasks/administer-cluster/configure-multiple-schedulers/ + ## + # schedulerName: + + persistentVolume: + ## If true, alertmanager will create/use a Persistent Volume Claim + ## If false, use emptyDir + ## + enabled: true + + ## alertmanager 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 + + ## alertmanager data Persistent Volume Claim annotations + ## + annotations: {} + + ## alertmanager data Persistent Volume existing claim name + ## Requires alertmanager.persistentVolume.enabled: true + ## If defined, PVC must be created manually before volume will be bound + existingClaim: "" + + ## alertmanager data Persistent Volume mount root path + ## + mountPath: /data + + ## alertmanager data Persistent Volume size + ## + size: 2Gi + + ## alertmanager 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: "-" + + ## alertmanager 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 alertmanager 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 ] } + + emptyDir: + ## alertmanager emptyDir volume size limit + ## + sizeLimit: "" + + ## Annotations to be added to alertmanager pods + ## + podAnnotations: {} + ## Tell prometheus to use a specific set of alertmanager pods + ## instead of all alertmanager pods found in the same namespace + ## Useful if you deploy multiple releases within the same namespace + ## + ## prometheus.io/probe: alertmanager-teamA + + ## Labels to be added to Prometheus AlertManager pods + ## + podLabels: {} + + ## 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 + + ## 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: {} + + ## Enabling peer mesh service end points for enabling the HA alert manager + ## Ref: https://github.com/prometheus/alertmanager/blob/master/README.md + enableMeshPeer: false + + servicePort: 80 + + ## alertmanager resource requests and limits + ## Ref: http://kubernetes.io/docs/user-guide/compute-resources/ + ## + resources: + limits: + cpu: 100m + memory: 100Mi + requests: + cpu: 50m + memory: 50Mi + + # Custom DNS configuration to be added to alertmanager 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 alertmanager pods + ## + securityContext: + runAsUser: 65534 + runAsNonRoot: true + runAsGroup: 65534 + fsGroup: 65534 + + service: + annotations: {} + labels: {} + clusterIP: "" + + ## Enabling peer mesh service end points for enabling the HA alert manager + ## Ref: https://github.com/prometheus/alertmanager/blob/master/README.md + # enableMeshPeer : true + + ## List of IP addresses at which the alertmanager service is available + ## Ref: https://kubernetes.io/docs/user-guide/services/#external-ips + ## + externalIPs: [] + + loadBalancerIP: "" + loadBalancerSourceRanges: [] + servicePort: 80 + # nodePort: 30000 + sessionAffinity: None + type: ClusterIP + + ## List of initial peers + ## Ref: https://github.com/prometheus/alertmanager/blob/main/README.md#high-availability + clusterPeers: [] + +## Monitors ConfigMap changes and POSTs to a URL +## Ref: https://github.com/jimmidyson/configmap-reload +## +configmapReload: + 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: + registry: docker.io + repository: jimmidyson/configmap-reload + tag: v0.5.0 + pullPolicy: IfNotPresent + + # containerPort: 9533 + + ## Additional configmap-reload container arguments + ## + extraArgs: {} + ## Additional configmap-reload volume directories + ## + extraVolumeDirs: [] + + + ## Additional configmap-reload mounts + ## + extraConfigmapMounts: [] + # - name: prometheus-alerts + # mountPath: /etc/alerts.d + # subPath: "" + # configMap: prometheus-alerts + # readOnly: true + + + ## configmap-reload resource requests and limits + ## Ref: http://kubernetes.io/docs/user-guide/compute-resources/ + ## + resources: + limits: + cpu: 100m + memory: 100Mi + requests: + cpu: 50m + memory: 50Mi + alertmanager: + ## If false, the configmap-reload container will not be deployed + ## + enabled: true + + ## configmap-reload container name + ## + name: configmap-reload + + ## configmap-reload container image + ## + image: + registry: docker.io + repository: jimmidyson/configmap-reload + tag: v0.5.0 + pullPolicy: IfNotPresent + + # containerPort: 9533 + + ## Additional configmap-reload container arguments + ## + extraArgs: {} + ## Additional configmap-reload volume directories + ## + extraVolumeDirs: [] + + + ## Additional configmap-reload mounts + ## + extraConfigmapMounts: [] + # - name: prometheus-alerts + # mountPath: /etc/alerts.d + # subPath: "" + # configMap: prometheus-alerts + # readOnly: true + + + ## configmap-reload resource requests and limits + ## Ref: http://kubernetes.io/docs/user-guide/compute-resources/ + ## + resources: + limits: + cpu: 100m + memory: 100Mi + requests: + cpu: 50m + memory: 50Mi + +kubeStateMetrics: + ## If false, kube-state-metrics sub-chart will not be installed + ## + enabled: true + +## 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: + +nodeExporter: + ## If false, node-exporter will not be installed + ## + enabled: true + + ## If true, node-exporter pods share the host network namespace + ## + hostNetwork: true + + ## If true, node-exporter pods share the host PID namespace + ## + hostPID: true + + ## If true, node-exporter pods mounts host / at /host/root + ## + hostRootfs: true + + ## node-exporter container name + ## + name: node-exporter + + ## node-exporter container image + ## + image: + registry: quay.io + repository: prometheus/node-exporter + tag: v1.3.0 + pullPolicy: IfNotPresent + + ## 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' + + ## node-exporter priorityClassName + ## + priorityClassName: "" + + ## Custom Update Strategy + ## + updateStrategy: + type: RollingUpdate + + ## Additional node-exporter container arguments + ## + extraArgs: {} + + ## Additional InitContainers to initialize the pod + ## + extraInitContainers: [] + + ## Additional node-exporter hostPath mounts + ## + extraHostPathMounts: [] + # - name: textfile-dir + # mountPath: /srv/txt_collector + # hostPath: /var/lib/node-exporter + # readOnly: true + # mountPropagation: HostToContainer + + extraConfigmapMounts: [] + # - name: certs-configmap + # mountPath: /prometheus + # configMap: certs-configmap + # readOnly: true + + ## Node tolerations for node-exporter scheduling to nodes with taints + ## Ref: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/ + ## + tolerations: [] + # - key: "key" + # operator: "Equal|Exists" + # value: "value" + # effect: "NoSchedule|PreferNoSchedule|NoExecute(1.6 only)" + + ## Node labels for node-exporter pod assignment + ## Ref: https://kubernetes.io/docs/user-guide/node-selection/ + ## + nodeSelector: {} + + ## Annotations to be added to node-exporter pods + ## + podAnnotations: {} + + ## Labels to be added to node-exporter pods + ## + pod: + labels: {} + + ## PodDisruptionBudget settings + ## ref: https://kubernetes.io/docs/concepts/workloads/pods/disruptions/ + ## + podDisruptionBudget: + enabled: false + maxUnavailable: 1 + + ## node-exporter resource limits & requests + ## Ref: https://kubernetes.io/docs/user-guide/compute-resources/ + ## + resources: + limits: + cpu: 500m + memory: 100Mi + requests: + cpu: 200m + memory: 50Mi + container: + securityContext: + allowPrivilegeEscalation: false + # Custom DNS configuration to be added to 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 + + ## Security context to be added to node-exporter pods + ## + securityContext: + fsGroup: 65534 + runAsGroup: 65534 + runAsNonRoot: true + runAsUser: 65534 + + service: + annotations: + prometheus.io/scrape: "true" + labels: {} + + # Exposed as a headless service: + # https://kubernetes.io/docs/concepts/services-networking/service/#headless-services + clusterIP: "" + + ## List of IP addresses at which the node-exporter service is available + ## Ref: https://kubernetes.io/docs/user-guide/services/#external-ips + ## + externalIPs: [] + + hostPort: 9100 + loadBalancerIP: "" + loadBalancerSourceRanges: [] + servicePort: 9100 + type: ClusterIP + +server: + ## Prometheus server container name + ## + enabled: true + + ## 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 + + ## namespaces to monitor (instead of monitoring all - clusterwide). Needed if you want to run without Cluster-admin privileges. + # namespaces: + # - yournamespace + + name: server + + # sidecarContainers - add more containers to prometheus server + # Key/Value where Key is the sidecar `- name: ` + # Example: + # sidecarContainers: + # webserver: + # image: nginx + 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: + registry: quay.io + repository: prometheus/prometheus + tag: v2.34.0 + pullPolicy: IfNotPresent + + ## 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 + 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: [] + + ## Custom HTTP headers for Liveness/Readiness/Startup Probe + ## + ## Useful for providing HTTP Basic Auth to healthchecks + probeHeaders: [] + + ## Additional Prometheus server container arguments + ## + extraArgs: {} + + ## 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: "" + + 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: {} + + ## Prometheus server Ingress hostnames with optional path + ## 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. + extraPaths: [] + # - path: /* + # backend: + # serviceName: ssl-redirect + # servicePort: use-annotation + + ## Prometheus server Ingress TLS configuration + ## Secrets must be manually created in the namespace + ## + tls: [] + # - secretName: prometheus-server-tls + # hosts: + # - prometheus.domain.com + + ## Server Deployment Strategy type + strategy: + type: RollingUpdate + + ## 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/configuration/assign-pod-node/ + ## + 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/user-guide/node-selection/ + ## + nodeSelector: {} + + ## Pod affinity + ## + affinity: {} + + ## PodDisruptionBudget settings + ## ref: https://kubernetes.io/docs/concepts/workloads/pods/disruptions/ + ## + podDisruptionBudget: + enabled: false + maxUnavailable: 1 + + ## 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 + + ## 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 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 ] } + + 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 + + ## 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 + + ## 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: + limits: + cpu: 1000m + 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, you probably want to set this to ClusterFirstWithHostNet + dnsPolicy: ClusterFirst + + # Use hostPort + # hostPort: 9090 + + ## 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 + + 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/user-guide/services/#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 + + ## Prometheus server pod termination grace period + ## + terminationGracePeriodSeconds: 300 + + ## Prometheus data retention period (default if not specified is 15 days) + ## + retention: "15d" + + ## Array of extra Kubernetes manifests, if you want to deploy + extraObjects: [] + +pushgateway: + ## If false, pushgateway will not be installed + ## + enabled: true + + ## Use an alternate scheduler, e.g. "stork". + ## ref: https://kubernetes.io/docs/tasks/administer-cluster/configure-multiple-schedulers/ + ## + # schedulerName: + + ## pushgateway container name + ## + name: pushgateway + + ## pushgateway container image + ## + image: + registry: docker.io + repository: prom/pushgateway + tag: v1.4.2 + pullPolicy: IfNotPresent + + ## pushgateway priorityClassName + ## + priorityClassName: "" + + ## Additional pushgateway container arguments + ## + ## for example: persistence.file: /data/pushgateway.data + extraArgs: {} + + ## Additional InitContainers to initialize the pod + ## + extraInitContainers: [] + + ingress: + ## If true, pushgateway 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 + + ## pushgateway Ingress annotations + ## + annotations: {} + # kubernetes.io/ingress.class: nginx + # kubernetes.io/tls-acme: 'true' + + ## pushgateway Ingress hostnames with optional path + ## Must be provided if Ingress is enabled + ## + hosts: [] + # - pushgateway.domain.com + # - domain.com/pushgateway + + 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. + extraPaths: [] + # - path: /* + # backend: + # serviceName: ssl-redirect + # servicePort: use-annotation + + ## pushgateway Ingress TLS configuration + ## Secrets must be manually created in the namespace + ## + tls: [] + # - secretName: prometheus-alerts-tls + # hosts: + # - pushgateway.domain.com + + ## Node tolerations for pushgateway scheduling to nodes with taints + ## Ref: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/ + ## + tolerations: [] + # - key: "key" + # operator: "Equal|Exists" + # value: "value" + # effect: "NoSchedule|PreferNoSchedule|NoExecute(1.6 only)" + + ## Node labels for pushgateway pod assignment + ## Ref: https://kubernetes.io/docs/user-guide/node-selection/ + ## + nodeSelector: {} + + ## Annotations to be added to pushgateway pods + ## + podAnnotations: {} + + ## Labels to be added to pushgateway pods + ## + podLabels: {} + + ## 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' + + replicaCount: 1 + + ## Annotations to be added to deployment + ## + deploymentAnnotations: {} + + ## PodDisruptionBudget settings + ## ref: https://kubernetes.io/docs/concepts/workloads/pods/disruptions/ + ## + podDisruptionBudget: + enabled: false + maxUnavailable: 1 + + ## pushgateway resource requests and limits + ## Ref: http://kubernetes.io/docs/user-guide/compute-resources/ + ## + resources: + limits: + cpu: 100m + memory: 100Mi + requests: + cpu: 50m + memory: 50Mi + + ## 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 + enabled: false + # updateMode: "Auto" + # containerPolicies: + # - containerName: 'prometheus-pushgateway' + + # Custom DNS configuration to be added to push-gateway 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 push-gateway pods + ## + securityContext: + runAsUser: 65534 + runAsNonRoot: true + + service: + annotations: + prometheus.io/probe: pushgateway + labels: {} + clusterIP: "" + + ## List of IP addresses at which the pushgateway service is available + ## Ref: https://kubernetes.io/docs/user-guide/services/#external-ips + ## + externalIPs: [] + + loadBalancerIP: "" + loadBalancerSourceRanges: [] + servicePort: 9091 + type: ClusterIP + + ## pushgateway Deployment Strategy type + # strategy: + # type: Recreate + + persistentVolume: + ## If true, pushgateway will create/use a Persistent Volume Claim + ## + 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: "-" + + ## pushgateway 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 pushgateway data Persistent Volume to mount + ## Useful if the volume's root directory is not empty + ## + subPath: "" + + +## alertmanager ConfigMap entries +## +alertmanagerFiles: + alertmanager.yml: + global: {} + # slack_api_url: '' + + receivers: + - name: default-receiver + # slack_configs: + # - channel: '@you' + # send_resolved: true + + route: + group_wait: 10s + group_interval: 5m + receiver: default-receiver + repeat_interval: 3h + +## Prometheus server ConfigMap entries for rule files (allow prometheus labels interpolation) +ruleFiles: {} + +## 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 + + # 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: [__address__, __meta_kubernetes_pod_annotation_prometheus_io_port] + action: replace + regex: ([^:]+)(?::\d+)?;(\d+) + replacement: $1:$2 + 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 + + # 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: [__address__, __meta_kubernetes_pod_annotation_prometheus_io_port] + action: replace + regex: ([^:]+)(?::\d+)?;(\d+) + replacement: $1:$2 + 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 + +# 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. + ## + enabled: false + +# Force namespace of namespaced resources +forceNamespace: null diff --git a/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/templates/_helpers.tpl b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/templates/_helpers.tpl new file mode 100644 index 000000000..4dae8715b --- /dev/null +++ b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/templates/_helpers.tpl @@ -0,0 +1,49 @@ +{{/* vim: set filetype=mustache: */}} +{{/* +Expand the name of the chart. +*/}} +{{- define "monitoring.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 "monitoring.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 "monitoring.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}} +{{- end -}} + +{{/* +Override the naming defined by the prometheus chart. +Added as a fix for https://github.com/grafana/loki/issues/1169 +*/}} +{{- define "prometheus.fullname" -}} +{{- if .Values.prometheus.server.fullnameOverride -}} +{{- .Values.prometheus.server.fullnameOverride | trunc 63 -}} +{{- 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/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/templates/datasources.yaml b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/templates/datasources.yaml new file mode 100644 index 000000000..3300d7aec --- /dev/null +++ b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/monitoring/templates/datasources.yaml @@ -0,0 +1,25 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ template "monitoring.fullname" . }} + namespace: {{ .Release.Namespace }} + labels: + app: {{ template "monitoring.name" . }} + chart: {{ template "monitoring.chart" . }} + release: {{ .Release.Name }} + heritage: {{ .Release.Service }} + app.kubernetes.io/instance: {{ template "monitoring.name" . }} + {{- include "k8s-triliovault-operator.observability" . | nindent 4 }} + grafana_datasource: "1" +data: + monitoring-datasource.yaml: |- + apiVersion: 1 + datasources: +{{- if .Values.prometheus.enabled }} + - name: Prometheus + type: prometheus + access: proxy + isDefault: true + url: http://{{ include "prometheus.fullname" .}}:{{ .Values.prometheus.server.service.servicePort }}{{ .Values.prometheus.server.prefixURL }} + version: 1 +{{- end }} diff --git a/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/visualization/Chart.yaml b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/visualization/Chart.yaml new file mode 100644 index 000000000..12fa0fc95 --- /dev/null +++ b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/visualization/Chart.yaml @@ -0,0 +1,16 @@ +apiVersion: v2 +appVersion: 0.1.0 +dependencies: +- condition: grafana.enabled + name: grafana + repository: https://grafana.github.io/helm-charts + version: ^6.29.2 +description: Visualization Stack designed to manage the K8s-TrilioVault Application's + Visualization. +icon: https://www.trilio.io/wp-content/uploads/2021/01/Trilio-2020-logo-RGB-gray-green.png +kubeVersion: '>=1.19.0-0' +maintainers: +- email: support@trilio.io + name: Trilio +name: visualization +version: 0.1.0 diff --git a/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/visualization/charts/grafana/Chart.yaml b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/visualization/charts/grafana/Chart.yaml new file mode 100644 index 000000000..5a3ffe454 --- /dev/null +++ b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/visualization/charts/grafana/Chart.yaml @@ -0,0 +1,14 @@ +apiVersion: v2 +appVersion: 8.5.0 +description: The leading tool for querying and visualizing time series and metrics. +home: https://grafana.net +icon: https://raw.githubusercontent.com/grafana/grafana/master/public/img/logo_transparent_400x.png +kubeVersion: ^1.8.0-0 +maintainers: +- email: support@trilio.io + name: Trilio +name: grafana +sources: +- https://github.com/grafana/grafana +type: application +version: 6.29.2 diff --git a/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/visualization/charts/grafana/dashboards/backup-detail.json b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/visualization/charts/grafana/dashboards/backup-detail.json new file mode 100644 index 000000000..cee756e93 --- /dev/null +++ b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/visualization/charts/grafana/dashboards/backup-detail.json @@ -0,0 +1,926 @@ +{ + "__inputs": [ + { + "name": "DS_PROMETHEUS", + "label": "Prometheus", + "description": "", + "type": "datasource", + "pluginId": "prometheus", + "pluginName": "Prometheus" + }, + { + "name": "DS_LOKI", + "label": "Loki", + "description": "", + "type": "datasource", + "pluginId": "loki", + "pluginName": "Loki" + } + ], + "__elements": [], + "__requires": [ + { + "type": "panel", + "id": "gauge", + "name": "Gauge", + "version": "" + }, + { + "type": "grafana", + "id": "grafana", + "name": "Grafana", + "version": "8.5.0" + }, + { + "type": "panel", + "id": "logs", + "name": "Logs", + "version": "" + }, + { + "type": "datasource", + "id": "loki", + "name": "Loki", + "version": "1.0.0" + }, + { + "type": "datasource", + "id": "prometheus", + "name": "Prometheus", + "version": "1.0.0" + }, + { + "type": "panel", + "id": "stat", + "name": "Stat", + "version": "" + }, + { + "type": "panel", + "id": "table", + "name": "Table", + "version": "" + }, + { + "type": "panel", + "id": "table-old", + "name": "Table (old)", + "version": "" + }, + { + "type": "panel", + "id": "text", + "name": "Text", + "version": "" + } + ], + "annotations": { + "list": [ + { + "$$hashKey": "object:20", + "builtIn": 1, + "datasource": { + "type": "datasource", + "uid": "grafana" + }, + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "limit": 100, + "name": "Annotations & Alerts", + "showIn": 0, + "target": { + "limit": 100, + "matchAny": false, + "tags": [], + "type": "dashboard" + }, + "type": "dashboard" + } + ] + }, + "editable": true, + "fiscalYearStartMonth": 0, + "gnetId": 12601, + "graphTooltip": 0, + "id": null, + "iteration": 1655448146244, + "links": [], + "liveNow": false, + "panels": [ + { + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "gridPos": { + "h": 2, + "w": 24, + "x": 0, + "y": 0 + }, + "id": 31, + "options": { + "content": "

Backup Detail

", + "mode": "html" + }, + "pluginVersion": "8.5.0", + "transparent": true, + "type": "text" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "mappings": [], + "max": 100, + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "dark-red", + "value": null + }, + { + "color": "rgb(255, 255, 255)", + "value": 1 + }, + { + "color": "dark-green", + "value": 100 + } + ] + }, + "unit": "percent" + }, + "overrides": [] + }, + "gridPos": { + "h": 4, + "w": 7, + "x": 0, + "y": 2 + }, + "id": 45, + "options": { + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "last" + ], + "fields": "", + "values": false + }, + "showThresholdLabels": false, + "showThresholdMarkers": false + }, + "pluginVersion": "8.5.0", + "targets": [ + { + "datasource": "${DS_PROMETHEUS}", + "expr": "trilio_backup_status_percentage{backup=~\"$Backup\",install_namespace=~\"$Install_Namespace\",cluster=~\"$Cluster\",kind=\"Backup\"}", + "format": "time_series", + "instant": true, + "interval": "", + "intervalFactor": 1, + "legendFormat": "{{status}}", + "refId": "A" + } + ], + "transparent": true, + "type": "gauge" + }, + { + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "gridPos": { + "h": 1, + "w": 4, + "x": 8, + "y": 2 + }, + "id": 50, + "links": [], + "maxDataPoints": 100, + "options": { + "content": "", + "mode": "markdown" + }, + "pluginVersion": "8.5.0", + "targets": [ + { + "datasource": "${DS_PROMETHEUS}", + "expr": "trilio_backup_info{backup=~\"$Backup\",namespace=~\"$Namespace\",cluster=~\"$Cluster\"}", + "format": "time_series", + "instant": true, + "interval": "", + "intervalFactor": 1, + "legendFormat": "{{status}}", + "refId": "A" + } + ], + "transparent": true, + "type": "text" + }, + { + "columns": [], + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "description": "", + "fontSize": "100%", + "gridPos": { + "h": 14, + "w": 12, + "x": 12, + "y": 2 + }, + "id": 42, + "links": [], + "showHeader": true, + "sort": { + "col": 18, + "desc": true + }, + "styles": [ + { + "$$hashKey": "object:10447", + "alias": "Object Type", + "align": "auto", + "dateFormat": "YYYY-MM-DD HH:mm:ss", + "link": true, + "linkTooltip": "Show Metadata Details", + "linkUrl": "/d/Metadata/metadata-detail?var-Backup=${Backup}&var-ObjectType=${__cell}&var-Cluster=${Cluster}&var-Install_Namespace=${Install_Namespace}", + "mappingType": 1, + "pattern": "objecttype", + "type": "string" + }, + { + "$$hashKey": "object:1072", + "alias": "Source", + "align": "auto", + "colors": [ + "rgba(245, 54, 54, 0.9)", + "rgba(237, 129, 40, 0.89)", + "rgba(50, 172, 45, 0.97)" + ], + "dateFormat": "YYYY-MM-DD HH:mm:ss", + "decimals": 2, + "mappingType": 1, + "pattern": "applicationtype", + "thresholds": [], + "type": "string", + "unit": "short" + }, + { + "$$hashKey": "object:1249", + "alias": "Count", + "align": "auto", + "colors": [ + "rgba(245, 54, 54, 0.9)", + "rgba(237, 129, 40, 0.89)", + "rgba(50, 172, 45, 0.97)" + ], + "dateFormat": "YYYY-MM-DD HH:mm:ss", + "mappingType": 1, + "pattern": "Value", + "thresholds": [], + "type": "number", + "unit": "short" + }, + { + "$$hashKey": "object:10448", + "alias": "", + "align": "right", + "colors": [ + "rgba(245, 54, 54, 0.9)", + "rgba(237, 129, 40, 0.89)", + "rgba(50, 172, 45, 0.97)" + ], + "decimals": 2, + "pattern": "/.*/", + "thresholds": [], + "type": "hidden", + "unit": "short" + } + ], + "targets": [ + { + "datasource": "${DS_PROMETHEUS}", + "expr": "avg(trilio_backup_metadata_info{backup=~\"$Backup\",install_namespace=~\"$Install_Namespace\",cluster=~\"$Cluster\",kind=\"Backup\"}) by (objecttype, applicationtype)", + "format": "table", + "instant": true, + "interval": "", + "legendFormat": "", + "refId": "A" + } + ], + "title": "Metadata Info", + "transform": "table", + "type": "table-old" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "mappings": [ + { + "options": { + "0": { + "text": "InProgress" + }, + "1": { + "text": "Available" + }, + "-1": { + "text": "Failed" + }, + "-2": { + "text": "UnKnown" + } + }, + "type": "value" + } + ], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "red", + "value": null + }, + { + "color": "dark-red", + "value": -1 + }, + { + "color": "blue", + "value": 0 + }, + { + "color": "green", + "value": 1 + } + ] + }, + "unit": "short" + }, + "overrides": [] + }, + "gridPos": { + "h": 2, + "w": 5, + "x": 7, + "y": 3 + }, + "id": 46, + "links": [], + "maxDataPoints": 100, + "options": { + "colorMode": "value", + "fieldOptions": { + "calcs": [ + "mean" + ] + }, + "graphMode": "none", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "first" + ], + "fields": "", + "values": false + }, + "textMode": "value" + }, + "pluginVersion": "8.5.0", + "targets": [ + { + "datasource": "${DS_PROMETHEUS}", + "expr": "trilio_backup_info{backup=~\"$Backup\",install_namespace=~\"$Install_Namespace\",cluster=~\"$Cluster\",kind=\"Backup\"}", + "format": "time_series", + "instant": true, + "interval": "", + "intervalFactor": 1, + "legendFormat": "{{status}}", + "refId": "A" + } + ], + "transparent": true, + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [ + { + "options": { + "match": "null", + "result": { + "text": "N/A" + } + }, + "type": "special" + } + ], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "none" + }, + "overrides": [] + }, + "gridPos": { + "h": 3, + "w": 6, + "x": 0, + "y": 6 + }, + "id": 47, + "links": [], + "maxDataPoints": 100, + "options": { + "colorMode": "none", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "horizontal", + "reduceOptions": { + "calcs": [ + "first" + ], + "fields": "/^backup$/", + "values": false + }, + "text": { + "valueSize": 30 + }, + "textMode": "auto" + }, + "pluginVersion": "8.5.0", + "targets": [ + { + "datasource": "${DS_PROMETHEUS}", + "expr": "trilio_backup_info{ backup=~\"$Backup\",install_namespace=~\"$Install_Namespace\",cluster=~\"$Cluster\",kind=\"Backup\"}", + "format": "table", + "instant": true, + "interval": "", + "legendFormat": "", + "refId": "A" + } + ], + "title": "Name", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [ + { + "options": { + "match": "null", + "result": { + "text": "N/A" + } + }, + "type": "special" + } + ], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "none" + }, + "overrides": [] + }, + "gridPos": { + "h": 3, + "w": 6, + "x": 6, + "y": 6 + }, + "id": 36, + "links": [], + "maxDataPoints": 100, + "options": { + "colorMode": "none", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "horizontal", + "reduceOptions": { + "calcs": [ + "first" + ], + "fields": "/^backupplan$/", + "values": false + }, + "text": { + "valueSize": 30 + }, + "textMode": "auto" + }, + "pluginVersion": "8.5.0", + "targets": [ + { + "datasource": "${DS_PROMETHEUS}", + "expr": "trilio_backup_info{ backup=~\"$Backup\",install_namespace=~\"$Install_Namespace\",cluster=~\"$Cluster\",kind=\"Backup\"}", + "format": "table", + "instant": true, + "interval": "", + "legendFormat": "", + "refId": "A" + } + ], + "title": "Backup Plan", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "custom": { + "align": "left", + "displayMode": "auto", + "filterable": false, + "inspect": false + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + }, + "unit": "short" + }, + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "First" + }, + "properties": [ + { + "id": "displayName", + "value": "Value" + } + ] + } + ] + }, + "gridPos": { + "h": 7, + "w": 12, + "x": 0, + "y": 9 + }, + "id": 49, + "options": { + "footer": { + "fields": "", + "reducer": [ + "sum" + ], + "show": false + }, + "showHeader": true + }, + "pluginVersion": "8.5.0", + "targets": [ + { + "datasource": "${DS_PROMETHEUS}", + "expr": "trilio_backup_info{ backup=~\"$Backup\",install_namespace=~\"$Install_Namespace\",cluster=~\"$Cluster\",kind=\"Backup\"}", + "format": "table", + "instant": true, + "interval": "", + "legendFormat": "", + "refId": "A" + } + ], + "title": "Details", + "transformations": [ + { + "id": "filterFieldsByName", + "options": { + "include": { + "names": [ + "Time", + "applicationtype", + "backup_type", + "completion_ts", + "hook", + "size", + "start_ts", + "target", + "resource_namespace" + ] + } + } + }, + { + "id": "reduce", + "options": { + "reducers": [ + "first" + ] + } + } + ], + "type": "table" + }, + { + "datasource": { + "type": "loki", + "uid": "${DS_LOKI}" + }, + "description": "Backup Logs", + "gridPos": { + "h": 11, + "w": 24, + "x": 0, + "y": 16 + }, + "id": 52, + "options": { + "dedupStrategy": "none", + "enableLogDetails": true, + "prettifyLogMessage": false, + "showCommonLabels": false, + "showLabels": false, + "showTime": true, + "sortOrder": "Descending", + "wrapLogMessage": false + }, + "targets": [ + { + "datasource": { + "type": "loki", + "uid": "${DS_LOKI}" + }, + "expr": "{transaction_type=\"Backup\",transaction_resource_name=~\"$Backup\",service_type=~\"$service_type\",transaction_resource_namespace=~\"$Namespace\"}", + "refId": "A" + } + ], + "title": "Backup Logs", + "type": "logs" + } + ], + "refresh": "30s", + "schemaVersion": 36, + "style": "dark", + "tags": [ + "logging" + ], + "templating": { + "list": [ + { + "current": { + "selected": false, + "text": "Prometheus", + "value": "Prometheus" + }, + "hide": 2, + "includeAll": false, + "label": "datasource", + "multi": false, + "name": "DS_PROMETHEUS", + "options": [], + "query": "prometheus", + "refresh": 1, + "regex": "", + "skipUrlSync": false, + "type": "datasource" + }, + { + "current": { + "selected": false, + "text": "Loki", + "value": "Loki" + }, + "description": "loki datasource", + "hide": 2, + "includeAll": false, + "label": "datasource", + "multi": false, + "name": "DS_LOKI", + "options": [], + "query": "loki", + "refresh": 1, + "regex": "", + "skipUrlSync": false, + "type": "datasource" + }, + { + "current": {}, + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "definition": "trilio_system_info", + "hide": 2, + "includeAll": false, + "multi": false, + "name": "Cluster", + "options": [], + "query": { + "query": "trilio_system_info", + "refId": "Prometheus-Cluster-Variable-Query" + }, + "refresh": 1, + "regex": "/.*cluster=\"([^\"]*).*/", + "skipUrlSync": false, + "sort": 0, + "tagValuesQuery": "", + "tagsQuery": "", + "type": "query", + "useTags": false + }, + { + "current": {}, + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "definition": "trilio_system_info{cluster=~\"$Cluster\"}", + "hide": 2, + "includeAll": false, + "multi": false, + "name": "Scope", + "options": [], + "query": { + "query": "trilio_system_info{cluster=~\"$Cluster\"}", + "refId": "Prometheus-Scope-Variable-Query" + }, + "refresh": 1, + "regex": "/.*scope=\"([^\"]*).*/", + "skipUrlSync": false, + "sort": 0, + "tagValuesQuery": "", + "tagsQuery": "", + "type": "query", + "useTags": false + }, + { + "current": {}, + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "definition": "trilio_system_info{scope=~\"$Scope\",cluster=~\"$Cluster\"}", + "hide": 0, + "includeAll": false, + "label": "Install Namespace", + "multi": false, + "name": "Install_Namespace", + "options": [], + "query": { + "query": "trilio_system_info{scope=~\"$Scope\",cluster=~\"$Cluster\"}", + "refId": "Prometheus-Install_Namespace-Variable-Query" + }, + "refresh": 2, + "regex": "/.*install_namespace=\"([^\"]*).*/", + "skipUrlSync": false, + "sort": 0, + "tagValuesQuery": "", + "tagsQuery": "", + "type": "query", + "useTags": false + }, + { + "current": {}, + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "definition": "trilio_backup_info{cluster=~\"$Cluster\",install_namespace=~\"$Install_Namespace\",kind=\"Backup\"}", + "hide": 0, + "includeAll": false, + "label": "Backup", + "multi": false, + "name": "Backup", + "options": [], + "query": { + "query": "trilio_backup_info{cluster=~\"$Cluster\",install_namespace=~\"$Install_Namespace\",kind=\"Backup\"}", + "refId": "StandardVariableQuery" + }, + "refresh": 2, + "regex": "/.*backup=\"([^\"]*).*/", + "skipUrlSync": false, + "sort": 0, + "tagValuesQuery": "", + "tagsQuery": "", + "type": "query", + "useTags": false + }, + { + "current": {}, + "datasource": { + "type": "loki", + "uid": "${DS_LOKI}" + }, + "definition": "label_values({transaction_type=~\"Backup\",transaction_resource_name=~\"$Backup\"}, service_type)", + "description": "Service Type", + "hide": 0, + "includeAll": true, + "label": "Service Type", + "multi": false, + "name": "service_type", + "options": [], + "query": "label_values({transaction_type=~\"Backup\",transaction_resource_name=~\"$Backup\"}, service_type)", + "refresh": 2, + "regex": "", + "skipUrlSync": false, + "sort": 0, + "type": "query" + }, + { + "current": {}, + "datasource": { + "type": "loki", + "uid": "${DS_LOKI}" + }, + "definition": "label_values({transaction_type=~\"Backup\",transaction_resource_name=~\"$Backup\"},transaction_resource_namespace)", + "description": "Backup Namespace", + "hide": 0, + "includeAll": true, + "label": "Backup Namespace", + "multi": false, + "name": "Namespace", + "options": [], + "query": "label_values({transaction_type=~\"Backup\",transaction_resource_name=~\"$Backup\"},transaction_resource_namespace)", + "refresh": 2, + "regex": "", + "skipUrlSync": false, + "sort": 0, + "type": "query" + } + ] + }, + "time": { + "from": "now-15m", + "to": "now" + }, + "timepicker": { + "refresh_intervals": [ + "10s", + "30s", + "1m", + "5m", + "15m", + "30m", + "1h", + "2h", + "1d" + ] + }, + "timezone": "", + "title": "Backup Detail", + "uid": "Backup", + "version": 1, + "weekStart": "" +} diff --git a/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/visualization/charts/grafana/dashboards/backup-overview.json b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/visualization/charts/grafana/dashboards/backup-overview.json new file mode 100644 index 000000000..5d4c209c8 --- /dev/null +++ b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/visualization/charts/grafana/dashboards/backup-overview.json @@ -0,0 +1,883 @@ +{ + "__inputs": [ + { + "name": "DS_PROMETHEUS", + "label": "Prometheus", + "description": "", + "type": "datasource", + "pluginId": "prometheus", + "pluginName": "Prometheus" + } + ], + "__elements": [], + "__requires": [ + { + "type": "grafana", + "id": "grafana", + "name": "Grafana", + "version": "8.5.0" + }, + { + "type": "datasource", + "id": "prometheus", + "name": "Prometheus", + "version": "1.0.0" + }, + { + "type": "panel", + "id": "stat", + "name": "Stat", + "version": "" + }, + { + "type": "panel", + "id": "table", + "name": "Table", + "version": "" + }, + { + "type": "panel", + "id": "text", + "name": "Text", + "version": "" + } + ], + "annotations": { + "list": [ + { + "$$hashKey": "object:14091", + "builtIn": 1, + "datasource": { + "type": "datasource", + "uid": "grafana" + }, + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "limit": 100, + "name": "Annotations & Alerts", + "showIn": 0, + "target": { + "limit": 100, + "matchAny": false, + "tags": [], + "type": "dashboard" + }, + "type": "dashboard" + } + ] + }, + "editable": true, + "fiscalYearStartMonth": 0, + "gnetId": 12600, + "graphTooltip": 0, + "id": null, + "iteration": 1655400242671, + "links": [], + "liveNow": false, + "panels": [ + { + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "gridPos": { + "h": 2, + "w": 24, + "x": 0, + "y": 0 + }, + "id": 4, + "options": { + "content": "

Backups Overview

", + "mode": "html" + }, + "pluginVersion": "8.5.0", + "transparent": true, + "type": "text" + }, + { + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "links": [], + "mappings": [], + "min": 0, + "noValue": "0", + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "rgb(115, 181, 181)", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "short" + }, + "overrides": [] + }, + "gridPos": { + "h": 3, + "w": 4, + "x": 2, + "y": 2 + }, + "id": 31, + "options": { + "colorMode": "background", + "graphMode": "none", + "justifyMode": "center", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "last" + ], + "fields": "/^All$/", + "values": false + }, + "textMode": "auto" + }, + "pluginVersion": "8.5.0", + "targets": [ + { + "expr": "count(trilio_backup_info{install_namespace=~\"$Install_Namespace\",cluster=~\"$Cluster\"}) ", + "format": "time_series", + "instant": true, + "interval": "", + "legendFormat": "All", + "refId": "A", + "datasource": "${DS_PROMETHEUS}" + } + ], + "title": "All", + "transparent": true, + "type": "stat" + }, + { + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "links": [], + "mappings": [], + "min": 0, + "noValue": "0", + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "short" + }, + "overrides": [] + }, + "gridPos": { + "h": 3, + "w": 4, + "x": 6, + "y": 2 + }, + "id": 34, + "links": [], + "options": { + "colorMode": "background", + "graphMode": "none", + "justifyMode": "center", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "last" + ], + "fields": "/^Available$/", + "values": false + }, + "textMode": "auto" + }, + "pluginVersion": "8.5.0", + "targets": [ + { + "expr": "count(trilio_backup_info{status=\"Available\",install_namespace=~\"$Install_Namespace\",cluster=~\"$Cluster\"}) by (status)", + "format": "time_series", + "instant": true, + "interval": "", + "legendFormat": "{{status}}", + "refId": "A", + "datasource": "${DS_PROMETHEUS}" + } + ], + "title": "Available", + "transparent": true, + "type": "stat" + }, + { + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "links": [], + "mappings": [], + "min": 0, + "noValue": "0", + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "red", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "short" + }, + "overrides": [] + }, + "gridPos": { + "h": 3, + "w": 4, + "x": 10, + "y": 2 + }, + "id": 33, + "options": { + "colorMode": "background", + "graphMode": "none", + "justifyMode": "center", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "last" + ], + "fields": "/^Failed$/", + "values": false + }, + "textMode": "auto" + }, + "pluginVersion": "8.5.0", + "targets": [ + { + "expr": "count(trilio_backup_info{status=\"Failed\",install_namespace=~\"$Install_Namespace\",cluster=~\"$Cluster\"}) by (status)", + "format": "time_series", + "instant": true, + "interval": "", + "legendFormat": "{{status}}", + "refId": "A", + "datasource": "${DS_PROMETHEUS}" + } + ], + "title": "Failed", + "transparent": true, + "type": "stat" + }, + { + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "links": [], + "mappings": [], + "min": 0, + "noValue": "0", + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "blue", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "short" + }, + "overrides": [] + }, + "gridPos": { + "h": 3, + "w": 4, + "x": 14, + "y": 2 + }, + "id": 32, + "options": { + "colorMode": "background", + "graphMode": "none", + "justifyMode": "center", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "last" + ], + "fields": "/^InProgress$/", + "values": false + }, + "textMode": "auto" + }, + "pluginVersion": "8.5.0", + "targets": [ + { + "expr": "count(trilio_backup_info{status=\"InProgress\",install_namespace=~\"$Install_Namespace\",cluster=~\"$Cluster\"}) by (status)", + "format": "time_series", + "instant": true, + "interval": "", + "legendFormat": "{{status}}", + "refId": "A", + "datasource": "${DS_PROMETHEUS}" + } + ], + "title": "InProgress", + "transparent": true, + "type": "stat" + }, + { + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "links": [], + "mappings": [], + "min": 0, + "noValue": "0", + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "rgb(129, 135, 135)", + "value": null + } + ] + }, + "unit": "short" + }, + "overrides": [] + }, + "gridPos": { + "h": 3, + "w": 4, + "x": 18, + "y": 2 + }, + "id": 37, + "options": { + "colorMode": "background", + "graphMode": "none", + "justifyMode": "center", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "last" + ], + "fields": "/^UnKnown$/", + "values": false + }, + "textMode": "auto" + }, + "pluginVersion": "8.5.0", + "targets": [ + { + "expr": "count(trilio_backup_info{status=\"UnKnown\",install_namespace=~\"$Install_Namespace\",cluster=~\"$Cluster\"}) by (status)", + "format": "time_series", + "instant": true, + "interval": "", + "legendFormat": "{{status}}", + "refId": "A", + "datasource": "${DS_PROMETHEUS}" + } + ], + "title": "UnKnown", + "transparent": true, + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "custom": { + "align": "auto", + "displayMode": "auto", + "filterable": false, + "inspect": false + }, + "links": [], + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "backup" + }, + "properties": [ + { + "id": "displayName", + "value": "Backup" + }, + { + "id": "unit", + "value": "short" + }, + { + "id": "decimals", + "value": 2 + }, + { + "id": "links", + "value": [ + { + "targetBlank": false, + "title": "Backup Detail", + "url": "/d/${__data.fields.kind}/?refresh=5s&var-Backup=${__value.text}&var-Cluster=${Cluster}&var-Install_Namespace=${Install_Namespace}" + } + ] + }, + { + "id": "custom.align" + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "backupplan" + }, + "properties": [ + { + "id": "displayName", + "value": "Backup Plan" + }, + { + "id": "unit", + "value": "short" + }, + { + "id": "decimals", + "value": 2 + }, + { + "id": "custom.align" + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "completion_ts" + }, + "properties": [ + { + "id": "displayName", + "value": "Completion" + }, + { + "id": "unit", + "value": "short" + }, + { + "id": "decimals", + "value": 2 + }, + { + "id": "unit", + "value": "time: YYYY-MM-DD HH:mm:ss.SSS" + }, + { + "id": "custom.align" + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "target" + }, + "properties": [ + { + "id": "displayName", + "value": "Target" + }, + { + "id": "unit", + "value": "short" + }, + { + "id": "decimals", + "value": 2 + }, + { + "id": "links", + "value": [ + { + "targetBlank": false, + "title": "Target Detail", + "url": "/d/TargetDetail/target-detail?refresh=5s&var-Target=${__value.text}&var-Cluster=${Cluster}&var-Install_Namespace=${Install_Namespace}" + } + ] + }, + { + "id": "custom.align" + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "size" + }, + "properties": [ + { + "id": "displayName", + "value": "Size" + }, + { + "id": "unit", + "value": "decbytes" + }, + { + "id": "custom.align" + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Value" + }, + "properties": [ + { + "id": "displayName", + "value": "Percentage" + }, + { + "id": "unit", + "value": "percent" + }, + { + "id": "custom.align" + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "status" + }, + "properties": [ + { + "id": "displayName", + "value": "Status" + }, + { + "id": "unit", + "value": "short" + }, + { + "id": "decimals", + "value": 2 + }, + { + "id": "custom.align" + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "backup_type" + }, + "properties": [ + { + "id": "displayName", + "value": "Backup Type" + }, + { + "id": "unit", + "value": "short" + }, + { + "id": "decimals", + "value": 2 + }, + { + "id": "custom.align" + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "kind" + }, + "properties": [ + { + "id": "displayName", + "value": "Backup Kind" + }, + { + "id": "unit", + "value": "short" + }, + { + "id": "decimals", + "value": 2 + }, + { + "id": "custom.align" + } + ] + } + ] + }, + "gridPos": { + "h": 12, + "w": 24, + "x": 0, + "y": 5 + }, + "id": 29, + "options": { + "footer": { + "enablePagination": false, + "fields": "", + "reducer": [ + "sum" + ], + "show": false + }, + "showHeader": true + }, + "pluginVersion": "8.5.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "trilio_backup_status_percentage{status=~\"$Status\",install_namespace=~\"$Install_Namespace\",cluster=~\"$Cluster\"}", + "format": "table", + "instant": true, + "interval": "", + "intervalFactor": 1, + "legendFormat": "", + "refId": "A" + } + ], + "transformations": [ + { + "id": "merge", + "options": { + "reducers": [] + } + }, + { + "id": "filterFieldsByName", + "options": { + "include": { + "names": [ + "backup", + "backup_type", + "backupplan", + "completion_ts", + "size", + "status", + "target", + "Value", + "kind" + ] + } + } + } + ], + "type": "table" + } + ], + "refresh": "10s", + "schemaVersion": 36, + "style": "dark", + "tags": [], + "templating": { + "list": [ + { + "current": { + "selected": false, + "text": "Prometheus", + "value": "Prometheus" + }, + "hide": 2, + "includeAll": false, + "label": "datasource", + "multi": false, + "name": "DS_PROMETHEUS", + "options": [], + "query": "prometheus", + "refresh": 1, + "regex": "", + "skipUrlSync": false, + "type": "datasource" + }, + { + "current": {}, + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "definition": "trilio_system_info", + "hide": 2, + "includeAll": false, + "multi": false, + "name": "Cluster", + "options": [], + "query": { + "query": "trilio_system_info", + "refId": "Prometheus-Cluster-Variable-Query" + }, + "refresh": 1, + "regex": "/.*cluster=\"([^\"]*).*/", + "skipUrlSync": false, + "sort": 0, + "tagValuesQuery": "", + "tagsQuery": "", + "type": "query", + "useTags": false + }, + { + "current": {}, + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "definition": "trilio_system_info{cluster=~\"$Cluster\"}", + "hide": 2, + "includeAll": false, + "multi": false, + "name": "Scope", + "options": [], + "query": { + "query": "trilio_system_info{cluster=~\"$Cluster\"}", + "refId": "Prometheus-Scope-Variable-Query" + }, + "refresh": 1, + "regex": "/.*scope=\"([^\"]*).*/", + "skipUrlSync": false, + "sort": 0, + "tagValuesQuery": "", + "tagsQuery": "", + "type": "query", + "useTags": false + }, + { + "current": {}, + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "definition": "trilio_system_info{scope=~\"$Scope\",cluster=~\"$Cluster\"}", + "hide": 0, + "includeAll": false, + "label": "Install Namespace", + "multi": false, + "name": "Install_Namespace", + "options": [], + "query": { + "query": "trilio_system_info{scope=~\"$Scope\",cluster=~\"$Cluster\"}", + "refId": "Prometheus-Install_Namespace-Variable-Query" + }, + "refresh": 1, + "regex": "/.*install_namespace=\"([^\"]*).*/", + "skipUrlSync": false, + "sort": 0, + "tagValuesQuery": "", + "tagsQuery": "", + "type": "query", + "useTags": false + }, + { + "current": {}, + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "definition": "trilio_backup_info", + "hide": 0, + "includeAll": true, + "label": "Status", + "multi": false, + "name": "Status", + "options": [], + "query": { + "query": "trilio_backup_info", + "refId": "Prometheus-Status-Variable-Query" + }, + "refresh": 1, + "regex": "/.*status=\"([^\"]*).*/", + "skipUrlSync": false, + "sort": 0, + "tagValuesQuery": "", + "tagsQuery": "", + "type": "query", + "useTags": false + } + ] + }, + "time": { + "from": "now-15m", + "to": "now" + }, + "timepicker": { + "refresh_intervals": [ + "10s", + "30s", + "1m", + "5m", + "15m", + "30m", + "1h", + "2h", + "1d" + ] + }, + "timezone": "", + "title": "Backup Overview", + "uid": "BackupOverview", + "version": 1, + "weekStart": "" +} \ No newline at end of file diff --git a/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/visualization/charts/grafana/dashboards/backupplan-detail.json b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/visualization/charts/grafana/dashboards/backupplan-detail.json new file mode 100644 index 000000000..2295b2b09 --- /dev/null +++ b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/visualization/charts/grafana/dashboards/backupplan-detail.json @@ -0,0 +1,1198 @@ +{ + "__inputs": [ + { + "name": "DS_PROMETHEUS", + "label": "Prometheus", + "description": "", + "type": "datasource", + "pluginId": "prometheus", + "pluginName": "Prometheus" + }, + { + "name": "DS_LOKI", + "label": "Loki", + "description": "", + "type": "datasource", + "pluginId": "loki", + "pluginName": "Loki" + } + ], + "__elements": [], + "__requires": [ + { + "type": "grafana", + "id": "grafana", + "name": "Grafana", + "version": "8.5.0" + }, + { + "type": "panel", + "id": "logs", + "name": "Logs", + "version": "" + }, + { + "type": "datasource", + "id": "loki", + "name": "Loki", + "version": "1.0.0" + }, + { + "type": "datasource", + "id": "prometheus", + "name": "Prometheus", + "version": "1.0.0" + }, + { + "type": "panel", + "id": "stat", + "name": "Stat", + "version": "" + }, + { + "type": "panel", + "id": "table", + "name": "Table", + "version": "" + }, + { + "type": "panel", + "id": "text", + "name": "Text", + "version": "" + } + ], + "annotations": { + "list": [ + { + "$$hashKey": "object:4254", + "builtIn": 1, + "datasource": { + "type": "datasource", + "uid": "grafana" + }, + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "limit": 100, + "name": "Annotations & Alerts", + "showIn": 0, + "target": { + "limit": 100, + "matchAny": false, + "tags": [], + "type": "dashboard" + }, + "type": "dashboard" + } + ] + }, + "editable": true, + "fiscalYearStartMonth": 0, + "gnetId": 12605, + "graphTooltip": 0, + "id": null, + "iteration": 1655401068331, + "links": [], + "liveNow": false, + "panels": [ + { + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "gridPos": { + "h": 2, + "w": 23, + "x": 0, + "y": 0 + }, + "id": 4, + "options": { + "content": "

Backup Plan Detail

", + "mode": "html" + }, + "pluginVersion": "8.5.0", + "transparent": true, + "type": "text" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "mappings": [ + { + "options": { + "0": { + "text": "InProgress" + }, + "1": { + "text": "Available" + }, + "-1": { + "text": "Failed" + }, + "-2": { + "text": "UnKnown" + } + }, + "type": "value" + } + ], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "red", + "value": null + }, + { + "color": "dark-red", + "value": -1 + }, + { + "color": "blue", + "value": 0 + }, + { + "color": "green", + "value": 1 + } + ] + }, + "unit": "short" + }, + "overrides": [] + }, + "gridPos": { + "h": 3, + "w": 5, + "x": 0, + "y": 2 + }, + "id": 16, + "links": [], + "maxDataPoints": 100, + "options": { + "colorMode": "value", + "fieldOptions": { + "calcs": [ + "mean" + ] + }, + "graphMode": "none", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "first" + ], + "fields": "", + "values": false + }, + "textMode": "auto" + }, + "pluginVersion": "8.5.0", + "targets": [ + { + "datasource": "${DS_PROMETHEUS}", + "expr": "trilio_backupplan_info{backupplan=~\"$BackupPlan\",install_namespace=~\"$Install_Namespace\",cluster=~\"$Cluster\",kind=\"BackupPlan\"}", + "format": "time_series", + "instant": true, + "interval": "", + "intervalFactor": 1, + "legendFormat": "", + "refId": "A" + } + ], + "title": "Status", + "transparent": true, + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [ + { + "options": { + "match": "null", + "result": { + "text": "N/A" + } + }, + "type": "special" + } + ], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "none" + }, + "overrides": [] + }, + "gridPos": { + "h": 3, + "w": 4, + "x": 5, + "y": 2 + }, + "id": 9, + "links": [], + "maxDataPoints": 100, + "options": { + "colorMode": "none", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "horizontal", + "reduceOptions": { + "calcs": [ + "first" + ], + "fields": "/^applicationtype$/", + "values": false + }, + "textMode": "auto" + }, + "pluginVersion": "8.5.0", + "targets": [ + { + "datasource": "${DS_PROMETHEUS}", + "expr": "trilio_backupplan_info{ backupplan=~\"$BackupPlan\",install_namespace=~\"$Install_Namespace\",cluster=~\"$Cluster\",kind=\"BackupPlan\"}", + "format": "table", + "instant": true, + "interval": "", + "legendFormat": "", + "refId": "A" + } + ], + "title": "Application Type", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [ + { + "options": { + "match": "null", + "result": { + "text": "False" + } + }, + "type": "special" + } + ], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "none" + }, + "overrides": [] + }, + "gridPos": { + "h": 3, + "w": 4, + "x": 9, + "y": 2 + }, + "id": 10, + "links": [], + "maxDataPoints": 100, + "options": { + "colorMode": "none", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "horizontal", + "reduceOptions": { + "calcs": [ + "first" + ], + "fields": "/^protected$/", + "values": false + }, + "textMode": "auto" + }, + "pluginVersion": "8.5.0", + "targets": [ + { + "datasource": "${DS_PROMETHEUS}", + "expr": "trilio_backupplan_info{ backupplan=~\"$BackupPlan\",install_namespace=~\"$Install_Namespace\",cluster=~\"$Cluster\",kind=\"BackupPlan\"}", + "format": "table", + "instant": true, + "interval": "", + "legendFormat": "", + "refId": "A" + } + ], + "title": "Protected", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [ + { + "options": { + "match": "null", + "result": { + "text": "0" + } + }, + "type": "special" + } + ], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "none" + }, + "overrides": [] + }, + "gridPos": { + "h": 3, + "w": 4, + "x": 13, + "y": 2 + }, + "id": 13, + "links": [], + "maxDataPoints": 100, + "options": { + "colorMode": "none", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "horizontal", + "reduceOptions": { + "calcs": [ + "first" + ], + "fields": "/^backup_count$/", + "values": false + }, + "textMode": "auto" + }, + "pluginVersion": "8.5.0", + "targets": [ + { + "datasource": "${DS_PROMETHEUS}", + "expr": "trilio_backupplan_info{ backupplan=~\"$BackupPlan\",install_namespace=~\"$Install_Namespace\",cluster=~\"$Cluster\",kind=\"BackupPlan\"}", + "format": "table", + "instant": true, + "interval": "", + "legendFormat": "", + "refId": "A" + } + ], + "title": "Num of Backups", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [ + { + "options": { + "match": "null", + "result": { + "text": "N/A" + } + }, + "type": "special" + } + ], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "none" + }, + "overrides": [] + }, + "gridPos": { + "h": 3, + "w": 4, + "x": 17, + "y": 2 + }, + "id": 14, + "links": [], + "maxDataPoints": 100, + "options": { + "colorMode": "none", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "horizontal", + "reduceOptions": { + "calcs": [ + "first" + ], + "fields": "/^target$/", + "values": false + }, + "textMode": "auto" + }, + "pluginVersion": "8.5.0", + "targets": [ + { + "datasource": "${DS_PROMETHEUS}", + "expr": "trilio_backupplan_info{ backupplan=~\"$BackupPlan\",install_namespace=~\"$Install_Namespace\",cluster=~\"$Cluster\",kind=\"BackupPlan\"}", + "format": "table", + "instant": true, + "interval": "", + "legendFormat": "", + "refId": "A" + } + ], + "title": "Target", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "custom": { + "align": "auto", + "displayMode": "auto", + "inspect": false + }, + "decimals": 2, + "displayName": "", + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "short" + }, + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "backup" + }, + "properties": [ + { + "id": "displayName", + "value": "Backup" + }, + { + "id": "unit", + "value": "short" + }, + { + "id": "decimals", + "value": 2 + }, + { + "id": "links", + "value": [ + { + "targetBlank": false, + "title": "Backup Detail", + "url": "/d/${__data.fields.kind}/?refresh=5s&var-Backup=${__value.text}&var-Cluster=${Cluster}&var-Install_Namespace=${Install_Namespace}" + } + ] + }, + { + "id": "custom.align" + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "completion_ts" + }, + "properties": [ + { + "id": "displayName", + "value": "Completion" + }, + { + "id": "unit", + "value": "short" + }, + { + "id": "decimals", + "value": 2 + }, + { + "id": "custom.align" + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "size" + }, + "properties": [ + { + "id": "displayName", + "value": "Size" + }, + { + "id": "unit", + "value": "decbytes" + }, + { + "id": "custom.align" + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "status" + }, + "properties": [ + { + "id": "displayName", + "value": "Status" + }, + { + "id": "unit", + "value": "short" + }, + { + "id": "decimals", + "value": 2 + }, + { + "id": "custom.align" + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "backup_type" + }, + "properties": [ + { + "id": "displayName", + "value": "Backup Type" + }, + { + "id": "unit", + "value": "short" + }, + { + "id": "decimals", + "value": 2 + }, + { + "id": "custom.align" + } + ] + } + ] + }, + "gridPos": { + "h": 7, + "w": 12, + "x": 0, + "y": 5 + }, + "id": 12, + "options": { + "footer": { + "fields": "", + "reducer": [ + "sum" + ], + "show": false + }, + "showHeader": true + }, + "pluginVersion": "8.5.0", + "targets": [ + { + "datasource": "${DS_PROMETHEUS}", + "expr": "trilio_backup_info{backupplan=~\"$BackupPlan\",install_namespace=~\"$Install_Namespace\",cluster=~\"$Cluster\",kind=\"Backup\"}", + "format": "table", + "instant": true, + "interval": "", + "intervalFactor": 1, + "legendFormat": "", + "refId": "A" + } + ], + "title": "Backups", + "transformations": [ + { + "id": "merge", + "options": { + "reducers": [] + } + }, + { + "id": "filterFieldsByName", + "options": { + "include": { + "names": [ + "backup_type", + "status", + "backup", + "completion_ts", + "size", + "kind" + ] + } + } + } + ], + "type": "table" + }, + { + "datasource": { + "type": "loki", + "uid": "${DS_LOKI}" + }, + "description": "BackupPlan Logs", + "gridPos": { + "h": 14, + "w": 12, + "x": 12, + "y": 5 + }, + "id": 18, + "options": { + "dedupStrategy": "none", + "enableLogDetails": true, + "prettifyLogMessage": false, + "showCommonLabels": false, + "showLabels": false, + "showTime": true, + "sortOrder": "Descending", + "wrapLogMessage": false + }, + "targets": [ + { + "datasource": { + "type": "loki", + "uid": "${DS_LOKI}" + }, + "expr": "{transaction_type=\"BackupPlan\",transaction_resource_name=~\"$BackupPlan\",service_type=~\"$service_type\",transaction_resource_namespace=~\"$Namespace\"}", + "refId": "A" + } + ], + "title": "BackupPlan Logs", + "type": "logs" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "custom": { + "align": "auto", + "displayMode": "auto", + "inspect": false + }, + "decimals": 2, + "displayName": "", + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "short" + }, + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "consistentset_count" + }, + "properties": [ + { + "id": "displayName", + "value": "ConsistentSet Count" + }, + { + "id": "unit", + "value": "short" + }, + { + "id": "decimals", + "value": 0 + }, + { + "id": "links", + "value": [ + { + "targetBlank": false, + "title": "ConsistentSet Detail", + "url": "/d/ConsistentSet/?refresh=5s&var-Cluster=${Cluster}&var-Install_Namespace=${Install_Namespace}" + } + ] + }, + { + "id": "custom.align" + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "continuousrestoreinstance" + }, + "properties": [ + { + "id": "displayName", + "value": "ContinuousRestore Instance" + }, + { + "id": "custom.align", + "value": "left" + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "continuousrestoreplan" + }, + "properties": [ + { + "id": "displayName", + "value": "ContinuousRestorePlan" + }, + { + "id": "custom.align" + }, + { + "id": "links", + "value": [ + { + "title": "ContinuousRestorePlan Detail", + "url": "/d/ContinuousRestorePlan/?refresh=5s&var-ContinuousRestorePlan=${__value.text}&var-Cluster=${Cluster}&var-Install_Namespace=${Install_Namespace}" + } + ] + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "cr_status" + }, + "properties": [ + { + "id": "displayName", + "value": "ContinuousRestore Status" + }, + { + "id": "custom.align", + "value": "left" + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "ContinuousRestore Instance" + }, + "properties": [ + { + "id": "custom.width", + "value": 200 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "ConsistentSet" + }, + "properties": [ + { + "id": "custom.width", + "value": 112 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "ContinuousRestorePlan" + }, + "properties": [ + { + "id": "custom.width", + "value": 177 + } + ] + } + ] + }, + "gridPos": { + "h": 7, + "w": 12, + "x": 0, + "y": 12 + }, + "id": 19, + "options": { + "footer": { + "fields": "", + "reducer": [ + "sum" + ], + "show": false + }, + "showHeader": true, + "sortBy": [ + { + "desc": false, + "displayName": "ConsistentSet" + } + ] + }, + "pluginVersion": "8.5.0", + "targets": [ + { + "datasource": "${DS_PROMETHEUS}", + "expr": "trilio_backupplan_crstatus{backupplan=~\"$BackupPlan\",cluster=~\"$Cluster\",kind=\"BackupPlan\"}", + "format": "table", + "instant": true, + "interval": "", + "intervalFactor": 1, + "legendFormat": "", + "refId": "A" + } + ], + "title": "ContinuousRestore Info", + "transformations": [ + { + "id": "merge", + "options": { + "reducers": [] + } + }, + { + "id": "filterFieldsByName", + "options": { + "include": { + "names": [ + "consistentset_count", + "continuousrestoreinstance", + "continuousrestoreplan", + "cr_status" + ] + } + } + } + ], + "type": "table" + } + ], + "refresh": "10s", + "schemaVersion": 36, + "style": "dark", + "tags": [ + "logging" + ], + "templating": { + "list": [ + { + "current": { + "selected": false, + "text": "Prometheus", + "value": "Prometheus" + }, + "hide": 2, + "includeAll": false, + "label": "datasource", + "multi": false, + "name": "DS_PROMETHEUS", + "options": [], + "query": "prometheus", + "refresh": 1, + "regex": "", + "skipUrlSync": false, + "type": "datasource" + }, + { + "current": { + "selected": false, + "text": "Loki", + "value": "Loki" + }, + "hide": 2, + "includeAll": false, + "label": "loki", + "multi": false, + "name": "DS_LOKI", + "options": [], + "query": "loki", + "refresh": 1, + "regex": "", + "skipUrlSync": false, + "type": "datasource" + }, + { + "current": {}, + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "definition": "trilio_system_info", + "hide": 2, + "includeAll": false, + "multi": false, + "name": "Cluster", + "options": [], + "query": { + "query": "trilio_system_info", + "refId": "Prometheus-Cluster-Variable-Query" + }, + "refresh": 1, + "regex": "/.*cluster=\"([^\"]*).*/", + "skipUrlSync": false, + "sort": 0, + "tagValuesQuery": "", + "tagsQuery": "", + "type": "query", + "useTags": false + }, + { + "current": {}, + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "definition": "trilio_system_info{cluster=~\"$Cluster\"}", + "hide": 2, + "includeAll": false, + "multi": false, + "name": "Scope", + "options": [], + "query": { + "query": "trilio_system_info{cluster=~\"$Cluster\"}", + "refId": "Prometheus-Scope-Variable-Query" + }, + "refresh": 1, + "regex": "/.*scope=\"([^\"]*).*/", + "skipUrlSync": false, + "sort": 0, + "tagValuesQuery": "", + "tagsQuery": "", + "type": "query", + "useTags": false + }, + { + "current": {}, + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "definition": "trilio_system_info{scope=~\"$Scope\",cluster=~\"$Cluster\"}", + "hide": 0, + "includeAll": false, + "label": "Install Namespace", + "multi": false, + "name": "Install_Namespace", + "options": [], + "query": { + "query": "trilio_system_info{scope=~\"$Scope\",cluster=~\"$Cluster\"}", + "refId": "Prometheus-Install_Namespace-Variable-Query" + }, + "refresh": 2, + "regex": "/.*install_namespace=\"([^\"]*).*/", + "skipUrlSync": false, + "sort": 0, + "tagValuesQuery": "", + "tagsQuery": "", + "type": "query", + "useTags": false + }, + { + "current": {}, + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "definition": "trilio_backupplan_info{cluster=~\"$Cluster\",kind=\"BackupPlan\"}", + "hide": 0, + "includeAll": false, + "label": "BackupPlan", + "multi": false, + "name": "BackupPlan", + "options": [], + "query": { + "query": "trilio_backupplan_info{cluster=~\"$Cluster\",kind=\"BackupPlan\"}", + "refId": "StandardVariableQuery" + }, + "refresh": 2, + "regex": "/.*backupplan=\"([^\"]*).*/", + "skipUrlSync": false, + "sort": 0, + "tagValuesQuery": "", + "tagsQuery": "", + "type": "query", + "useTags": false + }, + { + "current": {}, + "datasource": { + "type": "loki", + "uid": "${DS_LOKI}" + }, + "definition": "label_values({transaction_type=\"BackupPlan\", transaction_resource_name=~\"$BackupPlan\"},transaction_resource_namespace)", + "description": "Backup Namespace", + "hide": 0, + "includeAll": true, + "label": "Backup Namespace", + "multi": false, + "name": "Namespace", + "options": [], + "query": "label_values({transaction_type=\"BackupPlan\", transaction_resource_name=~\"$BackupPlan\"},transaction_resource_namespace)", + "refresh": 2, + "regex": "", + "skipUrlSync": false, + "sort": 0, + "type": "query" + }, + { + "current": {}, + "datasource": { + "type": "loki", + "uid": "${DS_LOKI}" + }, + "definition": "label_values({transaction_type=\"BackupPlan\",transaction_resource_name=~\"$BackupPlan\"}, service_type)", + "description": "Service Type", + "hide": 0, + "includeAll": true, + "label": "Service Type", + "multi": false, + "name": "service_type", + "options": [], + "query": "label_values({transaction_type=\"BackupPlan\",transaction_resource_name=~\"$BackupPlan\"}, service_type)", + "refresh": 2, + "regex": "", + "skipUrlSync": false, + "sort": 0, + "type": "query" + } + ] + }, + "time": { + "from": "now-15m", + "to": "now" + }, + "timepicker": { + "refresh_intervals": [ + "10s", + "30s", + "1m", + "5m", + "15m", + "30m", + "1h", + "2h", + "1d" + ] + }, + "timezone": "", + "title": "BackupPlan Detail", + "uid": "BackupPlan", + "version": 1, + "weekStart": "" +} \ No newline at end of file diff --git a/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/visualization/charts/grafana/dashboards/backupplan-overview.json b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/visualization/charts/grafana/dashboards/backupplan-overview.json new file mode 100644 index 000000000..c780e0e67 --- /dev/null +++ b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/visualization/charts/grafana/dashboards/backupplan-overview.json @@ -0,0 +1,883 @@ +{ + "__inputs": [ + { + "name": "DS_PROMETHEUS", + "label": "Prometheus", + "description": "", + "type": "datasource", + "pluginId": "prometheus", + "pluginName": "Prometheus" + } + ], + "__elements": [], + "__requires": [ + { + "type": "grafana", + "id": "grafana", + "name": "Grafana", + "version": "8.5.0" + }, + { + "type": "panel", + "id": "grafana-piechart-panel", + "name": "Pie Chart (old)", + "version": "1.6.2" + }, + { + "type": "datasource", + "id": "prometheus", + "name": "Prometheus", + "version": "1.0.0" + }, + { + "type": "panel", + "id": "stat", + "name": "Stat", + "version": "" + }, + { + "type": "panel", + "id": "table", + "name": "Table", + "version": "" + }, + { + "type": "panel", + "id": "text", + "name": "Text", + "version": "" + } + ], + "annotations": { + "list": [ + { + "$$hashKey": "object:13226", + "builtIn": 1, + "datasource": { + "type": "datasource", + "uid": "grafana" + }, + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "limit": 100, + "name": "Annotations & Alerts", + "showIn": 0, + "target": { + "limit": 100, + "matchAny": false, + "tags": [], + "type": "dashboard" + }, + "type": "dashboard" + } + ] + }, + "editable": true, + "fiscalYearStartMonth": 0, + "gnetId": 12604, + "graphTooltip": 0, + "id": null, + "iteration": 1655400668324, + "links": [], + "liveNow": false, + "panels": [ + { + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "gridPos": { + "h": 2, + "w": 23, + "x": 0, + "y": 0 + }, + "id": 4, + "options": { + "content": "

Backup Plan Overview

", + "mode": "html" + }, + "pluginVersion": "8.5.0", + "transparent": true, + "type": "text" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "links": [], + "mappings": [], + "min": 0, + "noValue": "0", + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "rgb(46, 122, 122)", + "value": null + } + ] + }, + "unit": "short" + }, + "overrides": [] + }, + "gridPos": { + "h": 3, + "w": 4, + "x": 2, + "y": 2 + }, + "id": 34, + "links": [], + "options": { + "colorMode": "background", + "graphMode": "none", + "justifyMode": "center", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "last" + ], + "fields": "/^Available$/", + "values": false + }, + "textMode": "auto" + }, + "pluginVersion": "8.5.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "expr": "count(trilio_backupplan_info{install_namespace=~\"$Install_Namespace\",cluster=~\"$Cluster\"}) by (status)", + "format": "time_series", + "instant": true, + "interval": "", + "legendFormat": "{{status}}", + "refId": "A" + } + ], + "title": "All", + "transparent": true, + "type": "stat" + }, + { + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "links": [], + "mappings": [], + "min": 0, + "noValue": "0", + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "rgb(105, 191, 145)", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "short" + }, + "overrides": [] + }, + "gridPos": { + "h": 3, + "w": 4, + "x": 6, + "y": 2 + }, + "id": 35, + "links": [], + "options": { + "colorMode": "background", + "graphMode": "none", + "justifyMode": "center", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "last" + ], + "fields": "Helm", + "values": false + }, + "textMode": "auto" + }, + "pluginVersion": "8.5.0", + "targets": [ + { + "expr": "count(trilio_backupplan_info{applicationtype=\"Helm\",install_namespace=~\"$Install_Namespace\",cluster=~\"$Cluster\"}) by (applicationtype)", + "format": "time_series", + "instant": true, + "interval": "", + "legendFormat": "{{applicationtype}}", + "refId": "A", + "datasource": "${DS_PROMETHEUS}" + } + ], + "title": "Helm", + "transparent": true, + "type": "stat" + }, + { + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "links": [], + "mappings": [], + "min": 0, + "noValue": "0", + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "rgb(105, 191, 145)", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "short" + }, + "overrides": [] + }, + "gridPos": { + "h": 3, + "w": 4, + "x": 10, + "y": 2 + }, + "id": 36, + "links": [], + "options": { + "colorMode": "background", + "graphMode": "none", + "justifyMode": "center", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "last" + ], + "fields": "/^Operator$/", + "values": false + }, + "textMode": "auto" + }, + "pluginVersion": "8.5.0", + "targets": [ + { + "expr": "count(trilio_backupplan_info{applicationtype=\"Operator\",install_namespace=~\"$Install_Namespace\",cluster=~\"$Cluster\"}) by (applicationtype)", + "format": "time_series", + "instant": true, + "interval": "", + "legendFormat": "{{applicationtype}}", + "refId": "A", + "datasource": "${DS_PROMETHEUS}" + } + ], + "title": "Operator", + "transparent": true, + "type": "stat" + }, + { + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "links": [], + "mappings": [], + "min": 0, + "noValue": "0", + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "rgb(105, 191, 145)", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "short" + }, + "overrides": [] + }, + "gridPos": { + "h": 3, + "w": 4, + "x": 14, + "y": 2 + }, + "id": 37, + "links": [], + "options": { + "colorMode": "background", + "graphMode": "none", + "justifyMode": "center", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "last" + ], + "fields": "/^Custom$/", + "values": false + }, + "textMode": "auto" + }, + "pluginVersion": "8.5.0", + "targets": [ + { + "expr": "count(trilio_backupplan_info{applicationtype=\"Custom\",install_namespace=~\"$Install_Namespace\",cluster=~\"$Cluster\"}) by (applicationtype)", + "format": "time_series", + "instant": true, + "interval": "", + "legendFormat": "{{applicationtype}}", + "refId": "A", + "datasource": "${DS_PROMETHEUS}" + } + ], + "title": "Custom", + "transparent": true, + "type": "stat" + }, + { + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "links": [], + "mappings": [], + "min": 0, + "noValue": "0", + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "rgb(105, 191, 145)", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "short" + }, + "overrides": [] + }, + "gridPos": { + "h": 3, + "w": 4, + "x": 18, + "y": 2 + }, + "id": 40, + "links": [], + "options": { + "colorMode": "background", + "graphMode": "none", + "justifyMode": "center", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "last" + ], + "fields": "/^Namespace$/", + "values": false + }, + "textMode": "auto" + }, + "pluginVersion": "8.5.0", + "targets": [ + { + "expr": "count(trilio_backupplan_info{applicationtype=\"Namespace\",install_namespace=~\"$Install_Namespace\",cluster=~\"$Cluster\"}) by (applicationtype)", + "format": "time_series", + "instant": true, + "interval": "", + "legendFormat": "{{applicationtype}}", + "refId": "A", + "datasource": "${DS_PROMETHEUS}" + } + ], + "title": "Namespace", + "transparent": true, + "type": "stat" + }, + { + "aliasColors": {}, + "breakPoint": "50%", + "combine": { + "label": "Others", + "threshold": 0 + }, + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "fontSize": "80%", + "format": "short", + "gridPos": { + "h": 8, + "w": 7, + "x": 0, + "y": 5 + }, + "id": 39, + "legend": { + "header": "Protected", + "percentage": false, + "show": true, + "values": true + }, + "legendType": "Right side", + "links": [], + "maxDataPoints": 1, + "nullPointMode": "connected", + "pieType": "pie", + "strokeWidth": 1, + "targets": [ + { + "expr": "count(trilio_backupplan_info{applicationtype=~\"$ApplicationType\",install_namespace=~\"$Install_Namespace\",cluster=~\"$Cluster\"}) by (protected)", + "instant": true, + "interval": "", + "legendFormat": "{{protected}}", + "refId": "A", + "datasource": "${DS_PROMETHEUS}" + } + ], + "title": "Protected Backup Plan", + "transparent": true, + "type": "grafana-piechart-panel", + "valueName": "current" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "custom": { + "align": "auto", + "displayMode": "auto", + "inspect": false + }, + "decimals": 2, + "displayName": "", + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "short" + }, + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "backupplan" + }, + "properties": [ + { + "id": "displayName", + "value": "Backup Plan" + }, + { + "id": "unit", + "value": "short" + }, + { + "id": "decimals", + "value": 2 + }, + { + "id": "links", + "value": [ + { + "targetBlank": false, + "title": "Backup Plan Detail", + "url": "/d/${__data.fields.kind}/?refresh=5s&var-BackupPlan=${__value.text}&var-Cluster=${Cluster}&var-Install_Namespace=${Install_Namespace}" + } + ] + }, + { + "id": "custom.align" + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "applicationtype" + }, + "properties": [ + { + "id": "displayName", + "value": "Type" + }, + { + "id": "unit", + "value": "short" + }, + { + "id": "decimals", + "value": 2 + }, + { + "id": "custom.align" + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "backup_count" + }, + "properties": [ + { + "id": "displayName", + "value": "Num of Backups" + }, + { + "id": "unit", + "value": "short" + }, + { + "id": "custom.align" + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "protected" + }, + "properties": [ + { + "id": "displayName", + "value": "Protected" + }, + { + "id": "unit", + "value": "decbytes" + }, + { + "id": "decimals", + "value": 2 + }, + { + "id": "custom.align" + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "lastprotected" + }, + "properties": [ + { + "id": "displayName", + "value": "Last Protected" + }, + { + "id": "unit", + "value": "short" + }, + { + "id": "decimals", + "value": 2 + }, + { + "id": "custom.align" + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "target" + }, + "properties": [ + { + "id": "displayName", + "value": "Target" + }, + { + "id": "unit", + "value": "short" + }, + { + "id": "decimals", + "value": 2 + }, + { + "id": "links", + "value": [ + { + "targetBlank": false, + "title": "Target Detail", + "url": "/d/TargetDetail/target-detail?refresh=5s&var-Target=${__value.text}&var-Cluster=${Cluster}&var-Install_Namespace=${Install_Namespace}" + } + ] + }, + { + "id": "custom.align" + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "kind" + }, + "properties": [ + { + "id": "displayName", + "value": "BackupPlan Kind" + } + ] + } + ] + }, + "gridPos": { + "h": 8, + "w": 17, + "x": 7, + "y": 5 + }, + "id": 32, + "options": { + "footer": { + "fields": "", + "reducer": [ + "sum" + ], + "show": false + }, + "showHeader": true + }, + "pluginVersion": "8.5.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "expr": "trilio_backupplan_info{applicationtype=~\"$ApplicationType\",install_namespace=~\"$Install_Namespace\",cluster=~\"$Cluster\"}", + "format": "table", + "instant": true, + "interval": "", + "intervalFactor": 1, + "legendFormat": "", + "refId": "A" + } + ], + "transformations": [ + { + "id": "merge", + "options": { + "reducers": [] + } + }, + { + "id": "filterFieldsByName", + "options": { + "include": { + "names": [ + "applicationtype", + "backup_count", + "backupplan", + "kind", + "target", + "lastprotected", + "protected" + ] + } + } + } + ], + "type": "table" + } + ], + "refresh": "10s", + "schemaVersion": 36, + "style": "dark", + "tags": [], + "templating": { + "list": [ + { + "current": { + "selected": false, + "text": "Prometheus", + "value": "Prometheus" + }, + "hide": 2, + "includeAll": false, + "label": "datasource", + "multi": false, + "name": "DS_PROMETHEUS", + "options": [], + "query": "prometheus", + "refresh": 1, + "regex": "", + "skipUrlSync": false, + "type": "datasource" + }, + { + "current": {}, + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "definition": "trilio_system_info", + "hide": 0, + "includeAll": false, + "multi": false, + "name": "Cluster", + "options": [], + "query": { + "query": "trilio_system_info", + "refId": "Prometheus-Cluster-Variable-Query" + }, + "refresh": 1, + "regex": "/.*cluster=\"([^\"]*).*/", + "skipUrlSync": false, + "sort": 0, + "tagValuesQuery": "", + "tagsQuery": "", + "type": "query", + "useTags": false + }, + { + "current": {}, + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "definition": "trilio_system_info{cluster=~\"$Cluster\"}", + "hide": 2, + "includeAll": false, + "multi": false, + "name": "Scope", + "options": [], + "query": { + "query": "trilio_system_info{cluster=~\"$Cluster\"}", + "refId": "Prometheus-Scope-Variable-Query" + }, + "refresh": 1, + "regex": "/.*scope=\"([^\"]*).*/", + "skipUrlSync": false, + "sort": 0, + "tagValuesQuery": "", + "tagsQuery": "", + "type": "query", + "useTags": false + }, + { + "current": {}, + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "definition": "trilio_system_info{scope=~\"$Scope\",cluster=~\"$Cluster\"}", + "hide": 0, + "includeAll": false, + "label": "Install Namespace", + "multi": false, + "name": "Install_Namespace", + "options": [], + "query": { + "query": "trilio_system_info{scope=~\"$Scope\",cluster=~\"$Cluster\"}", + "refId": "Prometheus-Install_Namespace-Variable-Query" + }, + "refresh": 1, + "regex": "/.*install_namespace=\"([^\"]*).*/", + "skipUrlSync": false, + "sort": 0, + "tagValuesQuery": "", + "tagsQuery": "", + "type": "query", + "useTags": false + }, + { + "current": {}, + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "definition": "trilio_backupplan_info", + "hide": 0, + "includeAll": true, + "label": "Application Type", + "multi": false, + "name": "ApplicationType", + "options": [], + "query": { + "query": "trilio_backupplan_info", + "refId": "Prometheus-ApplicationType-Variable-Query" + }, + "refresh": 1, + "regex": "/.*applicationtype=\"([^\"]*).*/", + "skipUrlSync": false, + "sort": 0, + "tagValuesQuery": "", + "tagsQuery": "", + "type": "query", + "useTags": false + } + ] + }, + "time": { + "from": "now-15m", + "to": "now" + }, + "timepicker": { + "refresh_intervals": [ + "10s", + "30s", + "1m", + "5m", + "15m", + "30m", + "1h", + "2h", + "1d" + ] + }, + "timezone": "", + "title": "BackupPlan Overview", + "uid": "BackupPlanOverview", + "version": 1, + "weekStart": "" +} \ No newline at end of file diff --git a/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/visualization/charts/grafana/dashboards/clusterbackup-detail.json b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/visualization/charts/grafana/dashboards/clusterbackup-detail.json new file mode 100644 index 000000000..3603eb6bd --- /dev/null +++ b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/visualization/charts/grafana/dashboards/clusterbackup-detail.json @@ -0,0 +1,820 @@ +{ + "__inputs": [ + { + "name": "DS_PROMETHEUS", + "label": "Prometheus", + "description": "", + "type": "datasource", + "pluginId": "prometheus", + "pluginName": "Prometheus" + }, + { + "name": "DS_LOKI", + "label": "Loki", + "description": "", + "type": "datasource", + "pluginId": "loki", + "pluginName": "Loki" + } + ], + "__elements": [], + "__requires": [ + { + "type": "panel", + "id": "gauge", + "name": "Gauge", + "version": "" + }, + { + "type": "grafana", + "id": "grafana", + "name": "Grafana", + "version": "8.5.0" + }, + { + "type": "panel", + "id": "logs", + "name": "Logs", + "version": "" + }, + { + "type": "datasource", + "id": "loki", + "name": "Loki", + "version": "1.0.0" + }, + { + "type": "datasource", + "id": "prometheus", + "name": "Prometheus", + "version": "1.0.0" + }, + { + "type": "panel", + "id": "stat", + "name": "Stat", + "version": "" + }, + { + "type": "panel", + "id": "table", + "name": "Table", + "version": "" + }, + { + "type": "panel", + "id": "text", + "name": "Text", + "version": "" + } + ], + "annotations": { + "list": [ + { + "$$hashKey": "object:20", + "builtIn": 1, + "datasource": { + "type": "datasource", + "uid": "grafana" + }, + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "limit": 100, + "name": "Annotations & Alerts", + "showIn": 0, + "target": { + "limit": 100, + "matchAny": false, + "tags": [], + "type": "dashboard" + }, + "type": "dashboard" + } + ] + }, + "editable": true, + "fiscalYearStartMonth": 0, + "gnetId": 12601, + "graphTooltip": 0, + "id": null, + "iteration": 1655446898146, + "links": [], + "liveNow": false, + "panels": [ + { + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "gridPos": { + "h": 2, + "w": 24, + "x": 0, + "y": 0 + }, + "id": 31, + "options": { + "content": "

Cluster Backup Detail

", + "mode": "html" + }, + "pluginVersion": "8.5.0", + "transparent": true, + "type": "text" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "mappings": [], + "max": 100, + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "dark-red", + "value": null + }, + { + "color": "rgb(255, 255, 255)", + "value": 1 + }, + { + "color": "dark-green", + "value": 100 + } + ] + }, + "unit": "percent" + }, + "overrides": [] + }, + "gridPos": { + "h": 4, + "w": 7, + "x": 0, + "y": 2 + }, + "id": 45, + "options": { + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "last" + ], + "fields": "", + "values": false + }, + "showThresholdLabels": false, + "showThresholdMarkers": false + }, + "pluginVersion": "8.5.0", + "targets": [ + { + "datasource": "${DS_PROMETHEUS}", + "expr": "trilio_backup_status_percentage{backup=~\"$Backup\",install_namespace=~\"$Install_Namespace\",cluster=~\"$Cluster\",kind=\"ClusterBackup\"}", + "format": "time_series", + "instant": true, + "interval": "", + "intervalFactor": 1, + "legendFormat": "{{status}}", + "refId": "A" + } + ], + "transparent": true, + "type": "gauge" + }, + { + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "gridPos": { + "h": 1, + "w": 4, + "x": 8, + "y": 2 + }, + "id": 50, + "links": [], + "maxDataPoints": 100, + "options": { + "content": "", + "mode": "markdown" + }, + "pluginVersion": "8.5.0", + "targets": [ + { + "datasource": "${DS_PROMETHEUS}", + "expr": "trilio_backup_info{backup=~\"$Backup\",namespace=~\"$Namespace\",cluster=~\"$Cluster\"}", + "format": "time_series", + "instant": true, + "interval": "", + "intervalFactor": 1, + "legendFormat": "{{status}}", + "refId": "A" + } + ], + "transparent": true, + "type": "text" + }, + { + "datasource": { + "type": "loki", + "uid": "${DS_LOKI}" + }, + "description": "Cluster Backup Logs", + "gridPos": { + "h": 16, + "w": 12, + "x": 12, + "y": 2 + }, + "id": 52, + "options": { + "dedupStrategy": "none", + "enableLogDetails": true, + "prettifyLogMessage": false, + "showCommonLabels": false, + "showLabels": false, + "showTime": true, + "sortOrder": "Descending", + "wrapLogMessage": false + }, + "targets": [ + { + "datasource": { + "type": "loki", + "uid": "${DS_LOKI}" + }, + "expr": "{transaction_type=\"ClusterBackup\",transaction_resource_name=\"$Backup\",service_type=~\"$service_type\",child_transaction_resource_namespace=\"$Namespace\"}", + "refId": "A" + } + ], + "title": "Cluster Backup Logs", + "type": "logs" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "mappings": [ + { + "options": { + "0": { + "text": "InProgress" + }, + "1": { + "text": "Available" + }, + "-1": { + "text": "Failed" + }, + "-2": { + "text": "UnKnown" + } + }, + "type": "value" + } + ], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "red", + "value": null + }, + { + "color": "dark-red", + "value": -1 + }, + { + "color": "blue", + "value": 0 + }, + { + "color": "green", + "value": 1 + } + ] + }, + "unit": "short" + }, + "overrides": [] + }, + "gridPos": { + "h": 2, + "w": 5, + "x": 7, + "y": 3 + }, + "id": 46, + "links": [], + "maxDataPoints": 100, + "options": { + "colorMode": "value", + "fieldOptions": { + "calcs": [ + "mean" + ] + }, + "graphMode": "none", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "first" + ], + "fields": "", + "values": false + }, + "textMode": "value" + }, + "pluginVersion": "8.5.0", + "targets": [ + { + "datasource": "${DS_PROMETHEUS}", + "expr": "trilio_backup_info{backup=~\"$Backup\",install_namespace=~\"$Install_Namespace\",cluster=~\"$Cluster\",kind=\"ClusterBackup\"}", + "format": "time_series", + "instant": true, + "interval": "", + "intervalFactor": 1, + "legendFormat": "{{status}}", + "refId": "A" + } + ], + "transparent": true, + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [ + { + "options": { + "match": "null", + "result": { + "text": "N/A" + } + }, + "type": "special" + } + ], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "none" + }, + "overrides": [] + }, + "gridPos": { + "h": 3, + "w": 6, + "x": 0, + "y": 6 + }, + "id": 47, + "links": [], + "maxDataPoints": 100, + "options": { + "colorMode": "none", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "horizontal", + "reduceOptions": { + "calcs": [ + "first" + ], + "fields": "/^backup$/", + "values": false + }, + "text": { + "valueSize": 30 + }, + "textMode": "auto" + }, + "pluginVersion": "8.5.0", + "targets": [ + { + "datasource": "${DS_PROMETHEUS}", + "expr": "trilio_backup_info{ backup=~\"$Backup\",install_namespace=~\"$Install_Namespace\",cluster=~\"$Cluster\",kind=\"ClusterBackup\"}", + "format": "table", + "instant": true, + "interval": "", + "legendFormat": "", + "refId": "A" + } + ], + "title": "Name", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [ + { + "options": { + "match": "null", + "result": { + "text": "N/A" + } + }, + "type": "special" + } + ], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "none" + }, + "overrides": [] + }, + "gridPos": { + "h": 3, + "w": 6, + "x": 6, + "y": 6 + }, + "id": 36, + "links": [], + "maxDataPoints": 100, + "options": { + "colorMode": "none", + "graphMode": "none", + "justifyMode": "center", + "orientation": "horizontal", + "reduceOptions": { + "calcs": [ + "first" + ], + "fields": "/^backupplan$/", + "values": false + }, + "text": { + "valueSize": 30 + }, + "textMode": "auto" + }, + "pluginVersion": "8.5.0", + "targets": [ + { + "datasource": "${DS_PROMETHEUS}", + "expr": "trilio_backup_info{ backup=~\"$Backup\",install_namespace=~\"$Install_Namespace\",cluster=~\"$Cluster\",kind=\"ClusterBackup\"}", + "format": "table", + "instant": true, + "interval": "", + "legendFormat": "", + "refId": "A" + } + ], + "title": "Cluster Backup Plan", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "custom": { + "align": "left", + "displayMode": "auto", + "filterable": false, + "inspect": false + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + }, + "unit": "short" + }, + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "First" + }, + "properties": [ + { + "id": "displayName", + "value": "Value" + } + ] + } + ] + }, + "gridPos": { + "h": 9, + "w": 12, + "x": 0, + "y": 9 + }, + "id": 49, + "options": { + "footer": { + "fields": "", + "reducer": [ + "sum" + ], + "show": false + }, + "showHeader": true + }, + "pluginVersion": "8.5.0", + "targets": [ + { + "datasource": "${DS_PROMETHEUS}", + "expr": "trilio_backup_info{ backup=~\"$Backup\",install_namespace=~\"$Install_Namespace\",cluster=~\"$Cluster\",kind=\"ClusterBackup\"}", + "format": "table", + "instant": true, + "interval": "", + "legendFormat": "", + "refId": "A" + } + ], + "title": "Details", + "transformations": [ + { + "id": "filterFieldsByName", + "options": { + "include": { + "names": [ + "Time", + "applicationtype", + "backup_type", + "completion_ts", + "hook", + "size", + "start_ts", + "target", + "resource_namespace" + ] + } + } + }, + { + "id": "reduce", + "options": { + "reducers": [ + "first" + ] + } + } + ], + "type": "table" + } + ], + "refresh": "10s", + "schemaVersion": 36, + "style": "dark", + "tags": [], + "templating": { + "list": [ + { + "current": { + "selected": false, + "text": "Prometheus", + "value": "Prometheus" + }, + "hide": 2, + "includeAll": false, + "label": "datasource", + "multi": false, + "name": "DS_PROMETHEUS", + "options": [], + "query": "prometheus", + "refresh": 1, + "regex": "", + "skipUrlSync": false, + "type": "datasource" + }, + { + "current": { + "selected": false, + "text": "Loki", + "value": "Loki" + }, + "description": "loki datasource", + "hide": 2, + "includeAll": false, + "label": "datasource", + "multi": false, + "name": "DS_LOKI", + "options": [], + "query": "loki", + "refresh": 1, + "regex": "", + "skipUrlSync": false, + "type": "datasource" + }, + { + "current": {}, + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "definition": "trilio_system_info{cluster=~\"$Cluster\"}", + "hide": 2, + "includeAll": false, + "multi": false, + "name": "Scope", + "options": [], + "query": { + "query": "trilio_system_info{cluster=~\"$Cluster\"}", + "refId": "Prometheus-Scope-Variable-Query" + }, + "refresh": 1, + "regex": "/.*scope=\"([^\"]*).*/", + "skipUrlSync": false, + "sort": 0, + "tagValuesQuery": "", + "tagsQuery": "", + "type": "query", + "useTags": false + }, + { + "current": {}, + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "definition": "trilio_system_info{scope=~\"$Scope\",cluster=~\"$Cluster\"}", + "hide": 0, + "includeAll": false, + "label": "Install Namespace", + "multi": false, + "name": "Install_Namespace", + "options": [], + "query": { + "query": "trilio_system_info{scope=~\"$Scope\",cluster=~\"$Cluster\"}", + "refId": "Prometheus-Install_Namespace-Variable-Query" + }, + "refresh": 2, + "regex": "/.*install_namespace=\"([^\"]*).*/", + "skipUrlSync": false, + "sort": 0, + "tagValuesQuery": "", + "tagsQuery": "", + "type": "query", + "useTags": false + }, + { + "current": {}, + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "definition": "trilio_system_info", + "hide": 2, + "includeAll": false, + "multi": false, + "name": "Cluster", + "options": [], + "query": { + "query": "trilio_system_info", + "refId": "Prometheus-Cluster-Variable-Query" + }, + "refresh": 1, + "regex": "/.*cluster=\"([^\"]*).*/", + "skipUrlSync": false, + "sort": 0, + "tagValuesQuery": "", + "tagsQuery": "", + "type": "query", + "useTags": false + }, + { + "current": {}, + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "definition": "trilio_backup_info{cluster=~\"$Cluster\",install_namespace=~\"$Install_Namespace\",kind=\"ClusterBackup\"}", + "hide": 0, + "includeAll": false, + "label": "Cluster Backup", + "multi": false, + "name": "Backup", + "options": [], + "query": { + "query": "trilio_backup_info{cluster=~\"$Cluster\",install_namespace=~\"$Install_Namespace\",kind=\"ClusterBackup\"}", + "refId": "StandardVariableQuery" + }, + "refresh": 2, + "regex": "/.*backup=\"([^\"]*).*/", + "skipUrlSync": false, + "sort": 0, + "tagValuesQuery": "", + "tagsQuery": "", + "type": "query", + "useTags": false + }, + { + "current": {}, + "datasource": { + "type": "loki", + "uid": "${DS_LOKI}" + }, + "definition": "label_values({transaction_type=\"ClusterBackup\",transaction_name=~\"$Backup\"},child_transaction_resource_namespace)", + "description": "Child Transaction Namespace", + "hide": 0, + "includeAll": true, + "label": "Backup Namespace", + "multi": false, + "name": "Namespace", + "options": [], + "query": "label_values({transaction_type=\"ClusterBackup\",transaction_name=~\"$Backup\"},child_transaction_resource_namespace)", + "refresh": 2, + "regex": "", + "skipUrlSync": false, + "sort": 0, + "type": "query" + }, + { + "current": {}, + "datasource": { + "type": "loki", + "uid": "${DS_LOKI}" + }, + "definition": "label_values({transaction_type=\"ClusterBackup\"},service_type)", + "description": "Service Type", + "hide": 0, + "includeAll": true, + "label": "Service Type", + "multi": false, + "name": "service_type", + "options": [], + "query": "label_values({transaction_type=\"ClusterBackup\"},service_type)", + "refresh": 2, + "regex": "", + "skipUrlSync": false, + "sort": 0, + "type": "query" + } + ] + }, + "time": { + "from": "now-15m", + "to": "now" + }, + "timepicker": { + "refresh_intervals": [ + "10s", + "30s", + "1m", + "5m", + "15m", + "30m", + "1h", + "2h", + "1d" + ] + }, + "timezone": "", + "title": "Cluster Backup Detail", + "uid": "ClusterBackup", + "version": 1, + "weekStart": "" +} \ No newline at end of file diff --git a/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/visualization/charts/grafana/dashboards/clusterbackupplan-detail.json b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/visualization/charts/grafana/dashboards/clusterbackupplan-detail.json new file mode 100644 index 000000000..19b96b0a0 --- /dev/null +++ b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/visualization/charts/grafana/dashboards/clusterbackupplan-detail.json @@ -0,0 +1,1234 @@ +{ + "__inputs": [ + { + "name": "DS_PROMETHEUS", + "label": "Prometheus", + "description": "", + "type": "datasource", + "pluginId": "prometheus", + "pluginName": "Prometheus" + }, + { + "name": "DS_LOKI", + "label": "Loki", + "description": "", + "type": "datasource", + "pluginId": "loki", + "pluginName": "Loki" + } + ], + "__elements": [], + "__requires": [ + { + "type": "grafana", + "id": "grafana", + "name": "Grafana", + "version": "8.5.0" + }, + { + "type": "panel", + "id": "logs", + "name": "Logs", + "version": "" + }, + { + "type": "datasource", + "id": "loki", + "name": "Loki", + "version": "1.0.0" + }, + { + "type": "datasource", + "id": "prometheus", + "name": "Prometheus", + "version": "1.0.0" + }, + { + "type": "panel", + "id": "stat", + "name": "Stat", + "version": "" + }, + { + "type": "panel", + "id": "table", + "name": "Table", + "version": "" + }, + { + "type": "panel", + "id": "text", + "name": "Text", + "version": "" + } + ], + "annotations": { + "list": [ + { + "$$hashKey": "object:4254", + "builtIn": 1, + "datasource": { + "type": "datasource", + "uid": "grafana" + }, + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "limit": 100, + "name": "Annotations & Alerts", + "showIn": 0, + "target": { + "limit": 100, + "matchAny": false, + "tags": [], + "type": "dashboard" + }, + "type": "dashboard" + } + ] + }, + "editable": true, + "fiscalYearStartMonth": 0, + "gnetId": 12605, + "graphTooltip": 0, + "id": null, + "iteration": 1655445890261, + "links": [], + "liveNow": false, + "panels": [ + { + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "gridPos": { + "h": 2, + "w": 23, + "x": 0, + "y": 0 + }, + "id": 4, + "options": { + "content": "

Backup Plan Detail

", + "mode": "html" + }, + "pluginVersion": "8.5.0", + "transparent": true, + "type": "text" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "mappings": [ + { + "options": { + "0": { + "text": "InProgress" + }, + "1": { + "text": "Available" + }, + "-1": { + "text": "Failed" + }, + "-2": { + "text": "UnKnown" + } + }, + "type": "value" + } + ], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "red", + "value": null + }, + { + "color": "dark-red", + "value": -1 + }, + { + "color": "blue", + "value": 0 + }, + { + "color": "green", + "value": 1 + } + ] + }, + "unit": "short" + }, + "overrides": [] + }, + "gridPos": { + "h": 3, + "w": 5, + "x": 0, + "y": 2 + }, + "id": 16, + "links": [], + "maxDataPoints": 100, + "options": { + "colorMode": "value", + "fieldOptions": { + "calcs": [ + "mean" + ] + }, + "graphMode": "none", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "first" + ], + "fields": "", + "values": false + }, + "textMode": "auto" + }, + "pluginVersion": "8.5.0", + "targets": [ + { + "datasource": "${DS_PROMETHEUS}", + "expr": "trilio_backupplan_info{backupplan=~\"$BackupPlan\",install_namespace=~\"$Install_Namespace\",cluster=~\"$Cluster\",kind=\"ClusterBackupPlan\"}", + "format": "time_series", + "instant": true, + "interval": "", + "intervalFactor": 1, + "legendFormat": "", + "refId": "A" + } + ], + "title": "Status", + "transparent": true, + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [ + { + "options": { + "match": "null", + "result": { + "text": "N/A" + } + }, + "type": "special" + } + ], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "none" + }, + "overrides": [] + }, + "gridPos": { + "h": 3, + "w": 4, + "x": 5, + "y": 2 + }, + "id": 9, + "links": [], + "maxDataPoints": 100, + "options": { + "colorMode": "none", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "horizontal", + "reduceOptions": { + "calcs": [ + "first" + ], + "fields": "/^applicationtype$/", + "values": false + }, + "textMode": "auto" + }, + "pluginVersion": "8.5.0", + "targets": [ + { + "datasource": "${DS_PROMETHEUS}", + "expr": "trilio_backupplan_info{ backupplan=~\"$BackupPlan\",install_namespace=~\"$Install_Namespace\",cluster=~\"$Cluster\",kind=\"ClusterBackupPlan\"}", + "format": "table", + "instant": true, + "interval": "", + "legendFormat": "", + "refId": "A" + } + ], + "title": "Application Type", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [ + { + "options": { + "match": "null", + "result": { + "text": "False" + } + }, + "type": "special" + } + ], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "none" + }, + "overrides": [] + }, + "gridPos": { + "h": 3, + "w": 4, + "x": 9, + "y": 2 + }, + "id": 10, + "links": [], + "maxDataPoints": 100, + "options": { + "colorMode": "none", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "horizontal", + "reduceOptions": { + "calcs": [ + "first" + ], + "fields": "/^protected$/", + "values": false + }, + "textMode": "auto" + }, + "pluginVersion": "8.5.0", + "targets": [ + { + "datasource": "${DS_PROMETHEUS}", + "expr": "trilio_backupplan_info{ backupplan=~\"$BackupPlan\",install_namespace=~\"$Install_Namespace\",cluster=~\"$Cluster\",kind=\"ClusterBackupPlan\"}", + "format": "table", + "instant": true, + "interval": "", + "legendFormat": "", + "refId": "A" + } + ], + "title": "Protected", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [ + { + "options": { + "match": "null", + "result": { + "text": "0" + } + }, + "type": "special" + } + ], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "none" + }, + "overrides": [] + }, + "gridPos": { + "h": 3, + "w": 4, + "x": 13, + "y": 2 + }, + "id": 13, + "links": [], + "maxDataPoints": 100, + "options": { + "colorMode": "none", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "horizontal", + "reduceOptions": { + "calcs": [ + "first" + ], + "fields": "/^backup_count$/", + "values": false + }, + "textMode": "auto" + }, + "pluginVersion": "8.5.0", + "targets": [ + { + "datasource": "${DS_PROMETHEUS}", + "expr": "trilio_backupplan_info{ backupplan=~\"$BackupPlan\",install_namespace=~\"$Install_Namespace\",cluster=~\"$Cluster\",kind=\"ClusterBackupPlan\"}", + "format": "table", + "instant": true, + "interval": "", + "legendFormat": "", + "refId": "A" + } + ], + "title": "Num of Backups", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [ + { + "options": { + "match": "null", + "result": { + "text": "N/A" + } + }, + "type": "special" + } + ], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "none" + }, + "overrides": [] + }, + "gridPos": { + "h": 3, + "w": 4, + "x": 17, + "y": 2 + }, + "id": 14, + "links": [], + "maxDataPoints": 100, + "options": { + "colorMode": "none", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "horizontal", + "reduceOptions": { + "calcs": [ + "first" + ], + "fields": "/^target$/", + "values": false + }, + "textMode": "auto" + }, + "pluginVersion": "8.5.0", + "targets": [ + { + "datasource": "${DS_PROMETHEUS}", + "expr": "trilio_backupplan_info{ backupplan=~\"$BackupPlan\",install_namespace=~\"$Install_Namespace\",cluster=~\"$Cluster\",kind=\"ClusterBackupPlan\"}", + "format": "table", + "instant": true, + "interval": "", + "legendFormat": "", + "refId": "A" + } + ], + "title": "Target", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "custom": { + "align": "auto", + "displayMode": "auto", + "inspect": true, + "minWidth": 100 + }, + "decimals": 2, + "displayName": "", + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "short" + }, + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "backup" + }, + "properties": [ + { + "id": "displayName", + "value": "Backup" + }, + { + "id": "unit", + "value": "short" + }, + { + "id": "decimals", + "value": 2 + }, + { + "id": "links", + "value": [ + { + "targetBlank": false, + "title": "Backup Detail", + "url": "/d/${__data.fields.kind}?refresh=5s&var-Backup=${__value.text}&var-Cluster=${Cluster}&var-Install_Namespace=${Install_Namespace}" + } + ] + }, + { + "id": "custom.align" + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "completion_ts" + }, + "properties": [ + { + "id": "displayName", + "value": "Completion" + }, + { + "id": "unit", + "value": "short" + }, + { + "id": "decimals", + "value": 2 + }, + { + "id": "custom.align" + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "applicationtype" + }, + "properties": [ + { + "id": "displayName", + "value": "Type" + }, + { + "id": "unit", + "value": "short" + }, + { + "id": "decimals", + "value": 2 + }, + { + "id": "custom.align" + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "size" + }, + "properties": [ + { + "id": "displayName", + "value": "Size" + }, + { + "id": "unit", + "value": "decbytes" + }, + { + "id": "custom.align" + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "status" + }, + "properties": [ + { + "id": "displayName", + "value": "Status" + }, + { + "id": "unit", + "value": "short" + }, + { + "id": "decimals", + "value": 2 + }, + { + "id": "custom.align" + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "backup_type" + }, + "properties": [ + { + "id": "displayName", + "value": "Backup Type" + }, + { + "id": "unit", + "value": "short" + }, + { + "id": "decimals", + "value": 2 + }, + { + "id": "custom.align" + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "kind" + }, + "properties": [ + { + "id": "displayName", + "value": "Backup Kind" + } + ] + } + ] + }, + "gridPos": { + "h": 7, + "w": 12, + "x": 0, + "y": 5 + }, + "id": 12, + "options": { + "footer": { + "fields": "", + "reducer": [ + "sum" + ], + "show": false + }, + "showHeader": true + }, + "pluginVersion": "8.5.0", + "targets": [ + { + "datasource": "${DS_PROMETHEUS}", + "expr": "trilio_backup_info{backupplan=~\"$BackupPlan\",install_namespace=~\"$Install_Namespace\",cluster=~\"$Cluster\",kind=\"ClusterBackup\"}", + "format": "table", + "instant": true, + "interval": "", + "intervalFactor": 1, + "legendFormat": "", + "refId": "A" + } + ], + "title": "Backups", + "transformations": [ + { + "id": "merge", + "options": { + "reducers": [] + } + }, + { + "id": "filterFieldsByName", + "options": { + "include": { + "names": [ + "backup", + "backup_type", + "completion_ts", + "kind", + "size", + "status", + "target", + "applicationtype" + ] + } + } + } + ], + "type": "table" + }, + { + "datasource": { + "type": "loki", + "uid": "${DS_LOKI}" + }, + "description": "BackupPlan Logs", + "gridPos": { + "h": 14, + "w": 12, + "x": 12, + "y": 5 + }, + "id": 18, + "options": { + "dedupStrategy": "none", + "enableLogDetails": true, + "prettifyLogMessage": false, + "showCommonLabels": false, + "showLabels": false, + "showTime": true, + "sortOrder": "Descending", + "wrapLogMessage": false + }, + "targets": [ + { + "datasource": { + "type": "loki", + "uid": "${DS_LOKI}" + }, + "expr": "{transaction_type=\"ClusterBackupPlan\",transaction_resource_name=~\"$BackupPlan\",service_type=~\"$service_type\"}", + "refId": "A" + } + ], + "title": "BackupPlan Logs", + "type": "logs" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "custom": { + "align": "auto", + "displayMode": "auto", + "inspect": false + }, + "decimals": 2, + "displayName": "", + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "short" + }, + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "consistentset_count" + }, + "properties": [ + { + "id": "displayName", + "value": "ConsistentSet" + }, + { + "id": "unit", + "value": "short" + }, + { + "id": "decimals", + "value": 0 + }, + { + "id": "links", + "value": [ + { + "targetBlank": true, + "title": "ConsistentSet Detail", + "url": "/d/${__data.fields.kind}/?refresh=5s&var-ConsistentSet=${__value.text}&var-Cluster=${Cluster}&var-Install_Namespace=${Install_Namespace}" + } + ] + }, + { + "id": "custom.align" + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "continuousrestoreinstance" + }, + "properties": [ + { + "id": "displayName", + "value": "ContinuousRestore Instance" + }, + { + "id": "custom.align", + "value": "left" + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "continuousrestoreplan" + }, + "properties": [ + { + "id": "displayName", + "value": "ContinuousRestorePlan" + }, + { + "id": "custom.align" + }, + { + "id": "links", + "value": [ + { + "title": "ContinuousRestorePlan Detail", + "url": "/d/${__data.fields.kind}/?refresh=5s&var-ContinuousRestorePlan=${__value.text}&var-Cluster=${Cluster}&var-Install_Namespace=${Install_Namespace}" + } + ] + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "cr_status" + }, + "properties": [ + { + "id": "displayName", + "value": "ContinuousRestore Status" + }, + { + "id": "custom.align", + "value": "left" + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "ContinuousRestore Instance" + }, + "properties": [ + { + "id": "custom.width", + "value": 200 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "ConsistentSet" + }, + "properties": [ + { + "id": "custom.width", + "value": 112 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "ContinuousRestorePlan" + }, + "properties": [ + { + "id": "custom.width", + "value": 177 + } + ] + } + ] + }, + "gridPos": { + "h": 7, + "w": 12, + "x": 0, + "y": 12 + }, + "id": 20, + "options": { + "footer": { + "fields": "", + "reducer": [ + "sum" + ], + "show": false + }, + "showHeader": true, + "sortBy": [ + { + "desc": false, + "displayName": "ConsistentSet" + } + ] + }, + "pluginVersion": "8.5.0", + "targets": [ + { + "datasource": "${DS_PROMETHEUS}", + "expr": "trilio_backupplan_crstatus{backupplan=~\"$ClusterBackupPlan\",cluster=~\"$Cluster\",kind=\"ClusterBackupPlan\"}", + "format": "table", + "instant": true, + "interval": "", + "intervalFactor": 1, + "legendFormat": "", + "refId": "A" + } + ], + "title": "ContinuousRestore Info", + "transformations": [ + { + "id": "merge", + "options": { + "reducers": [] + } + }, + { + "id": "filterFieldsByName", + "options": { + "include": { + "names": [ + "consistentset_count", + "continuousrestoreinstance", + "continuousrestoreplan", + "cr_status" + ] + } + } + } + ], + "type": "table" + } + ], + "refresh": "30s", + "schemaVersion": 36, + "style": "dark", + "tags": [], + "templating": { + "list": [ + { + "current": { + "selected": false, + "text": "Prometheus", + "value": "Prometheus" + }, + "hide": 2, + "includeAll": false, + "label": "datasource", + "multi": false, + "name": "DS_PROMETHEUS", + "options": [], + "query": "prometheus", + "refresh": 1, + "regex": "", + "skipUrlSync": false, + "type": "datasource" + }, + { + "current": { + "selected": false, + "text": "Loki", + "value": "Loki" + }, + "hide": 2, + "includeAll": false, + "label": "loki", + "multi": false, + "name": "DS_LOKI", + "options": [], + "query": "loki", + "refresh": 1, + "regex": "", + "skipUrlSync": false, + "type": "datasource" + }, + { + "current": {}, + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "definition": "trilio_system_info", + "hide": 2, + "includeAll": false, + "multi": false, + "name": "Cluster", + "options": [], + "query": { + "query": "trilio_system_info", + "refId": "Prometheus-Cluster-Variable-Query" + }, + "refresh": 1, + "regex": "/.*cluster=\"([^\"]*).*/", + "skipUrlSync": false, + "sort": 0, + "tagValuesQuery": "", + "tagsQuery": "", + "type": "query", + "useTags": false + }, + { + "current": {}, + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "definition": "trilio_system_info{cluster=~\"$Cluster\"}", + "hide": 2, + "includeAll": false, + "multi": false, + "name": "Scope", + "options": [], + "query": { + "query": "trilio_system_info{cluster=~\"$Cluster\"}", + "refId": "Prometheus-Scope-Variable-Query" + }, + "refresh": 1, + "regex": "/.*scope=\"([^\"]*).*/", + "skipUrlSync": false, + "sort": 0, + "tagValuesQuery": "", + "tagsQuery": "", + "type": "query", + "useTags": false + }, + { + "current": {}, + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "definition": "trilio_system_info{scope=~\"$Scope\",cluster=~\"$Cluster\"}", + "hide": 0, + "includeAll": false, + "label": "Install Namespace", + "multi": false, + "name": "Install_Namespace", + "options": [], + "query": { + "query": "trilio_system_info{scope=~\"$Scope\",cluster=~\"$Cluster\"}", + "refId": "Prometheus-Install_Namespace-Variable-Query" + }, + "refresh": 2, + "regex": "/.*install_namespace=\"([^\"]*).*/", + "skipUrlSync": false, + "sort": 0, + "tagValuesQuery": "", + "tagsQuery": "", + "type": "query", + "useTags": false + }, + { + "current": {}, + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "definition": "trilio_backupplan_info{cluster=~\"$Cluster\",kind=\"ClusterBackupPlan\"}", + "hide": 0, + "includeAll": false, + "label": "Cluster Backup Plan", + "multi": false, + "name": "BackupPlan", + "options": [], + "query": { + "query": "trilio_backupplan_info{cluster=~\"$Cluster\",kind=\"ClusterBackupPlan\"}", + "refId": "StandardVariableQuery" + }, + "refresh": 2, + "regex": "/.*backupplan=\"([^\"]*).*/", + "skipUrlSync": false, + "sort": 0, + "tagValuesQuery": "", + "tagsQuery": "", + "type": "query", + "useTags": false + }, + { + "current": {}, + "datasource": { + "type": "loki", + "uid": "${DS_LOKI}" + }, + "definition": "label_values({transaction_type=\"ClusterBackupPlan\", transaction_resource_name=~\"$BackupPlan\"},child_transaction_resource_namespace)", + "description": "Backup Namespace", + "hide": 0, + "includeAll": true, + "label": "Backup Namespace", + "multi": false, + "name": "Namespace", + "options": [], + "query": "label_values({transaction_type=\"ClusterBackupPlan\", transaction_resource_name=~\"$BackupPlan\"},child_transaction_resource_namespace)", + "refresh": 2, + "regex": "", + "skipUrlSync": false, + "sort": 0, + "type": "query" + }, + { + "current": {}, + "datasource": { + "type": "loki", + "uid": "${DS_LOKI}" + }, + "definition": "label_values({transaction_type=\"ClusterBackupPlan\",transaction_resource_name=~\"$BackupPlan\"}, service_type)", + "description": "Service Type", + "hide": 0, + "includeAll": true, + "label": "Service Type", + "multi": false, + "name": "service_type", + "options": [], + "query": "label_values({transaction_type=\"ClusterBackupPlan\",transaction_resource_name=~\"$BackupPlan\"}, service_type)", + "refresh": 2, + "regex": "", + "skipUrlSync": false, + "sort": 0, + "type": "query" + } + ] + }, + "time": { + "from": "now-15m", + "to": "now" + }, + "timepicker": { + "refresh_intervals": [ + "10s", + "30s", + "1m", + "5m", + "15m", + "30m", + "1h", + "2h", + "1d" + ] + }, + "timezone": "", + "title": "ClusterBackupPlan Detail", + "uid": "ClusterBackupPlan", + "version": 1, + "weekStart": "" +} \ No newline at end of file diff --git a/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/visualization/charts/grafana/dashboards/clusterrestore-detail.json b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/visualization/charts/grafana/dashboards/clusterrestore-detail.json new file mode 100644 index 000000000..ce6851fd1 --- /dev/null +++ b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/visualization/charts/grafana/dashboards/clusterrestore-detail.json @@ -0,0 +1,802 @@ +{ + "__inputs": [ + { + "name": "DS_PROMETHEUS", + "label": "Prometheus", + "description": "", + "type": "datasource", + "pluginId": "prometheus", + "pluginName": "Prometheus" + }, + { + "name": "DS_LOKI", + "label": "Loki", + "description": "", + "type": "datasource", + "pluginId": "loki", + "pluginName": "Loki" + } + ], + "__elements": [], + "__requires": [ + { + "type": "panel", + "id": "gauge", + "name": "Gauge", + "version": "" + }, + { + "type": "grafana", + "id": "grafana", + "name": "Grafana", + "version": "8.5.0" + }, + { + "type": "panel", + "id": "logs", + "name": "Logs", + "version": "" + }, + { + "type": "datasource", + "id": "loki", + "name": "Loki", + "version": "1.0.0" + }, + { + "type": "datasource", + "id": "prometheus", + "name": "Prometheus", + "version": "1.0.0" + }, + { + "type": "panel", + "id": "stat", + "name": "Stat", + "version": "" + }, + { + "type": "panel", + "id": "table", + "name": "Table", + "version": "" + }, + { + "type": "panel", + "id": "text", + "name": "Text", + "version": "" + } + ], + "annotations": { + "list": [ + { + "$$hashKey": "object:1512", + "builtIn": 1, + "datasource": { + "type": "datasource", + "uid": "grafana" + }, + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "limit": 100, + "name": "Annotations & Alerts", + "showIn": 0, + "target": { + "limit": 100, + "matchAny": false, + "tags": [], + "type": "dashboard" + }, + "type": "dashboard" + } + ] + }, + "editable": true, + "fiscalYearStartMonth": 0, + "gnetId": 12603, + "graphTooltip": 0, + "id": null, + "iteration": 1655447850637, + "links": [], + "liveNow": false, + "panels": [ + { + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "gridPos": { + "h": 2, + "w": 23, + "x": 0, + "y": 0 + }, + "id": 4, + "options": { + "content": "

Restore Detail

", + "mode": "html" + }, + "pluginVersion": "8.5.0", + "transparent": true, + "type": "text" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "mappings": [], + "max": 100, + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "dark-red", + "value": null + }, + { + "color": "rgb(255, 255, 255)", + "value": 1 + }, + { + "color": "dark-green", + "value": 100 + } + ] + }, + "unit": "percent" + }, + "overrides": [] + }, + "gridPos": { + "h": 4, + "w": 7, + "x": 0, + "y": 2 + }, + "id": 13, + "options": { + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "last" + ], + "fields": "", + "values": false + }, + "showThresholdLabels": false, + "showThresholdMarkers": false + }, + "pluginVersion": "8.5.0", + "targets": [ + { + "datasource": "${DS_PROMETHEUS}", + "expr": "trilio_restore_status_percentage{restore=~\"$Restore\",install_namespace=~\"$Install_Namespace\",cluster=~\"$Cluster\",kind=\"ClusterRestore\"}", + "format": "time_series", + "instant": true, + "interval": "", + "intervalFactor": 1, + "legendFormat": "{{status}}", + "refId": "A" + } + ], + "transparent": true, + "type": "gauge" + }, + { + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "gridPos": { + "h": 1, + "w": 4, + "x": 7, + "y": 2 + }, + "id": 17, + "options": { + "content": "", + "mode": "markdown" + }, + "pluginVersion": "8.5.0", + "transparent": true, + "type": "text" + }, + { + "datasource": { + "type": "loki", + "uid": "${DS_LOKI}" + }, + "gridPos": { + "h": 15, + "w": 12, + "x": 12, + "y": 2 + }, + "id": 22, + "options": { + "dedupStrategy": "none", + "enableLogDetails": true, + "prettifyLogMessage": false, + "showCommonLabels": false, + "showLabels": false, + "showTime": true, + "sortOrder": "Descending", + "wrapLogMessage": false + }, + "targets": [ + { + "datasource": { + "type": "loki", + "uid": "${DS_LOKI}" + }, + "expr": "{transaction_type=\"ClusterRestore\",transaction_resource_name=~\"$Restore\",service_type=~\"$service_type\",child_transaction_resource_namespace=~\"$Namespace\"}", + "refId": "A" + } + ], + "title": "Restore Logs", + "type": "logs" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "mappings": [ + { + "options": { + "0": { + "text": "InProgress" + }, + "1": { + "text": "Completed" + }, + "-1": { + "text": "Failed" + }, + "-2": { + "text": "UnKnown" + } + }, + "type": "value" + } + ], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "red", + "value": null + }, + { + "color": "dark-red", + "value": -1 + }, + { + "color": "blue", + "value": 0 + }, + { + "color": "green", + "value": 1 + } + ] + }, + "unit": "short" + }, + "overrides": [] + }, + "gridPos": { + "h": 2, + "w": 5, + "x": 7, + "y": 3 + }, + "id": 15, + "links": [], + "maxDataPoints": 100, + "options": { + "colorMode": "value", + "fieldOptions": { + "calcs": [ + "mean" + ] + }, + "graphMode": "none", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "first" + ], + "fields": "", + "values": false + }, + "textMode": "value" + }, + "pluginVersion": "8.5.0", + "targets": [ + { + "datasource": "${DS_PROMETHEUS}", + "expr": "trilio_restore_info{restore=~\"$Restore\",install_namespace=~\"$Install_Namespace\",cluster=~\"$Cluster\",kind=\"ClusterRestore\"}", + "format": "time_series", + "instant": true, + "interval": "", + "intervalFactor": 1, + "legendFormat": "{{status}}", + "refId": "A" + } + ], + "transparent": true, + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [ + { + "options": { + "match": "null", + "result": { + "text": "N/A" + } + }, + "type": "special" + } + ], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "none" + }, + "overrides": [] + }, + "gridPos": { + "h": 3, + "w": 6, + "x": 0, + "y": 6 + }, + "id": 6, + "links": [], + "maxDataPoints": 100, + "options": { + "colorMode": "none", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "horizontal", + "reduceOptions": { + "calcs": [ + "first" + ], + "fields": "/^restore$/", + "values": false + }, + "text": { + "valueSize": 30 + }, + "textMode": "auto" + }, + "pluginVersion": "8.5.0", + "targets": [ + { + "datasource": "${DS_PROMETHEUS}", + "expr": "trilio_restore_info{ restore=~\"$Restore\",install_namespace=~\"$Install_Namespace\",cluster=~\"$Cluster\",kind=\"ClusterRestore\"}", + "format": "table", + "instant": true, + "interval": "", + "legendFormat": "", + "refId": "A" + } + ], + "title": "Restore", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [ + { + "options": { + "match": "null", + "result": { + "text": "N/A" + } + }, + "type": "special" + } + ], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "none" + }, + "overrides": [] + }, + "gridPos": { + "h": 3, + "w": 6, + "x": 6, + "y": 6 + }, + "id": 20, + "links": [], + "maxDataPoints": 100, + "options": { + "colorMode": "none", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "horizontal", + "reduceOptions": { + "calcs": [ + "first" + ], + "fields": "/^backup$/", + "values": false + }, + "text": { + "valueSize": 30 + }, + "textMode": "auto" + }, + "pluginVersion": "8.5.0", + "targets": [ + { + "datasource": "${DS_PROMETHEUS}", + "expr": "trilio_restore_info{ restore=~\"$Restore\",install_namespace=~\"$Install_Namespace\",cluster=~\"$Cluster\",kind=\"ClusterRestore\"}", + "format": "table", + "instant": true, + "interval": "", + "legendFormat": "", + "refId": "A" + } + ], + "title": "Backup", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "custom": { + "align": "left", + "displayMode": "auto", + "filterable": false, + "inspect": false + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + }, + "unit": "short" + }, + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "First" + }, + "properties": [ + { + "id": "displayName", + "value": "Value" + } + ] + } + ] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 9 + }, + "id": 19, + "options": { + "footer": { + "fields": "", + "reducer": [ + "sum" + ], + "show": false + }, + "showHeader": true + }, + "pluginVersion": "8.5.0", + "targets": [ + { + "datasource": "${DS_PROMETHEUS}", + "expr": "trilio_restore_info{ restore=~\"$Restore\",install_namespace=~\"$Install_Namespace\",cluster=~\"$Cluster\",kind=\"ClusterRestore\"}", + "format": "table", + "instant": true, + "interval": "", + "legendFormat": "", + "refId": "A" + } + ], + "title": "Details", + "transformations": [ + { + "id": "filterFieldsByName", + "options": { + "include": { + "names": [ + "Time", + "completion_ts", + "size", + "start_ts", + "target", + "resource_namespace" + ] + } + } + }, + { + "id": "reduce", + "options": { + "reducers": [ + "first" + ] + } + } + ], + "type": "table" + } + ], + "refresh": "30s", + "schemaVersion": 36, + "style": "dark", + "tags": [], + "templating": { + "list": [ + { + "current": { + "selected": false, + "text": "Prometheus", + "value": "Prometheus" + }, + "hide": 2, + "includeAll": false, + "label": "datasource", + "multi": false, + "name": "DS_PROMETHEUS", + "options": [], + "query": "prometheus", + "refresh": 1, + "regex": "", + "skipUrlSync": false, + "type": "datasource" + }, + { + "current": { + "selected": false, + "text": "Loki", + "value": "Loki" + }, + "hide": 2, + "includeAll": false, + "label": "loki", + "multi": false, + "name": "DS_LOKI", + "options": [], + "query": "loki", + "refresh": 1, + "regex": "", + "skipUrlSync": false, + "type": "datasource" + }, + { + "current": {}, + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "definition": "trilio_system_info", + "hide": 2, + "includeAll": false, + "multi": false, + "name": "Cluster", + "options": [], + "query": { + "query": "trilio_system_info", + "refId": "Prometheus-Cluster-Variable-Query" + }, + "refresh": 1, + "regex": "/.*cluster=\"([^\"]*).*/", + "skipUrlSync": false, + "sort": 0, + "tagValuesQuery": "", + "tagsQuery": "", + "type": "query", + "useTags": false + }, + { + "current": {}, + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "definition": "trilio_system_info{cluster=~\"$Cluster\"}", + "hide": 2, + "includeAll": false, + "multi": false, + "name": "Scope", + "options": [], + "query": { + "query": "trilio_system_info{cluster=~\"$Cluster\"}", + "refId": "Prometheus-Scope-Variable-Query" + }, + "refresh": 1, + "regex": "/.*scope=\"([^\"]*).*/", + "skipUrlSync": false, + "sort": 0, + "tagValuesQuery": "", + "tagsQuery": "", + "type": "query", + "useTags": false + }, + { + "current": {}, + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "definition": "trilio_system_info{scope=~\"$Scope\",cluster=~\"$Cluster\"}", + "hide": 0, + "includeAll": false, + "label": "Install Namespace", + "multi": false, + "name": "Install_Namespace", + "options": [], + "query": { + "query": "trilio_system_info{scope=~\"$Scope\",cluster=~\"$Cluster\"}", + "refId": "Prometheus-Install_Namespace-Variable-Query" + }, + "refresh": 2, + "regex": "/.*install_namespace=\"([^\"]*).*/", + "skipUrlSync": false, + "sort": 0, + "tagValuesQuery": "", + "tagsQuery": "", + "type": "query", + "useTags": false + }, + { + "allValue": " ", + "current": {}, + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "definition": "trilio_restore_info{cluster=~\"$Cluster\",kind=\"ClusterRestore\"}", + "hide": 0, + "includeAll": false, + "label": "Restore", + "multi": false, + "name": "Restore", + "options": [], + "query": { + "query": "trilio_restore_info{cluster=~\"$Cluster\",kind=\"ClusterRestore\"}", + "refId": "StandardVariableQuery" + }, + "refresh": 2, + "regex": "/.*restore=\"([^\"]*).*/", + "skipUrlSync": false, + "sort": 0, + "tagValuesQuery": "", + "tagsQuery": "", + "type": "query", + "useTags": false + }, + { + "current": {}, + "datasource": { + "type": "loki", + "uid": "${DS_LOKI}" + }, + "definition": "label_values({transaction_type=\"ClusterRestore\",transaction_resource_name=~\"$Restore\"},child_transaction_resource_namespace)", + "description": "Restore Namespace", + "hide": 0, + "includeAll": true, + "label": "Restore Namespace", + "multi": false, + "name": "Namespace", + "options": [], + "query": "label_values({transaction_type=\"ClusterRestore\",transaction_resource_name=~\"$Restore\"},child_transaction_resource_namespace)", + "refresh": 2, + "regex": "", + "skipUrlSync": false, + "sort": 0, + "type": "query" + }, + { + "current": {}, + "datasource": { + "type": "loki", + "uid": "${DS_LOKI}" + }, + "definition": "label_values({transaction_type=\"ClusterRestore\",transaction_resource_name=~\"$Restore\"}, service_type)", + "description": "Service Type", + "hide": 0, + "includeAll": true, + "label": "Service Type", + "multi": false, + "name": "service_type", + "options": [], + "query": "label_values({transaction_type=\"ClusterRestore\",transaction_resource_name=~\"$Restore\"}, service_type)", + "refresh": 2, + "regex": "", + "skipUrlSync": false, + "sort": 0, + "type": "query" + } + ] + }, + "time": { + "from": "now-15m", + "to": "now" + }, + "timepicker": { + "refresh_intervals": [ + "10s", + "30s", + "1m", + "5m", + "15m", + "30m", + "1h", + "2h", + "1d" + ] + }, + "timezone": "", + "title": "Cluster Restore Detail", + "uid": "ClusterRestore", + "version": 1, + "weekStart": "" +} \ No newline at end of file diff --git a/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/visualization/charts/grafana/dashboards/consistentset-detail.json b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/visualization/charts/grafana/dashboards/consistentset-detail.json new file mode 100644 index 000000000..c7bee36c4 --- /dev/null +++ b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/visualization/charts/grafana/dashboards/consistentset-detail.json @@ -0,0 +1,832 @@ +{ + "__inputs": [ + { + "name": "DS_PROMETHEUS", + "label": "Prometheus", + "description": "", + "type": "datasource", + "pluginId": "prometheus", + "pluginName": "Prometheus" + }, + { + "name": "DS_LOKI", + "label": "Loki", + "description": "", + "type": "datasource", + "pluginId": "loki", + "pluginName": "Loki" + } + ], + "__elements": [], + "__requires": [ + { + "type": "panel", + "id": "gauge", + "name": "Gauge", + "version": "" + }, + { + "type": "grafana", + "id": "grafana", + "name": "Grafana", + "version": "8.5.0" + }, + { + "type": "panel", + "id": "logs", + "name": "Logs", + "version": "" + }, + { + "type": "datasource", + "id": "loki", + "name": "Loki", + "version": "1.0.0" + }, + { + "type": "datasource", + "id": "prometheus", + "name": "Prometheus", + "version": "1.0.0" + }, + { + "type": "panel", + "id": "stat", + "name": "Stat", + "version": "" + }, + { + "type": "panel", + "id": "table", + "name": "Table", + "version": "" + }, + { + "type": "panel", + "id": "text", + "name": "Text", + "version": "" + } + ], + "annotations": { + "list": [ + { + "$$hashKey": "object:20", + "builtIn": 1, + "datasource": { + "type": "datasource", + "uid": "grafana" + }, + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "limit": 100, + "name": "Annotations & Alerts", + "showIn": 0, + "target": { + "limit": 100, + "matchAny": false, + "tags": [], + "type": "dashboard" + }, + "type": "dashboard" + } + ] + }, + "editable": true, + "fiscalYearStartMonth": 0, + "gnetId": 12601, + "graphTooltip": 0, + "id": null, + "iteration": 1672672013297, + "links": [], + "liveNow": false, + "panels": [ + { + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "gridPos": { + "h": 2, + "w": 24, + "x": 0, + "y": 0 + }, + "id": 31, + "options": { + "content": "

ConsistentSet Detail

", + "mode": "html" + }, + "pluginVersion": "8.5.0", + "transparent": true, + "type": "text" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "mappings": [], + "max": 100, + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "dark-red", + "value": null + }, + { + "color": "rgb(255, 255, 255)", + "value": 1 + }, + { + "color": "dark-green", + "value": 100 + } + ] + }, + "unit": "percent" + }, + "overrides": [] + }, + "gridPos": { + "h": 4, + "w": 7, + "x": 0, + "y": 2 + }, + "id": 45, + "options": { + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "last" + ], + "fields": "", + "values": false + }, + "showThresholdLabels": false, + "showThresholdMarkers": false + }, + "pluginVersion": "8.5.0", + "targets": [ + { + "datasource": "${DS_PROMETHEUS}", + "expr": "trilio_consistentset_status_percentage{consistentset=~\"$ConsistentSet\",install_namespace=~\"$Install_Namespace\",cluster=~\"$Cluster\",kind=\"ConsistentSet\"}", + "format": "time_series", + "instant": true, + "interval": "", + "intervalFactor": 1, + "legendFormat": "{{status}}", + "refId": "A" + } + ], + "transparent": true, + "type": "gauge" + }, + { + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "gridPos": { + "h": 1, + "w": 4, + "x": 8, + "y": 2 + }, + "id": 50, + "links": [], + "maxDataPoints": 100, + "options": { + "content": "", + "mode": "markdown" + }, + "pluginVersion": "8.5.0", + "targets": [ + { + "datasource": "${DS_PROMETHEUS}", + "expr": "trilio_consistentset_info{consistentset=~\"$ConsistentSet\",cluster=~\"$Cluster\"}", + "format": "time_series", + "instant": true, + "interval": "", + "intervalFactor": 1, + "legendFormat": "{{status}}", + "refId": "A" + } + ], + "transparent": true, + "type": "text" + }, + { + "datasource": { + "type": "loki", + "uid": "${DS_LOKI}" + }, + "description": "ConsistentSet Logs", + "gridPos": { + "h": 16, + "w": 12, + "x": 12, + "y": 2 + }, + "id": 52, + "options": { + "dedupStrategy": "none", + "enableLogDetails": true, + "prettifyLogMessage": false, + "showCommonLabels": false, + "showLabels": false, + "showTime": true, + "sortOrder": "Descending", + "wrapLogMessage": false + }, + "targets": [ + { + "datasource": { + "type": "loki", + "uid": "${DS_LOKI}" + }, + "expr": "{child_transaction_resource_name=\"$ConsistentSet\",child_transaction_type=\"ConsistentSet\",transaction_resource_name=\"$ContinuousRestorePlan\",transaction_type=\"ContinuousRestorePlan\"}", + "refId": "A" + } + ], + "title": "ConsistentSet Logs", + "type": "logs" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "mappings": [ + { + "options": { + "0": { + "text": "InProgress" + }, + "1": { + "text": "Available" + }, + "-1": { + "text": "Failed" + }, + "-2": { + "text": "UnKnown" + } + }, + "type": "value" + } + ], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "red", + "value": null + }, + { + "color": "dark-red", + "value": -1 + }, + { + "color": "blue", + "value": 0 + }, + { + "color": "green", + "value": 1 + } + ] + }, + "unit": "short" + }, + "overrides": [] + }, + "gridPos": { + "h": 2, + "w": 5, + "x": 7, + "y": 3 + }, + "id": 46, + "links": [], + "maxDataPoints": 100, + "options": { + "colorMode": "value", + "fieldOptions": { + "calcs": [ + "mean" + ] + }, + "graphMode": "none", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "first" + ], + "fields": "", + "values": false + }, + "textMode": "value" + }, + "pluginVersion": "8.5.0", + "targets": [ + { + "datasource": "${DS_PROMETHEUS}", + "expr": "trilio_consistentset_info{consistentset=~\"$ConsistentSet\",install_namespace=~\"$Install_Namespace\",cluster=~\"$Cluster\",kind=\"ConsistentSet\"}", + "format": "time_series", + "instant": true, + "interval": "", + "intervalFactor": 1, + "legendFormat": "{{status}}", + "refId": "A" + } + ], + "transparent": true, + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [ + { + "options": { + "match": "null", + "result": { + "text": "N/A" + } + }, + "type": "special" + } + ], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "none" + }, + "overrides": [] + }, + "gridPos": { + "h": 3, + "w": 6, + "x": 0, + "y": 6 + }, + "id": 47, + "links": [], + "maxDataPoints": 100, + "options": { + "colorMode": "none", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "horizontal", + "reduceOptions": { + "calcs": [ + "first" + ], + "fields": "/^consistentset$/", + "values": false + }, + "text": { + "valueSize": 10 + }, + "textMode": "auto" + }, + "pluginVersion": "8.5.0", + "targets": [ + { + "datasource": "${DS_PROMETHEUS}", + "expr": "trilio_consistentset_info{ consistentset=~\"$ConsistentSet\",install_namespace=~\"$Install_Namespace\",cluster=~\"$Cluster\",kind=\"ConsistentSet\"}", + "format": "table", + "instant": true, + "interval": "", + "legendFormat": "", + "refId": "A" + } + ], + "title": "Name", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [ + { + "options": { + "match": "null", + "result": { + "text": "N/A" + } + }, + "type": "special" + } + ], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "none" + }, + "overrides": [] + }, + "gridPos": { + "h": 3, + "w": 6, + "x": 6, + "y": 6 + }, + "id": 36, + "links": [], + "maxDataPoints": 100, + "options": { + "colorMode": "none", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "horizontal", + "reduceOptions": { + "calcs": [ + "first" + ], + "fields": "/^continuousrestoreplan$/", + "values": false + }, + "text": { + "valueSize": 10 + }, + "textMode": "auto" + }, + "pluginVersion": "8.5.0", + "targets": [ + { + "datasource": "${DS_PROMETHEUS}", + "expr": "trilio_consistentset_info{consistentset=~\"$ConsistentSet\",install_namespace=~\"$Install_Namespace\",cluster=~\"$Cluster\",kind=\"ConsistentSet\"}", + "format": "table", + "instant": true, + "interval": "", + "legendFormat": "", + "refId": "A" + } + ], + "title": "ContinuousRestorePlan", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "custom": { + "align": "left", + "displayMode": "auto", + "filterable": false, + "inspect": false + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + }, + "unit": "short" + }, + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "First" + }, + "properties": [ + { + "id": "displayName", + "value": "Value" + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Field" + }, + "properties": [ + { + "id": "displayName" + } + ] + } + ] + }, + "gridPos": { + "h": 9, + "w": 12, + "x": 0, + "y": 9 + }, + "id": 49, + "options": { + "footer": { + "fields": "", + "reducer": [ + "sum" + ], + "show": false + }, + "showHeader": true + }, + "pluginVersion": "8.5.0", + "targets": [ + { + "datasource": "${DS_PROMETHEUS}", + "expr": "trilio_consistentset_info{ consistentset=~\"$ConsistentSet\",install_namespace=~\"$Install_Namespace\",cluster=~\"$Cluster\",kind=\"ConsistentSet\"}", + "format": "table", + "instant": true, + "interval": "", + "legendFormat": "", + "refId": "A" + } + ], + "title": "Details", + "transformations": [ + { + "id": "filterFieldsByName", + "options": { + "include": { + "names": [ + "Time", + "completion_ts", + "size", + "start_ts", + "sourcebackupplan", + "sourceinstanceinfo", + "backupName", + "backupNamespace", + "backupStatus" + ] + } + } + }, + { + "id": "reduce", + "options": { + "reducers": [ + "first" + ] + } + } + ], + "type": "table" + } + ], + "refresh": "10s", + "schemaVersion": 36, + "style": "dark", + "tags": [], + "templating": { + "list": [ + { + "current": { + "selected": false, + "text": "Prometheus", + "value": "Prometheus" + }, + "hide": 2, + "includeAll": false, + "label": "datasource", + "multi": false, + "name": "DS_PROMETHEUS", + "options": [], + "query": "prometheus", + "refresh": 1, + "regex": "", + "skipUrlSync": false, + "type": "datasource" + }, + { + "current": { + "selected": false, + "text": "Loki", + "value": "Loki" + }, + "description": "loki datasource", + "hide": 2, + "includeAll": false, + "label": "datasource", + "multi": false, + "name": "DS_LOKI", + "options": [], + "query": "loki", + "refresh": 1, + "regex": "", + "skipUrlSync": false, + "type": "datasource" + }, + { + "current": {}, + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "definition": "trilio_system_info", + "hide": 0, + "includeAll": false, + "multi": false, + "name": "Cluster", + "options": [], + "query": { + "query": "trilio_system_info", + "refId": "Prometheus-Cluster-Variable-Query" + }, + "refresh": 1, + "regex": "/.*cluster=\"([^\"]*).*/", + "skipUrlSync": false, + "sort": 0, + "tagValuesQuery": "", + "tagsQuery": "", + "type": "query", + "useTags": false + }, + { + "current": {}, + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "definition": "trilio_system_info{cluster=~\"$Cluster\"}", + "hide": 2, + "includeAll": false, + "multi": false, + "name": "Scope", + "options": [], + "query": { + "query": "trilio_system_info{cluster=~\"$Cluster\"}", + "refId": "Prometheus-Scope-Variable-Query" + }, + "refresh": 1, + "regex": "/.*scope=\"([^\"]*).*/", + "skipUrlSync": false, + "sort": 0, + "tagValuesQuery": "", + "tagsQuery": "", + "type": "query", + "useTags": false + }, + { + "current": {}, + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "definition": "trilio_system_info{scope=~\"$Scope\",cluster=~\"$Cluster\"}", + "hide": 0, + "includeAll": false, + "label": "Install Namespace", + "multi": false, + "name": "Install_Namespace", + "options": [], + "query": { + "query": "trilio_system_info{scope=~\"$Scope\",cluster=~\"$Cluster\"}", + "refId": "Prometheus-Install_Namespace-Variable-Query" + }, + "refresh": 2, + "regex": "/.*install_namespace=\"([^\"]*).*/", + "skipUrlSync": false, + "sort": 0, + "tagValuesQuery": "", + "tagsQuery": "", + "type": "query", + "useTags": false + }, + { + "current": {}, + "datasource": { + "type": "loki", + "uid": "${DS_LOKI}" + }, + "definition": "label_values({child_transaction_resource_name=\"$ConsistentSet\",child_transaction_type=\"ConsistentSet\",transaction_resource_name=\"$ContinuousRestorePlan\",transaction_type=\"ContinuousRestorePlan\"}, service_type)", + "description": "Service Type", + "hide": 0, + "includeAll": true, + "label": "Service Type", + "multi": false, + "name": "service_type", + "options": [], + "query": "label_values({child_transaction_resource_name=\"$ConsistentSet\",child_transaction_type=\"ConsistentSet\",transaction_resource_name=\"$ContinuousRestorePlan\",transaction_type=\"ContinuousRestorePlan\"}, service_type)", + "refresh": 2, + "regex": "", + "skipUrlSync": false, + "sort": 0, + "type": "query" + }, + { + "current": {}, + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "definition": "trilio_consistentset_info{cluster=~\"$Cluster\",install_namespace=~\"$Install_Namespace\",kind=\"ConsistentSet\"}", + "hide": 0, + "includeAll": false, + "label": "ConsistentSet", + "multi": false, + "name": "ConsistentSet", + "options": [], + "query": { + "query": "trilio_consistentset_info{cluster=~\"$Cluster\",install_namespace=~\"$Install_Namespace\",kind=\"ConsistentSet\"}", + "refId": "StandardVariableQuery" + }, + "refresh": 2, + "regex": "/.*consistentset=\"([^\"]*).*/", + "skipUrlSync": false, + "sort": 0, + "tagValuesQuery": "", + "tagsQuery": "", + "type": "query", + "useTags": false + }, + { + "current": {}, + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "definition": "trilio_continuousrestoreplan_info{cluster=~\"$Cluster\",kind=\"ContinuousRestorePlan\"}", + "hide": 0, + "includeAll": false, + "label": "ContinuousRestorePlan", + "multi": false, + "name": "ContinuousRestorePlan", + "options": [], + "query": { + "query": "trilio_continuousrestoreplan_info{cluster=~\"$Cluster\",kind=\"ContinuousRestorePlan\"}", + "refId": "StandardVariableQuery" + }, + "refresh": 1, + "regex": "/.*continuousrestoreplan=\"([^\"]*).*/", + "skipUrlSync": false, + "sort": 0, + "type": "query" + } + ] + }, + "time": { + "from": "now-15m", + "to": "now" + }, + "timepicker": { + "refresh_intervals": [ + "10s", + "30s", + "1m", + "5m", + "15m", + "30m", + "1h", + "2h", + "1d" + ] + }, + "timezone": "", + "title": "ConsistentSet Detail", + "uid": "ConsistentSet", + "version": 1 +} \ No newline at end of file diff --git a/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/visualization/charts/grafana/dashboards/continuousrestoreplan-detail.json b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/visualization/charts/grafana/dashboards/continuousrestoreplan-detail.json new file mode 100644 index 000000000..02e201867 --- /dev/null +++ b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/visualization/charts/grafana/dashboards/continuousrestoreplan-detail.json @@ -0,0 +1,1052 @@ +{ + "__inputs": [ + { + "name": "DS_PROMETHEUS", + "label": "Prometheus", + "description": "", + "type": "datasource", + "pluginId": "prometheus", + "pluginName": "Prometheus" + }, + { + "name": "DS_LOKI", + "label": "Loki", + "description": "", + "type": "datasource", + "pluginId": "loki", + "pluginName": "Loki" + } + ], + "__elements": [], + "__requires": [ + { + "type": "grafana", + "id": "grafana", + "name": "Grafana", + "version": "8.5.0" + }, + { + "type": "panel", + "id": "logs", + "name": "Logs", + "version": "" + }, + { + "type": "datasource", + "id": "loki", + "name": "Loki", + "version": "1.0.0" + }, + { + "type": "datasource", + "id": "prometheus", + "name": "Prometheus", + "version": "1.0.0" + }, + { + "type": "panel", + "id": "stat", + "name": "Stat", + "version": "" + }, + { + "type": "panel", + "id": "table", + "name": "Table", + "version": "" + }, + { + "type": "panel", + "id": "text", + "name": "Text", + "version": "" + } + ], + "annotations": { + "list": [ + { + "$$hashKey": "object:4254", + "builtIn": 1, + "datasource": { + "type": "datasource", + "uid": "grafana" + }, + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "limit": 100, + "name": "Annotations & Alerts", + "showIn": 0, + "target": { + "limit": 100, + "matchAny": false, + "tags": [], + "type": "dashboard" + }, + "type": "dashboard" + } + ] + }, + "editable": true, + "fiscalYearStartMonth": 0, + "gnetId": 12605, + "graphTooltip": 0, + "id": null, + "iteration": 1672673323255, + "links": [], + "liveNow": false, + "panels": [ + { + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "gridPos": { + "h": 2, + "w": 23, + "x": 0, + "y": 0 + }, + "id": 4, + "options": { + "content": "

ContinuousRestorePlan Detail

", + "mode": "html" + }, + "pluginVersion": "8.5.0", + "transparent": true, + "type": "text" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "mappings": [ + { + "options": { + "0": { + "text": "InProgress" + }, + "1": { + "text": "Available" + }, + "-1": { + "text": "Failed" + }, + "-2": { + "text": "UnKnown" + } + }, + "type": "value" + } + ], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "red", + "value": null + }, + { + "color": "dark-red", + "value": -1 + }, + { + "color": "blue", + "value": 0 + }, + { + "color": "green", + "value": 1 + } + ] + }, + "unit": "short" + }, + "overrides": [] + }, + "gridPos": { + "h": 3, + "w": 3, + "x": 0, + "y": 2 + }, + "id": 16, + "links": [], + "maxDataPoints": 100, + "options": { + "colorMode": "value", + "fieldOptions": { + "calcs": [ + "mean" + ] + }, + "graphMode": "none", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "first" + ], + "fields": "", + "values": false + }, + "text": { + "valueSize": 30 + }, + "textMode": "auto" + }, + "pluginVersion": "8.5.0", + "targets": [ + { + "datasource": "${DS_PROMETHEUS}", + "expr": "trilio_continuousrestoreplan_info{continuousrestoreplan=~\"$ContinuousRestorePlan\",install_namespace=~\"$Install_Namespace\",cluster=~\"$Cluster\",kind=\"ContinuousRestorePlan\"}", + "format": "time_series", + "instant": true, + "interval": "", + "intervalFactor": 1, + "legendFormat": "", + "refId": "A" + } + ], + "title": "Status", + "transparent": true, + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [ + { + "options": { + "match": "null", + "result": { + "text": "N/A" + } + }, + "type": "special" + } + ], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "none" + }, + "overrides": [] + }, + "gridPos": { + "h": 3, + "w": 4, + "x": 3, + "y": 2 + }, + "id": 19, + "links": [], + "maxDataPoints": 100, + "options": { + "colorMode": "none", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "horizontal", + "reduceOptions": { + "calcs": [ + "first" + ], + "fields": "/^sourcebackupplan$/", + "values": false + }, + "text": { + "valueSize": 30 + }, + "textMode": "auto" + }, + "pluginVersion": "8.5.0", + "targets": [ + { + "datasource": "${DS_PROMETHEUS}", + "expr": "trilio_continuousrestoreplan_info{ continuousrestoreplan=~\"$ContinuousRestorePlan\",install_namespace=~\"$Install_Namespace\",cluster=~\"$Cluster\",kind=\"ContinuousRestorePlan\"}", + "format": "table", + "instant": true, + "interval": "", + "legendFormat": "", + "refId": "A" + } + ], + "title": "Source BackupPlan", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [ + { + "options": { + "match": "null", + "result": { + "text": "N/A" + } + }, + "type": "special" + } + ], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "none" + }, + "overrides": [] + }, + "gridPos": { + "h": 3, + "w": 4, + "x": 7, + "y": 2 + }, + "id": 21, + "links": [], + "maxDataPoints": 100, + "options": { + "colorMode": "none", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "horizontal", + "reduceOptions": { + "calcs": [ + "first" + ], + "fields": "/^sourceinstanceinfo$/", + "values": false + }, + "text": { + "valueSize": 30 + }, + "textMode": "auto" + }, + "pluginVersion": "8.5.0", + "targets": [ + { + "datasource": "${DS_PROMETHEUS}", + "expr": "trilio_continuousrestoreplan_info{ continuousrestoreplan=~\"$ContinuousRestorePlan\",install_namespace=~\"$Install_Namespace\",cluster=~\"$Cluster\",kind=\"ContinuousRestorePlan\"}", + "format": "table", + "instant": true, + "interval": "", + "legendFormat": "", + "refId": "A" + } + ], + "title": "Source Instance", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [ + { + "options": { + "match": "null", + "result": { + "text": "0" + } + }, + "type": "special" + } + ], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "none" + }, + "overrides": [] + }, + "gridPos": { + "h": 3, + "w": 3, + "x": 11, + "y": 2 + }, + "id": 13, + "links": [], + "maxDataPoints": 100, + "options": { + "colorMode": "none", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "horizontal", + "reduceOptions": { + "calcs": [ + "first" + ], + "fields": "/^consistentsetcount$/", + "values": false + }, + "textMode": "auto" + }, + "pluginVersion": "8.5.0", + "targets": [ + { + "datasource": "${DS_PROMETHEUS}", + "expr": "trilio_continuousrestoreplan_info{ continuousrestoreplan=~\"$ContinuousRestorePlan\",install_namespace=~\"$Install_Namespace\",cluster=~\"$Cluster\",kind=\"ContinuousRestorePlan\"}", + "format": "table", + "instant": true, + "interval": "", + "legendFormat": "", + "refId": "A" + } + ], + "title": "Num of ConsistentSets", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [ + { + "options": { + "match": "null", + "result": { + "text": "N/A" + } + }, + "type": "special" + } + ], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "none" + }, + "overrides": [] + }, + "gridPos": { + "h": 3, + "w": 5, + "x": 14, + "y": 2 + }, + "id": 20, + "links": [], + "maxDataPoints": 100, + "options": { + "colorMode": "none", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "horizontal", + "reduceOptions": { + "calcs": [ + "first" + ], + "fields": "/^continuousrestorepolicy$/", + "values": false + }, + "text": { + "valueSize": 10 + }, + "textMode": "auto" + }, + "pluginVersion": "8.5.0", + "targets": [ + { + "datasource": "${DS_PROMETHEUS}", + "expr": "trilio_continuousrestoreplan_info{ continuousrestoreplan=~\"$ContinuousRestorePlan\",install_namespace=~\"$Install_Namespace\",cluster=~\"$Cluster\",kind=\"ContinuousRestorePlan\"}", + "format": "table", + "instant": true, + "interval": "", + "legendFormat": "", + "refId": "A" + } + ], + "title": "ContinuousRestorePolicy", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [ + { + "options": { + "match": "null", + "result": { + "text": "N/A" + } + }, + "type": "special" + } + ], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "none" + }, + "overrides": [] + }, + "gridPos": { + "h": 3, + "w": 5, + "x": 19, + "y": 2 + }, + "id": 14, + "links": [], + "maxDataPoints": 100, + "options": { + "colorMode": "none", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "horizontal", + "reduceOptions": { + "calcs": [ + "first" + ], + "fields": "/^target$/", + "values": false + }, + "text": { + "valueSize": 10 + }, + "textMode": "auto" + }, + "pluginVersion": "8.5.0", + "targets": [ + { + "datasource": "${DS_PROMETHEUS}", + "expr": "trilio_continuousrestoreplan_info{ continuousrestoreplan=~\"$ContinuousRestorePlan\",install_namespace=~\"$Install_Namespace\",cluster=~\"$Cluster\",kind=\"ContinuousRestorePlan\"}", + "format": "table", + "instant": true, + "interval": "", + "legendFormat": "", + "refId": "A" + } + ], + "title": "Target", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "custom": { + "align": "auto", + "displayMode": "auto", + "inspect": false + }, + "decimals": 2, + "displayName": "", + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "short" + }, + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "consistentset" + }, + "properties": [ + { + "id": "displayName", + "value": "ConsistentSet" + }, + { + "id": "unit", + "value": "short" + }, + { + "id": "decimals", + "value": 2 + }, + { + "id": "links", + "value": [ + { + "targetBlank": false, + "title": "ConsistentSet Detail", + "url": "/d/${__data.fields.kind}/?refresh=5s&var-ConsistentSet=${__value.text}&var-Cluster=${Cluster}&var-Install_Namespace=${Install_Namespace}" + } + ] + }, + { + "id": "custom.align", + "value": "left" + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "consistentsetscope" + }, + "properties": [ + { + "id": "displayName", + "value": "ConsistentSet Scope" + }, + { + "id": "unit", + "value": "short" + }, + { + "id": "decimals", + "value": 2 + }, + { + "id": "custom.align", + "value": "left" + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "size" + }, + "properties": [ + { + "id": "displayName", + "value": "Size" + }, + { + "id": "unit", + "value": "decbytes" + }, + { + "id": "custom.align", + "value": "left" + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "status" + }, + "properties": [ + { + "id": "displayName", + "value": "Status" + }, + { + "id": "unit", + "value": "short" + }, + { + "id": "decimals", + "value": 2 + }, + { + "id": "custom.align" + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "sourcebackupplan" + }, + "properties": [ + { + "id": "displayName", + "value": "Source BackupPlan" + }, + { + "id": "unit", + "value": "short" + }, + { + "id": "decimals", + "value": 2 + }, + { + "id": "custom.align", + "value": "left" + } + ] + } + ] + }, + "gridPos": { + "h": 12, + "w": 12, + "x": 0, + "y": 5 + }, + "id": 12, + "options": { + "footer": { + "fields": "", + "reducer": [ + "sum" + ], + "show": false + }, + "showHeader": true, + "sortBy": [] + }, + "pluginVersion": "8.5.0", + "targets": [ + { + "datasource": "${DS_PROMETHEUS}", + "expr": "trilio_consistentset_info{continuousrestoreplan=~\"$ContinuousRestorePlan\",install_namespace=~\"$Install_Namespace\",cluster=~\"$Cluster\",kind=\"ConsistentSet\"}", + "format": "table", + "instant": true, + "interval": "", + "intervalFactor": 1, + "legendFormat": "", + "refId": "A" + } + ], + "title": "ConsistentSets", + "transformations": [ + { + "id": "merge", + "options": { + "reducers": [] + } + }, + { + "id": "filterFieldsByName", + "options": { + "include": { + "names": [ + "consistentset", + "consistentsetscope", + "size", + "sourcebackupplan", + "status", + "kind" + ] + } + } + } + ], + "type": "table" + }, + { + "datasource": { + "type": "loki", + "uid": "${DS_LOKI}" + }, + "description": "ContinuousRestorePlan Logs", + "gridPos": { + "h": 12, + "w": 12, + "x": 12, + "y": 5 + }, + "id": 18, + "options": { + "dedupStrategy": "none", + "enableLogDetails": true, + "prettifyLogMessage": false, + "showCommonLabels": false, + "showLabels": false, + "showTime": true, + "sortOrder": "Descending", + "wrapLogMessage": false + }, + "targets": [ + { + "datasource": { + "type": "loki", + "uid": "${DS_LOKI}" + }, + "expr": "{transaction_type=\"ContinuousRestorePlan\",transaction_resource_name=~\"$ContinuousRestorePlan\",service_type=~\"$service_type\"}", + "refId": "A" + } + ], + "title": "ContinuousRestorePlan Logs", + "type": "logs" + } + ], + "refresh": "10s", + "schemaVersion": 36, + "style": "dark", + "tags": [], + "templating": { + "list": [ + { + "current": { + "selected": false, + "text": "Prometheus", + "value": "Prometheus" + }, + "hide": 2, + "includeAll": false, + "label": "datasource", + "multi": false, + "name": "DS_PROMETHEUS", + "options": [], + "query": "prometheus", + "refresh": 1, + "regex": "", + "skipUrlSync": false, + "type": "datasource" + }, + { + "current": { + "selected": false, + "text": "Loki", + "value": "Loki" + }, + "hide": 2, + "includeAll": false, + "label": "loki", + "multi": false, + "name": "DS_LOKI", + "options": [], + "query": "loki", + "refresh": 1, + "regex": "", + "skipUrlSync": false, + "type": "datasource" + }, + { + "current": {}, + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "definition": "trilio_system_info", + "hide": 0, + "includeAll": false, + "multi": false, + "name": "Cluster", + "options": [], + "query": { + "query": "trilio_system_info", + "refId": "Prometheus-Cluster-Variable-Query" + }, + "refresh": 1, + "regex": "/.*cluster=\"([^\"]*).*/", + "skipUrlSync": false, + "sort": 0, + "tagValuesQuery": "", + "tagsQuery": "", + "type": "query", + "useTags": false + }, + { + "current": {}, + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "definition": "trilio_system_info{cluster=~\"$Cluster\"}", + "hide": 2, + "includeAll": false, + "multi": false, + "name": "Scope", + "options": [], + "query": { + "query": "trilio_system_info{cluster=~\"$Cluster\"}", + "refId": "Prometheus-Scope-Variable-Query" + }, + "refresh": 1, + "regex": "/.*scope=\"([^\"]*).*/", + "skipUrlSync": false, + "sort": 0, + "tagValuesQuery": "", + "tagsQuery": "", + "type": "query", + "useTags": false + }, + { + "current": {}, + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "definition": "trilio_system_info{scope=~\"$Scope\",cluster=~\"$Cluster\"}", + "hide": 0, + "includeAll": false, + "label": "Install Namespace", + "multi": false, + "name": "Install_Namespace", + "options": [], + "query": { + "query": "trilio_system_info{scope=~\"$Scope\",cluster=~\"$Cluster\"}", + "refId": "Prometheus-Install_Namespace-Variable-Query" + }, + "refresh": 2, + "regex": "/.*install_namespace=\"([^\"]*).*/", + "skipUrlSync": false, + "sort": 0, + "tagValuesQuery": "", + "tagsQuery": "", + "type": "query", + "useTags": false + }, + { + "current": {}, + "datasource": { + "type": "loki", + "uid": "${DS_LOKI}" + }, + "definition": "label_values({transaction_type=\"ContinuousRestorePlan\",transaction_resource_name=~\"$ContinuousRestorePlan\"}, service_type)", + "description": "Service Type", + "hide": 0, + "includeAll": true, + "label": "Service Type", + "multi": false, + "name": "service_type", + "options": [], + "query": "label_values({transaction_type=\"ContinuousRestorePlan\",transaction_resource_name=~\"$ContinuousRestorePlan\"}, service_type)", + "refresh": 2, + "regex": "", + "skipUrlSync": false, + "sort": 0, + "type": "query" + }, + { + "current": {}, + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "definition": "trilio_continuousrestoreplan_info{cluster=~\"$Cluster\",kind=\"ContinuousRestorePlan\"}", + "hide": 0, + "includeAll": false, + "label": "ContinuousRestorePlan", + "multi": false, + "name": "ContinuousRestorePlan", + "options": [], + "query": { + "query": "trilio_continuousrestoreplan_info{cluster=~\"$Cluster\",kind=\"ContinuousRestorePlan\"}", + "refId": "StandardVariableQuery" + }, + "refresh": 2, + "regex": "/.*continuousrestoreplan=\"([^\"]*).*/", + "skipUrlSync": false, + "sort": 0, + "tagValuesQuery": "", + "tagsQuery": "", + "type": "query", + "useTags": false + } + ] + }, + "time": { + "from": "now-15m", + "to": "now" + }, + "timepicker": { + "refresh_intervals": [ + "10s", + "30s", + "1m", + "5m", + "15m", + "30m", + "1h", + "2h", + "1d" + ] + }, + "timezone": "", + "title": "ContinuousRestorePlan Detail", + "uid": "ContinuousRestorePlan", + "version": 1 +} \ No newline at end of file diff --git a/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/visualization/charts/grafana/dashboards/logging-dashboard.json b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/visualization/charts/grafana/dashboards/logging-dashboard.json new file mode 100644 index 000000000..3eaae8683 --- /dev/null +++ b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/visualization/charts/grafana/dashboards/logging-dashboard.json @@ -0,0 +1,212 @@ +{ + "__inputs": [ + { + "name": "DS_LOKI", + "label": "Loki", + "description": "", + "type": "datasource", + "pluginId": "loki", + "pluginName": "Loki" + } + ], + "__elements": [], + "__requires": [ + { + "type": "grafana", + "id": "grafana", + "name": "Grafana", + "version": "8.5.0" + }, + { + "type": "panel", + "id": "logs", + "name": "Logs", + "version": "" + }, + { + "type": "datasource", + "id": "loki", + "name": "Loki", + "version": "1.0.0" + } + ], + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": { + "type": "datasource", + "uid": "grafana" + }, + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "target": { + "limit": 100, + "matchAny": false, + "tags": [], + "type": "dashboard" + }, + "type": "dashboard" + } + ] + }, + "description": "dashboard for logging", + "editable": true, + "fiscalYearStartMonth": 0, + "gnetId": 12611, + "graphTooltip": 0, + "id": null, + "iteration": 1655397917396, + "links": [], + "liveNow": false, + "panels": [ + { + "datasource": { + "type": "loki", + "uid": "${DS_LOKI}" + }, + "description": "Live logs is a like 'tail -f' in a real time", + "gridPos": { + "h": 19, + "w": 24, + "x": 0, + "y": 0 + }, + "id": 2, + "options": { + "dedupStrategy": "exact", + "enableLogDetails": true, + "prettifyLogMessage": false, + "showCommonLabels": false, + "showLabels": false, + "showTime": true, + "sortOrder": "Descending", + "wrapLogMessage": false + }, + "targets": [ + { + "datasource": { + "type": "loki", + "uid": "${DS_LOKI}" + }, + "expr": "{service_type=~\"$service_type\"}", + "queryType": "range", + "refId": "A" + } + ], + "title": "Live logs", + "type": "logs" + } + ], + "refresh": "", + "schemaVersion": 36, + "style": "dark", + "tags": [ + "logging" + ], + "templating": { + "list": [ + { + "current": { + "selected": true, + "text": "All", + "value": "$__all" + }, + "hide": 0, + "includeAll": true, + "label": "Service Type", + "multi": false, + "name": "service_type", + "options": [ + { + "selected": true, + "text": "All", + "value": "$__all" + }, + { + "selected": false, + "text": "ControlPlane", + "value": "ControlPlane" + }, + { + "selected": false, + "text": "WebhookServer", + "value": "WebhookServer" + }, + { + "selected": false, + "text": "Exporter", + "value": "Exporter" + }, + { + "selected": false, + "text": "ResourceCleaner", + "value": "ResourceCleaner" + }, + { + "selected": false, + "text": "WebBackend", + "value": "WebBackend" + }, + { + "selected": false, + "text": "Analyzer", + "value": "Analyzer" + }, + { + "selected": false, + "text": "DexInit", + "value": "DexInit" + } + ], + "query": "ControlPlane,WebhookServer,Exporter,ResourceCleaner,WebBackend,Analyzer,DexInit", + "queryValue": "", + "skipUrlSync": false, + "type": "custom" + }, + { + "current": { + "selected": false, + "text": "Loki", + "value": "Loki" + }, + "description": "loki datasource", + "hide": 2, + "includeAll": false, + "label": "loki datasource", + "multi": false, + "name": "DS_LOKI", + "options": [], + "query": "loki", + "refresh": 1, + "regex": "", + "skipUrlSync": false, + "type": "datasource" + } + ] + }, + "time": { + "from": "now-30m", + "to": "now" + }, + "timepicker": { + "refresh_intervals": [ + "10s", + "30s", + "1m", + "5m", + "15m", + "30m", + "1h", + "2h", + "1d" + ] + }, + "timezone": "", + "title": "Logging Dashboard", + "uid": "logging", + "version": 1, + "weekStart": "" +} \ No newline at end of file diff --git a/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/visualization/charts/grafana/dashboards/metadata-detail.json b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/visualization/charts/grafana/dashboards/metadata-detail.json new file mode 100644 index 000000000..02a6fb1cc --- /dev/null +++ b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/visualization/charts/grafana/dashboards/metadata-detail.json @@ -0,0 +1,889 @@ +{ + "__inputs": [ + { + "name": "DS_PROMETHEUS", + "label": "Prometheus", + "description": "", + "type": "datasource", + "pluginId": "prometheus", + "pluginName": "Prometheus" + } + ], + "__requires": [ + { + "type": "grafana", + "id": "grafana", + "name": "Grafana", + "version": "7.2.1" + }, + { + "type": "datasource", + "id": "prometheus", + "name": "Prometheus", + "version": "1.0.0" + }, + { + "type": "panel", + "id": "stat", + "name": "Stat", + "version": "" + }, + { + "type": "panel", + "id": "table-old", + "name": "Table (old)", + "version": "" + }, + { + "type": "panel", + "id": "text", + "name": "Text", + "version": "7.1.0" + } + ], + "annotations": { + "list": [ + { + "$$hashKey": "object:20", + "builtIn": 1, + "datasource": "-- Grafana --", + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "limit": 100, + "name": "Annotations & Alerts", + "showIn": 0, + "type": "dashboard" + } + ] + }, + "editable": true, + "gnetId": 12607, + "graphTooltip": 0, + "id": null, + "iteration": 1617300281944, + "links": [], + "panels": [ + { + "content": "

Backup Metatdata

", + "datasource": "${DS_PROMETHEUS}", + "fieldConfig": { + "defaults": { + "custom": {} + }, + "overrides": [] + }, + "gridPos": { + "h": 2, + "w": 12, + "x": 0, + "y": 0 + }, + "id": 58, + "mode": "html", + "options": { + "content": "

Backup Metatdata

", + "mode": "html" + }, + "pluginVersion": "7.1.0", + "timeFrom": null, + "timeShift": null, + "title": "", + "transparent": true, + "type": "text" + }, + { + "content": "

Restore Metatdata

", + "datasource": "${DS_PROMETHEUS}", + "fieldConfig": { + "defaults": { + "custom": {} + }, + "overrides": [] + }, + "gridPos": { + "h": 2, + "w": 12, + "x": 12, + "y": 0 + }, + "id": 59, + "mode": "html", + "options": { + "content": "

Restore Metatdata

", + "mode": "html" + }, + "pluginVersion": "7.1.0", + "timeFrom": null, + "timeShift": null, + "title": "", + "transparent": true, + "type": "text" + }, + { + "datasource": "${DS_PROMETHEUS}", + "fieldConfig": { + "defaults": { + "custom": {}, + "mappings": [], + "noValue": "0", + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "rgb(191, 194, 191)", + "value": null + } + ] + }, + "unit": "short" + }, + "overrides": [] + }, + "gridPos": { + "h": 3, + "w": 4, + "x": 2, + "y": 2 + }, + "id": 53, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "first" + ], + "fields": "", + "values": false + }, + "textMode": "auto" + }, + "pluginVersion": "7.2.1", + "targets": [ + { + "expr": "count(avg(trilio_backup_metadata_info{backup=~\"$Backup\",install_namespace=~\"$Install_Namespace\",cluster=~\"$Cluster\"}) by (objecttype))", + "instant": true, + "interval": "", + "legendFormat": "", + "refId": "A" + } + ], + "timeFrom": null, + "timeShift": null, + "title": "Total Component Type", + "type": "stat" + }, + { + "datasource": "${DS_PROMETHEUS}", + "fieldConfig": { + "defaults": { + "custom": {}, + "mappings": [], + "noValue": "0", + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "rgb(191, 194, 191)", + "value": null + } + ] + }, + "unit": "short" + }, + "overrides": [] + }, + "gridPos": { + "h": 3, + "w": 4, + "x": 6, + "y": 2 + }, + "id": 54, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "first" + ], + "fields": "", + "values": false + }, + "textMode": "auto" + }, + "pluginVersion": "7.2.1", + "targets": [ + { + "expr": "count(avg(trilio_backup_metadata_info{backup=~\"$Backup\",install_namespace=~\"$Install_Namespace\",cluster=~\"$Cluster\"}) by (objectname))", + "instant": true, + "interval": "", + "legendFormat": "", + "refId": "A" + } + ], + "timeFrom": null, + "timeShift": null, + "title": "Total Components", + "type": "stat" + }, + { + "datasource": "${DS_PROMETHEUS}", + "fieldConfig": { + "defaults": { + "custom": {}, + "mappings": [], + "noValue": "0", + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "rgb(191, 194, 191)", + "value": null + } + ] + }, + "unit": "short" + }, + "overrides": [] + }, + "gridPos": { + "h": 3, + "w": 4, + "x": 14, + "y": 2 + }, + "id": 55, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "first" + ], + "fields": "", + "values": false + }, + "textMode": "auto" + }, + "pluginVersion": "7.2.1", + "targets": [ + { + "expr": "count(avg(trilio_restore_metadata_info{restore=~\"$Restore\",install_namespace=~\"$Install_Namespace\",cluster=~\"$Cluster\"}) by (objecttype))", + "instant": true, + "interval": "", + "legendFormat": "", + "refId": "A" + } + ], + "timeFrom": null, + "timeShift": null, + "title": "Total Component Type", + "type": "stat" + }, + { + "datasource": "${DS_PROMETHEUS}", + "fieldConfig": { + "defaults": { + "custom": {}, + "mappings": [], + "noValue": "0", + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "rgb(191, 194, 191)", + "value": null + } + ] + }, + "unit": "short" + }, + "overrides": [] + }, + "gridPos": { + "h": 3, + "w": 4, + "x": 18, + "y": 2 + }, + "id": 56, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "first" + ], + "fields": "", + "values": false + }, + "textMode": "auto" + }, + "pluginVersion": "7.2.1", + "targets": [ + { + "expr": "count(avg(trilio_restore_metadata_info{restore=~\"$Restore\",install_namespace=~\"$Install_Namespace\",cluster=~\"$Cluster\"}) by (objectname))", + "instant": true, + "interval": "", + "legendFormat": "", + "refId": "A" + } + ], + "timeFrom": null, + "timeShift": null, + "title": "Total Components", + "type": "stat" + }, + { + "columns": [], + "datasource": "${DS_PROMETHEUS}", + "description": "", + "fieldConfig": { + "defaults": { + "custom": {} + }, + "overrides": [] + }, + "fontSize": "100%", + "gridPos": { + "h": 15, + "w": 12, + "x": 0, + "y": 5 + }, + "id": 42, + "links": [], + "pageSize": null, + "showHeader": true, + "sort": { + "col": 18, + "desc": true + }, + "styles": [ + { + "$$hashKey": "object:10447", + "alias": "Object Type", + "align": "auto", + "dateFormat": "YYYY-MM-DD HH:mm:ss", + "link": false, + "linkTooltip": "Show Metadata Details", + "linkUrl": "/d/0aiPMQMGk/metadata-detail?refresh=5s&var-Backup=${Backup}&var-ObjectType=${__cell}", + "mappingType": 1, + "pattern": "objecttype", + "type": "string" + }, + { + "$$hashKey": "object:1072", + "alias": "Source", + "align": "auto", + "colorMode": null, + "colors": [ + "rgba(245, 54, 54, 0.9)", + "rgba(237, 129, 40, 0.89)", + "rgba(50, 172, 45, 0.97)" + ], + "dateFormat": "YYYY-MM-DD HH:mm:ss", + "decimals": 2, + "mappingType": 1, + "pattern": "applicationtype", + "thresholds": [], + "type": "string", + "unit": "short" + }, + { + "$$hashKey": "object:1249", + "alias": "API Version", + "align": "auto", + "colorMode": null, + "colors": [ + "rgba(245, 54, 54, 0.9)", + "rgba(237, 129, 40, 0.89)", + "rgba(50, 172, 45, 0.97)" + ], + "dateFormat": "YYYY-MM-DD HH:mm:ss", + "decimals": null, + "mappingType": 1, + "pattern": "apiversion", + "thresholds": [], + "type": "number", + "unit": "short" + }, + { + "$$hashKey": "object:3063", + "alias": "Object Name", + "align": "auto", + "colorMode": null, + "colors": [ + "rgba(245, 54, 54, 0.9)", + "rgba(237, 129, 40, 0.89)", + "rgba(50, 172, 45, 0.97)" + ], + "dateFormat": "YYYY-MM-DD HH:mm:ss", + "decimals": 2, + "mappingType": 1, + "pattern": "objectname", + "thresholds": [], + "type": "number", + "unit": "short" + }, + { + "$$hashKey": "object:3158", + "alias": "", + "align": "auto", + "colorMode": null, + "colors": [ + "rgba(245, 54, 54, 0.9)", + "rgba(237, 129, 40, 0.89)", + "rgba(50, 172, 45, 0.97)" + ], + "dateFormat": "YYYY-MM-DD HH:mm:ss", + "decimals": 2, + "mappingType": 1, + "pattern": "", + "thresholds": [], + "type": "number", + "unit": "short" + }, + { + "$$hashKey": "object:10448", + "alias": "", + "align": "right", + "colorMode": null, + "colors": [ + "rgba(245, 54, 54, 0.9)", + "rgba(237, 129, 40, 0.89)", + "rgba(50, 172, 45, 0.97)" + ], + "decimals": 2, + "pattern": "/.*/", + "thresholds": [], + "type": "hidden", + "unit": "short" + } + ], + "targets": [ + { + "expr": "avg(trilio_backup_metadata_info{backup=~\"$Backup\",install_namespace=~\"$Install_Namespace\",cluster=~\"$Cluster\"}) by (objectname,objecttype, apiversion)", + "format": "table", + "instant": true, + "interval": "", + "legendFormat": "", + "refId": "A" + } + ], + "timeFrom": null, + "timeShift": null, + "title": "Metadata Info", + "transform": "table", + "type": "table-old" + }, + { + "columns": [], + "datasource": "${DS_PROMETHEUS}", + "description": "", + "fieldConfig": { + "defaults": { + "custom": {} + }, + "overrides": [] + }, + "fontSize": "100%", + "gridPos": { + "h": 15, + "w": 12, + "x": 12, + "y": 5 + }, + "id": 60, + "links": [], + "pageSize": null, + "showHeader": true, + "sort": { + "col": 18, + "desc": true + }, + "styles": [ + { + "$$hashKey": "object:10447", + "alias": "Object Type", + "align": "auto", + "dateFormat": "YYYY-MM-DD HH:mm:ss", + "link": false, + "linkTooltip": "Show Metadata Details", + "linkUrl": "/d/0aiPMQMGk/metadata-detail?refresh=5s&var-Backup=${Backup}&var-ObjectType=${__cell}", + "mappingType": 1, + "pattern": "objecttype", + "type": "string" + }, + { + "$$hashKey": "object:1072", + "alias": "Source", + "align": "auto", + "colorMode": null, + "colors": [ + "rgba(245, 54, 54, 0.9)", + "rgba(237, 129, 40, 0.89)", + "rgba(50, 172, 45, 0.97)" + ], + "dateFormat": "YYYY-MM-DD HH:mm:ss", + "decimals": 2, + "mappingType": 1, + "pattern": "applicationtype", + "thresholds": [], + "type": "string", + "unit": "short" + }, + { + "$$hashKey": "object:1249", + "alias": "API Version", + "align": "auto", + "colorMode": null, + "colors": [ + "rgba(245, 54, 54, 0.9)", + "rgba(237, 129, 40, 0.89)", + "rgba(50, 172, 45, 0.97)" + ], + "dateFormat": "YYYY-MM-DD HH:mm:ss", + "decimals": null, + "mappingType": 1, + "pattern": "apiversion", + "thresholds": [], + "type": "number", + "unit": "short" + }, + { + "$$hashKey": "object:3063", + "alias": "Object Name", + "align": "auto", + "colorMode": null, + "colors": [ + "rgba(245, 54, 54, 0.9)", + "rgba(237, 129, 40, 0.89)", + "rgba(50, 172, 45, 0.97)" + ], + "dateFormat": "YYYY-MM-DD HH:mm:ss", + "decimals": 2, + "mappingType": 1, + "pattern": "objectname", + "thresholds": [], + "type": "number", + "unit": "short" + }, + { + "$$hashKey": "object:3158", + "alias": "", + "align": "auto", + "colorMode": null, + "colors": [ + "rgba(245, 54, 54, 0.9)", + "rgba(237, 129, 40, 0.89)", + "rgba(50, 172, 45, 0.97)" + ], + "dateFormat": "YYYY-MM-DD HH:mm:ss", + "decimals": 2, + "mappingType": 1, + "pattern": "", + "thresholds": [], + "type": "number", + "unit": "short" + }, + { + "$$hashKey": "object:10448", + "alias": "", + "align": "right", + "colorMode": null, + "colors": [ + "rgba(245, 54, 54, 0.9)", + "rgba(237, 129, 40, 0.89)", + "rgba(50, 172, 45, 0.97)" + ], + "decimals": 2, + "pattern": "/.*/", + "thresholds": [], + "type": "hidden", + "unit": "short" + } + ], + "targets": [ + { + "expr": "avg(trilio_restore_metadata_info{restore=~\"$Restore\",install_namespace=~\"$Install_Namespace\",cluster=~\"$Cluster\"}) by (objectname,objecttype, apiversion)", + "format": "table", + "instant": true, + "interval": "", + "legendFormat": "", + "refId": "A" + } + ], + "timeFrom": null, + "timeShift": null, + "title": "Metadata Info", + "transform": "table", + "type": "table-old" + }, + { + "cacheTimeout": null, + "content": "", + "datasource": "${DS_PROMETHEUS}", + "fieldConfig": { + "defaults": { + "custom": {}, + "mappings": [ + { + "from": "", + "id": 0, + "operator": "", + "text": "Available", + "to": "", + "type": 1, + "value": "1" + }, + { + "from": "", + "id": 1, + "operator": "", + "text": "InProgress", + "to": "", + "type": 1, + "value": "0" + }, + { + "from": "", + "id": 2, + "operator": "", + "text": "Failed", + "to": "", + "type": 1, + "value": "-1" + }, + { + "from": "", + "id": 3, + "operator": "", + "text": "UnKnown", + "to": "", + "type": 1, + "value": "-2" + } + ], + "nullValueMode": "connected", + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "red", + "value": null + }, + { + "color": "dark-red", + "value": -1 + }, + { + "color": "blue", + "value": 0 + }, + { + "color": "green", + "value": 1 + } + ] + }, + "unit": "short" + }, + "overrides": [] + }, + "gridPos": { + "h": 1, + "w": 4, + "x": 8, + "y": 20 + }, + "id": 50, + "interval": null, + "links": [], + "maxDataPoints": 100, + "mode": "markdown", + "options": { + "content": "", + "mode": "markdown" + }, + "pluginVersion": "7.1.0", + "targets": [ + { + "expr": "trilio_backup_info{backup=~\"$Backup\",namespace=~\"$Namespace\",cluster=~\"$Cluster\"}", + "format": "time_series", + "instant": true, + "interval": "", + "intervalFactor": 1, + "legendFormat": "{{status}}", + "refId": "A" + } + ], + "timeFrom": null, + "timeShift": null, + "title": "", + "transparent": true, + "type": "text" + } + ], + "refresh": "5s", + "schemaVersion": 26, + "style": "dark", + "tags": [ + "logging" + ], + "templating": { + "list": [ + { + "hide": 2, + "label": "datasource", + "name": "DS_PROMETHEUS", + "options": [], + "query": "prometheus", + "refresh": 1, + "regex": "", + "type": "datasource" + }, + { + "allValue": null, + "current": {}, + "datasource": "${DS_PROMETHEUS}", + "definition": "trilio_system_info", + "hide": 0, + "includeAll": false, + "label": null, + "multi": false, + "name": "Cluster", + "options": [], + "query": "trilio_system_info", + "refresh": 1, + "regex": "/.*cluster=\"([^\"]*).*/", + "skipUrlSync": false, + "sort": 0, + "tagValuesQuery": "", + "tags": [], + "tagsQuery": "", + "type": "query", + "useTags": false + }, + { + "allValue": null, + "current": {}, + "datasource": "${DS_PROMETHEUS}", + "definition": "trilio_system_info{cluster=~\"$Cluster\"}", + "hide": 2, + "includeAll": false, + "label": null, + "multi": false, + "name": "Scope", + "options": [], + "query": "trilio_system_info{cluster=~\"$Cluster\"}", + "refresh": 1, + "regex": "/.*scope=\"([^\"]*).*/", + "skipUrlSync": false, + "sort": 0, + "tagValuesQuery": "", + "tags": [], + "tagsQuery": "", + "type": "query", + "useTags": false + }, + { + "allValue": null, + "current": {}, + "datasource": "${DS_PROMETHEUS}", + "definition": "trilio_system_info{scope=~\"$Scope\",cluster=~\"$Cluster\"}", + "hide": 0, + "includeAll": false, + "label": "Install Namespace", + "multi": false, + "name": "Install_Namespace", + "options": [], + "query": "trilio_system_info{scope=~\"$Scope\",cluster=~\"$Cluster\"}", + "refresh": 1, + "regex": "/.*install_namespace=\"([^\"]*).*/", + "skipUrlSync": false, + "sort": 0, + "tagValuesQuery": "", + "tags": [], + "tagsQuery": "", + "type": "query", + "useTags": false + }, + { + "allValue": null, + "current": {}, + "datasource": "${DS_PROMETHEUS}", + "definition": "trilio_backup_info{cluster=~\"$Cluster\",install_namespace=~\"$Install_Namespace\"}", + "hide": 0, + "includeAll": false, + "label": "Backup", + "multi": false, + "name": "Backup", + "options": [], + "query": "trilio_backup_info{cluster=~\"$Cluster\",install_namespace=~\"$Install_Namespace\"}", + "refresh": 1, + "regex": "/.*backup=\"([^\"]*).*/", + "skipUrlSync": false, + "sort": 0, + "tagValuesQuery": "", + "tags": [], + "tagsQuery": "", + "type": "query", + "useTags": false + }, + { + "allValue": null, + "current": {}, + "datasource": "${DS_PROMETHEUS}", + "definition": "trilio_restore_info{cluster=~\"$Cluster\",install_namespace=~\"$Install_Namespace\"}", + "hide": 0, + "includeAll": false, + "label": "Restore", + "multi": false, + "name": "Restore", + "options": [], + "query": "trilio_restore_info{cluster=~\"$Cluster\",install_namespace=~\"$Install_Namespace\"}", + "refresh": 1, + "regex": "/.*restore=\"([^\"]*).*/", + "skipUrlSync": false, + "sort": 0, + "tagValuesQuery": "", + "tags": [], + "tagsQuery": "", + "type": "query", + "useTags": false + } + ] + }, + "time": { + "from": "now-15m", + "to": "now" + }, + "timepicker": { + "refresh_intervals": [ + "10s", + "30s", + "1m", + "5m", + "15m", + "30m", + "1h", + "2h", + "1d" + ] + }, + "timezone": "", + "title": "Metadata Detail", + "uid": "Metadata", + "version": 1 +} \ No newline at end of file diff --git a/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/visualization/charts/grafana/dashboards/overview.json b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/visualization/charts/grafana/dashboards/overview.json new file mode 100644 index 000000000..785f63bdd --- /dev/null +++ b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/visualization/charts/grafana/dashboards/overview.json @@ -0,0 +1,1429 @@ +{ + "__inputs": [ + { + "name": "DS_PROMETHEUS", + "label": "Prometheus", + "description": "", + "type": "datasource", + "pluginId": "prometheus", + "pluginName": "Prometheus" + } + ], + "__elements": [], + "__requires": [ + { + "type": "grafana", + "id": "grafana", + "name": "Grafana", + "version": "8.5.0" + }, + { + "type": "panel", + "id": "piechart", + "name": "Pie chart", + "version": "" + }, + { + "type": "datasource", + "id": "prometheus", + "name": "Prometheus", + "version": "1.0.0" + }, + { + "type": "panel", + "id": "stat", + "name": "Stat", + "version": "" + }, + { + "type": "panel", + "id": "table", + "name": "Table", + "version": "" + }, + { + "type": "panel", + "id": "text", + "name": "Text", + "version": "" + } + ], + "annotations": { + "list": [ + { + "$$hashKey": "object:4047", + "builtIn": 1, + "datasource": { + "type": "datasource", + "uid": "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, + "gnetId": 12599, + "graphTooltip": 0, + "id": null, + "iteration": 1672675438573, + "links": [], + "liveNow": false, + "panels": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "gridPos": { + "h": 5, + "w": 10, + "x": 0, + "y": 0 + }, + "id": 45, + "options": { + "content": "

", + "mode": "html" + }, + "pluginVersion": "8.5.0", + "transparent": true, + "type": "text" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + } + }, + "mappings": [ + { + "options": { + "0": { + "color": "red", + "index": 1, + "text": "Not Ready" + }, + "1": { + "color": "green", + "index": 0, + "text": "Ready" + } + }, + "type": "value" + } + ] + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 7, + "x": 11, + "y": 0 + }, + "id": 47, + "options": { + "legend": { + "displayMode": "list", + "placement": "bottom" + }, + "pieType": "donut", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "exemplar": false, + "expr": "trilio_component_status", + "instant": true, + "interval": "", + "legendFormat": "{{deployment}}-{{status}}", + "refId": "A" + } + ], + "transparent": true, + "type": "piechart" + }, + { + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [ + { + "options": { + "match": "null", + "result": { + "text": "N/A" + } + }, + "type": "special" + } + ], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "none" + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 3, + "x": 18, + "y": 0 + }, + "id": 42, + "links": [], + "maxDataPoints": 100, + "options": { + "colorMode": "none", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "horizontal", + "reduceOptions": { + "calcs": [ + "first" + ], + "fields": "/^tvk_version$/", + "values": false + }, + "textMode": "auto" + }, + "pluginVersion": "8.5.0", + "targets": [ + { + "datasource": "${DS_PROMETHEUS}", + "expr": "trilio_system_info{ install_namespace=~\"$Install_Namespace\",cluster=~\"$Cluster\"}", + "format": "table", + "instant": true, + "interval": "", + "legendFormat": "", + "refId": "A" + } + ], + "title": "TVK Version", + "type": "stat" + }, + { + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [ + { + "options": { + "match": "null", + "result": { + "text": "N/A" + } + }, + "type": "special" + } + ], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "none" + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 3, + "x": 21, + "y": 0 + }, + "id": 43, + "links": [], + "maxDataPoints": 100, + "options": { + "colorMode": "none", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "horizontal", + "reduceOptions": { + "calcs": [ + "first" + ], + "fields": "/^scope$/", + "values": false + }, + "textMode": "auto" + }, + "pluginVersion": "8.5.0", + "targets": [ + { + "datasource": "${DS_PROMETHEUS}", + "expr": "trilio_system_info{ install_namespace=~\"$Install_Namespace\",cluster=~\"$Cluster\"}", + "format": "table", + "instant": true, + "interval": "", + "legendFormat": "", + "refId": "A" + } + ], + "title": "TVK Scope", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + } + }, + "decimals": 0, + "mappings": [], + "unit": "short" + }, + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "Available" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "#37872D", + "mode": "fixed" + } + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Failed" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "#C4162A", + "mode": "fixed" + } + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "InProgress" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "#FADE2A", + "mode": "fixed" + } + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "UnKnown" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "rgb(43, 36, 36)", + "mode": "fixed" + } + } + ] + } + ] + }, + "gridPos": { + "h": 8, + "w": 8, + "x": 0, + "y": 5 + }, + "id": 34, + "links": [ + { + "title": "Show Backup Overview", + "url": "/d/BackupOverview/backup-overview?refresh=5s&var-Cluster=${Cluster}&var-Install_Namespace=${Install_Namespace}" + } + ], + "maxDataPoints": 1, + "options": { + "legend": { + "calcs": [], + "displayMode": "table", + "placement": "bottom", + "values": [ + "value" + ] + }, + "pieType": "donut", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "expr": "count(trilio_backup_info{ install_namespace=~\"$Install_Namespace\",cluster=~\"$Cluster\"}) by (status)", + "format": "time_series", + "instant": true, + "interval": "", + "legendFormat": "{{status}}", + "refId": "A" + } + ], + "title": "Backup Summary", + "type": "piechart" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + } + }, + "decimals": 0, + "mappings": [], + "unit": "short" + }, + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "Value" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "#37872D", + "mode": "fixed" + } + } + ] + } + ] + }, + "gridPos": { + "h": 8, + "w": 8, + "x": 8, + "y": 5 + }, + "id": 36, + "links": [ + { + "title": "Show Backup Plan Overview", + "url": "/d/BackupPlanOverview/backupplan-overview?refresh=5s&var-Cluster=${Cluster}&var-Install_Namespace=${Install_Namespace}" + } + ], + "maxDataPoints": 1, + "options": { + "legend": { + "calcs": [], + "displayMode": "table", + "placement": "bottom", + "values": [ + "value" + ] + }, + "pieType": "donut", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "expr": "count(trilio_backupplan_info{install_namespace=~\"$Install_Namespace\",cluster=~\"$Cluster\"}) by (kind) or count(trilio_continuousrestoreplan_info{install_namespace=~\"$Install_Namespace\",cluster=~\"$Cluster\"}) by (kind)", + "format": "time_series", + "instant": true, + "interval": "", + "legendFormat": "__auto", + "refId": "A" + } + ], + "title": " Plan Summary ", + "type": "piechart" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "custom": { + "align": "left", + "displayMode": "auto", + "inspect": false + }, + "decimals": 2, + "displayName": "", + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "short" + }, + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "Value" + }, + "properties": [ + { + "id": "displayName", + "value": "Total Targets" + }, + { + "id": "unit", + "value": "short" + }, + { + "id": "custom.align", + "value": "left" + }, + { + "id": "decimals", + "value": 0 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "status" + }, + "properties": [ + { + "id": "displayName", + "value": "Health" + }, + { + "id": "unit", + "value": "short" + }, + { + "id": "decimals", + "value": 2 + }, + { + "id": "custom.align" + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "vendorType" + }, + "properties": [ + { + "id": "displayName", + "value": "Vendor Type" + }, + { + "id": "unit", + "value": "short" + }, + { + "id": "decimals", + "value": 2 + }, + { + "id": "custom.align" + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "eventTarget" + }, + "properties": [ + { + "id": "displayName", + "value": "Target Type" + }, + { + "id": "mappings", + "value": [ + { + "options": { + "false": { + "index": 1, + "text": "Data Target" + }, + "true": { + "index": 0, + "text": "Event Target" + } + }, + "type": "value" + } + ] + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "target" + }, + "properties": [ + { + "id": "displayName", + "value": "Target Name" + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Target Type" + }, + "properties": [ + { + "id": "custom.width", + "value": 100 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Health" + }, + "properties": [ + { + "id": "custom.width", + "value": 84 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Target Name" + }, + "properties": [ + { + "id": "custom.width", + "value": 106 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Vendor Type" + }, + "properties": [ + { + "id": "custom.width", + "value": 96 + } + ] + } + ] + }, + "gridPos": { + "h": 8, + "w": 8, + "x": 16, + "y": 5 + }, + "id": 40, + "links": [ + { + "title": "Show Target Details", + "url": "/d/TargetDetail/target-detail?refresh=5s&var-Cluster=${Cluster}&var-Install_Namespace=${Install_Namespace}" + } + ], + "options": { + "footer": { + "fields": [ + "Value" + ], + "reducer": [ + "sum" + ], + "show": true + }, + "showHeader": true, + "sortBy": [] + }, + "pluginVersion": "8.5.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "exemplar": false, + "expr": "trilio_target_info{install_namespace=~\"$Install_Namespace\",cluster=~\"$Cluster\"}", + "format": "table", + "instant": true, + "interval": "", + "legendFormat": "", + "refId": "A" + } + ], + "title": "Target Summary", + "transformations": [ + { + "id": "merge", + "options": { + "reducers": [] + } + }, + { + "id": "filterFieldsByName", + "options": { + "include": { + "names": [ + "status", + "vendorType", + "Value", + "target", + "eventTarget" + ] + } + } + } + ], + "type": "table" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + } + }, + "decimals": 0, + "mappings": [], + "unit": "short" + }, + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "Available" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "#37872D", + "mode": "fixed" + } + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Failed" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "#C4162A", + "mode": "fixed" + } + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "InProgress" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "#FADE2A", + "mode": "fixed" + } + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "UnKnown" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "rgb(43, 36, 36)", + "mode": "fixed" + } + } + ] + } + ] + }, + "gridPos": { + "h": 8, + "w": 8, + "x": 0, + "y": 13 + }, + "id": 48, + "links": [ + { + "title": "Show ConsistentSet Overview", + "url": "/d/ConsistentSet/consistentset-detail?refresh=5s&var-Cluster=${Cluster}&var-Install_Namespace=${Install_Namespace}" + } + ], + "maxDataPoints": 1, + "options": { + "legend": { + "calcs": [], + "displayMode": "table", + "placement": "bottom", + "values": [ + "value" + ] + }, + "pieType": "donut", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "expr": "count(trilio_consistentset_info{ install_namespace=~\"$Install_Namespace\",cluster=~\"$Cluster\"}) by (status)", + "format": "time_series", + "instant": true, + "interval": "", + "legendFormat": "{{status}}", + "refId": "A" + } + ], + "title": "ConsistentSet Summary", + "type": "piechart" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + } + }, + "decimals": 0, + "mappings": [], + "unit": "short" + }, + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "Completed" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "#37872D", + "mode": "fixed" + } + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Failed" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "#C4162A", + "mode": "fixed" + } + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Value #C" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "#E0B400", + "mode": "fixed" + } + } + ] + } + ] + }, + "gridPos": { + "h": 8, + "w": 8, + "x": 8, + "y": 13 + }, + "id": 35, + "links": [ + { + "title": "Show Restore Overview", + "url": "/d/RestoreOverview/restore-overview?refresh=5s&var-Cluster=${Cluster}&var-Install_Namespace=${Install_Namespace}" + } + ], + "maxDataPoints": 1, + "options": { + "legend": { + "calcs": [], + "displayMode": "table", + "placement": "bottom", + "values": [ + "value" + ] + }, + "pieType": "donut", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "expr": "count(trilio_restore_info{ install_namespace=~\"$Install_Namespace\",cluster=~\"$Cluster\"}) by (status) ", + "format": "time_series", + "instant": true, + "interval": "", + "legendFormat": "{{status}}", + "refId": "A" + } + ], + "title": "Restore Summary", + "type": "piechart" + }, + { + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "custom": { + "align": "left", + "displayMode": "auto", + "inspect": false, + "width": 50 + }, + "decimals": 2, + "displayName": "", + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "short" + }, + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "deployment" + }, + "properties": [ + { + "id": "displayName", + "value": "Component" + }, + { + "id": "unit", + "value": "short" + }, + { + "id": "decimals", + "value": 2 + }, + { + "id": "custom.align" + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Value" + }, + "properties": [ + { + "id": "displayName", + "value": "Health" + }, + { + "id": "unit", + "value": "none" + }, + { + "id": "custom.displayMode", + "value": "color-background" + }, + { + "id": "custom.align", + "value": "left" + }, + { + "id": "thresholds", + "value": { + "mode": "absolute", + "steps": [ + { + "color": "#C4162A", + "value": null + }, + { + "color": "#C4162A", + "value": 0 + }, + { + "color": "rgba(50, 172, 45, 0.97)", + "value": 1 + } + ] + } + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "status" + }, + "properties": [ + { + "id": "displayName", + "value": "Status" + }, + { + "id": "unit", + "value": "short" + }, + { + "id": "decimals", + "value": 2 + }, + { + "id": "custom.align" + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Component" + }, + "properties": [ + { + "id": "custom.width", + "value": 264 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Health" + }, + "properties": [ + { + "id": "custom.width", + "value": 134 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Status" + }, + "properties": [ + { + "id": "custom.width", + "value": 75 + } + ] + } + ] + }, + "gridPos": { + "h": 8, + "w": 8, + "x": 16, + "y": 13 + }, + "id": 32, + "links": [ + { + "targetBlank": false, + "title": "Logging Dashboard", + "url": "/d/logging/logging-dashboard?orgId=1" + } + ], + "options": { + "footer": { + "fields": "", + "reducer": [ + "sum" + ], + "show": false + }, + "showHeader": true, + "sortBy": [] + }, + "pluginVersion": "8.5.0", + "targets": [ + { + "datasource": "${DS_PROMETHEUS}", + "expr": "trilio_component_status{install_namespace=~\"$Install_Namespace\",cluster=~\"$Cluster\"}", + "format": "table", + "instant": true, + "interval": "", + "legendFormat": "", + "refId": "A" + } + ], + "title": "TVK Health", + "transformations": [ + { + "id": "merge", + "options": { + "reducers": [] + } + }, + { + "id": "filterFieldsByName", + "options": { + "include": { + "names": [ + "deployment", + "status", + "Value" + ] + } + } + } + ], + "type": "table" + } + ], + "refresh": "", + "schemaVersion": 36, + "style": "dark", + "tags": [], + "templating": { + "list": [ + { + "current": { + "selected": false, + "text": "Prometheus", + "value": "Prometheus" + }, + "hide": 2, + "includeAll": false, + "label": "datasource", + "multi": false, + "name": "DS_PROMETHEUS", + "options": [], + "query": "prometheus", + "refresh": 1, + "regex": "", + "skipUrlSync": false, + "type": "datasource" + }, + { + "current": {}, + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "definition": "trilio_system_info", + "hide": 0, + "includeAll": false, + "multi": false, + "name": "Cluster", + "options": [], + "query": { + "query": "trilio_system_info", + "refId": "Prometheus-Cluster-Variable-Query" + }, + "refresh": 1, + "regex": "/.*cluster=\"([^\"]*).*/", + "skipUrlSync": false, + "sort": 0, + "tagValuesQuery": "", + "tagsQuery": "", + "type": "query", + "useTags": false + }, + { + "current": {}, + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "definition": "trilio_system_info{cluster=~\"$Cluster\"}", + "hide": 2, + "includeAll": false, + "multi": false, + "name": "Scope", + "options": [], + "query": { + "query": "trilio_system_info{cluster=~\"$Cluster\"}", + "refId": "Prometheus-Scope-Variable-Query" + }, + "refresh": 1, + "regex": "/.*scope=\"([^\"]*).*/", + "skipUrlSync": false, + "sort": 0, + "tagValuesQuery": "", + "tagsQuery": "", + "type": "query", + "useTags": false + }, + { + "current": {}, + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "definition": "trilio_system_info{scope=~\"$Scope\",cluster=~\"$Cluster\"}", + "hide": 0, + "includeAll": false, + "label": "Install Namespace", + "multi": false, + "name": "Install_Namespace", + "options": [], + "query": { + "query": "trilio_system_info{scope=~\"$Scope\",cluster=~\"$Cluster\"}", + "refId": "Prometheus-Install_Namespace-Variable-Query" + }, + "refresh": 1, + "regex": "/.*install_namespace=\"([^\"]*).*/", + "skipUrlSync": false, + "sort": 0, + "tagValuesQuery": "", + "tagsQuery": "", + "type": "query", + "useTags": false + } + ] + }, + "time": { + "from": "now-5m", + "to": "now" + }, + "timepicker": { + "refresh_intervals": [ + "10s", + "30s", + "1m", + "5m", + "15m", + "30m", + "1h", + "2h", + "1d" + ] + }, + "timezone": "", + "title": "Overview", + "uid": "Overview", + "version": 1, + "weekStart": "" +} \ No newline at end of file diff --git a/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/visualization/charts/grafana/dashboards/restore-detail.json b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/visualization/charts/grafana/dashboards/restore-detail.json new file mode 100644 index 000000000..aa3fcad9a --- /dev/null +++ b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/visualization/charts/grafana/dashboards/restore-detail.json @@ -0,0 +1,901 @@ +{ + "__inputs": [ + { + "name": "DS_PROMETHEUS", + "label": "Prometheus", + "description": "", + "type": "datasource", + "pluginId": "prometheus", + "pluginName": "Prometheus" + }, + { + "name": "DS_LOKI", + "label": "Loki", + "description": "", + "type": "datasource", + "pluginId": "loki", + "pluginName": "Loki" + } + ], + "__elements": [], + "__requires": [ + { + "type": "panel", + "id": "gauge", + "name": "Gauge", + "version": "" + }, + { + "type": "grafana", + "id": "grafana", + "name": "Grafana", + "version": "8.5.0" + }, + { + "type": "panel", + "id": "logs", + "name": "Logs", + "version": "" + }, + { + "type": "datasource", + "id": "loki", + "name": "Loki", + "version": "1.0.0" + }, + { + "type": "datasource", + "id": "prometheus", + "name": "Prometheus", + "version": "1.0.0" + }, + { + "type": "panel", + "id": "stat", + "name": "Stat", + "version": "" + }, + { + "type": "panel", + "id": "table", + "name": "Table", + "version": "" + }, + { + "type": "panel", + "id": "table-old", + "name": "Table (old)", + "version": "" + }, + { + "type": "panel", + "id": "text", + "name": "Text", + "version": "" + } + ], + "annotations": { + "list": [ + { + "$$hashKey": "object:1512", + "builtIn": 1, + "datasource": { + "type": "datasource", + "uid": "grafana" + }, + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "limit": 100, + "name": "Annotations & Alerts", + "showIn": 0, + "target": { + "limit": 100, + "matchAny": false, + "tags": [], + "type": "dashboard" + }, + "type": "dashboard" + } + ] + }, + "editable": true, + "fiscalYearStartMonth": 0, + "gnetId": 12603, + "graphTooltip": 0, + "id": null, + "iteration": 1655448141180, + "links": [], + "liveNow": false, + "panels": [ + { + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "gridPos": { + "h": 2, + "w": 23, + "x": 0, + "y": 0 + }, + "id": 4, + "options": { + "content": "

Restore Detail

", + "mode": "html" + }, + "pluginVersion": "8.5.0", + "transparent": true, + "type": "text" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "mappings": [], + "max": 100, + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "dark-red", + "value": null + }, + { + "color": "rgb(255, 255, 255)", + "value": 1 + }, + { + "color": "dark-green", + "value": 100 + } + ] + }, + "unit": "percent" + }, + "overrides": [] + }, + "gridPos": { + "h": 4, + "w": 7, + "x": 0, + "y": 2 + }, + "id": 13, + "options": { + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "last" + ], + "fields": "", + "values": false + }, + "showThresholdLabels": false, + "showThresholdMarkers": false + }, + "pluginVersion": "8.5.0", + "targets": [ + { + "datasource": "${DS_PROMETHEUS}", + "expr": "trilio_restore_status_percentage{restore=~\"$Restore\",install_namespace=~\"$Install_Namespace\",cluster=~\"$Cluster\",kind=\"Restore\"}", + "format": "time_series", + "instant": true, + "interval": "", + "intervalFactor": 1, + "legendFormat": "{{status}}", + "refId": "A" + } + ], + "transparent": true, + "type": "gauge" + }, + { + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "gridPos": { + "h": 1, + "w": 4, + "x": 7, + "y": 2 + }, + "id": 17, + "options": { + "content": "", + "mode": "markdown" + }, + "pluginVersion": "8.5.0", + "transparent": true, + "type": "text" + }, + { + "columns": [], + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "description": "", + "fontSize": "100%", + "gridPos": { + "h": 7, + "w": 12, + "x": 12, + "y": 2 + }, + "id": 11, + "showHeader": true, + "sort": { + "col": 0, + "desc": true + }, + "styles": [ + { + "$$hashKey": "object:2545", + "alias": "Object Type", + "align": "auto", + "dateFormat": "YYYY-MM-DD HH:mm:ss", + "link": true, + "linkTooltip": "${__cell}", + "linkUrl": "/d/Metadata/metadata-detail?var-Restore=${Restore}&var-ObjectType=${__cell}&var-Cluster=${Cluster}&var-Install_Namespace=${Install_Namespace}", + "pattern": "objecttype", + "type": "string" + }, + { + "$$hashKey": "object:1086", + "alias": "Source", + "align": "auto", + "colors": [ + "rgba(245, 54, 54, 0.9)", + "rgba(237, 129, 40, 0.89)", + "rgba(50, 172, 45, 0.97)" + ], + "dateFormat": "YYYY-MM-DD HH:mm:ss", + "decimals": 2, + "mappingType": 1, + "pattern": "applicationtype", + "thresholds": [], + "type": "string", + "unit": "short" + }, + { + "$$hashKey": "object:1112", + "alias": "Count", + "align": "auto", + "colors": [ + "rgba(245, 54, 54, 0.9)", + "rgba(237, 129, 40, 0.89)", + "rgba(50, 172, 45, 0.97)" + ], + "dateFormat": "YYYY-MM-DD HH:mm:ss", + "mappingType": 1, + "pattern": "Value", + "thresholds": [], + "type": "number", + "unit": "short" + }, + { + "$$hashKey": "object:2546", + "alias": "", + "align": "right", + "colors": [ + "rgba(245, 54, 54, 0.9)", + "rgba(237, 129, 40, 0.89)", + "rgba(50, 172, 45, 0.97)" + ], + "decimals": 2, + "pattern": "/.*/", + "thresholds": [], + "type": "hidden", + "unit": "short" + } + ], + "targets": [ + { + "datasource": "${DS_PROMETHEUS}", + "expr": "avg(trilio_restore_metadata_info{restore=~\"$Restore\",install_namespace=~\"$Install_Namespace\",cluster=~\"$Cluster\",kind=\"Restore\"}) by (objecttype, applicationtype)", + "format": "table", + "instant": true, + "interval": "", + "legendFormat": "", + "refId": "A" + } + ], + "title": "Metadata Info", + "transform": "table", + "type": "table-old" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "mappings": [ + { + "options": { + "0": { + "text": "InProgress" + }, + "1": { + "text": "Completed" + }, + "-1": { + "text": "Failed" + }, + "-2": { + "text": "UnKnown" + } + }, + "type": "value" + } + ], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "red", + "value": null + }, + { + "color": "dark-red", + "value": -1 + }, + { + "color": "blue", + "value": 0 + }, + { + "color": "green", + "value": 1 + } + ] + }, + "unit": "short" + }, + "overrides": [] + }, + "gridPos": { + "h": 2, + "w": 5, + "x": 7, + "y": 3 + }, + "id": 15, + "links": [], + "maxDataPoints": 100, + "options": { + "colorMode": "value", + "fieldOptions": { + "calcs": [ + "mean" + ] + }, + "graphMode": "none", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "first" + ], + "fields": "", + "values": false + }, + "textMode": "value" + }, + "pluginVersion": "8.5.0", + "targets": [ + { + "datasource": "${DS_PROMETHEUS}", + "expr": "trilio_restore_info{restore=~\"$Restore\",install_namespace=~\"$Install_Namespace\",cluster=~\"$Cluster\",kind=\"Restore\"}", + "format": "time_series", + "instant": true, + "interval": "", + "intervalFactor": 1, + "legendFormat": "{{status}}", + "refId": "A" + } + ], + "transparent": true, + "type": "stat" + }, + { + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [ + { + "options": { + "match": "null", + "result": { + "text": "N/A" + } + }, + "type": "special" + } + ], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "none" + }, + "overrides": [] + }, + "gridPos": { + "h": 3, + "w": 6, + "x": 0, + "y": 6 + }, + "id": 6, + "links": [], + "maxDataPoints": 100, + "options": { + "colorMode": "none", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "horizontal", + "reduceOptions": { + "calcs": [ + "first" + ], + "fields": "/^restore$/", + "values": false + }, + "text": { + "valueSize": 30 + }, + "textMode": "auto" + }, + "pluginVersion": "8.5.0", + "targets": [ + { + "datasource": "${DS_PROMETHEUS}", + "expr": "trilio_restore_info{ restore=~\"$Restore\",install_namespace=~\"$Install_Namespace\",cluster=~\"$Cluster\"}", + "format": "table", + "instant": true, + "interval": "", + "legendFormat": "", + "refId": "A" + } + ], + "title": "Restore", + "type": "stat" + }, + { + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [ + { + "options": { + "match": "null", + "result": { + "text": "N/A" + } + }, + "type": "special" + } + ], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "none" + }, + "overrides": [] + }, + "gridPos": { + "h": 3, + "w": 6, + "x": 6, + "y": 6 + }, + "id": 20, + "links": [], + "maxDataPoints": 100, + "options": { + "colorMode": "none", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "horizontal", + "reduceOptions": { + "calcs": [ + "first" + ], + "fields": "/^backup$/", + "values": false + }, + "text": { + "valueSize": 30 + }, + "textMode": "auto" + }, + "pluginVersion": "8.5.0", + "targets": [ + { + "datasource": "${DS_PROMETHEUS}", + "expr": "trilio_restore_info{ restore=~\"$Restore\",install_namespace=~\"$Install_Namespace\",cluster=~\"$Cluster\"}", + "format": "table", + "instant": true, + "interval": "", + "legendFormat": "", + "refId": "A" + } + ], + "title": "Backup", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "custom": { + "align": "left", + "displayMode": "auto", + "filterable": false, + "inspect": false + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + }, + "unit": "short" + }, + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "First" + }, + "properties": [ + { + "id": "displayName", + "value": "Value" + } + ] + } + ] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 9 + }, + "id": 19, + "options": { + "footer": { + "fields": "", + "reducer": [ + "sum" + ], + "show": false + }, + "showHeader": true + }, + "pluginVersion": "8.5.0", + "targets": [ + { + "datasource": "${DS_PROMETHEUS}", + "expr": "trilio_restore_info{ restore=~\"$Restore\",install_namespace=~\"$Install_Namespace\",cluster=~\"$Cluster\",kind=\"Restore\"}", + "format": "table", + "instant": true, + "interval": "", + "legendFormat": "", + "refId": "A" + } + ], + "title": "Details", + "transformations": [ + { + "id": "filterFieldsByName", + "options": { + "include": { + "names": [ + "Time", + "completion_ts", + "size", + "start_ts", + "target", + "resource_namespace" + ] + } + } + }, + { + "id": "reduce", + "options": { + "reducers": [ + "first" + ] + } + } + ], + "type": "table" + }, + { + "datasource": { + "type": "loki", + "uid": "${DS_LOKI}" + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 9 + }, + "id": 22, + "options": { + "dedupStrategy": "none", + "enableLogDetails": true, + "prettifyLogMessage": false, + "showCommonLabels": false, + "showLabels": false, + "showTime": true, + "sortOrder": "Descending", + "wrapLogMessage": false + }, + "targets": [ + { + "datasource": { + "type": "loki", + "uid": "${DS_LOKI}" + }, + "expr": "{transaction_type=\"Restore\",transaction_resource_name=~\"$Restore\",service_type=~\"$service_type\"}", + "refId": "A" + } + ], + "title": "Restore Logs", + "type": "logs" + } + ], + "refresh": "30s", + "schemaVersion": 36, + "style": "dark", + "tags": [], + "templating": { + "list": [ + { + "current": { + "selected": false, + "text": "Prometheus", + "value": "Prometheus" + }, + "hide": 2, + "includeAll": false, + "label": "datasource", + "multi": false, + "name": "DS_PROMETHEUS", + "options": [], + "query": "prometheus", + "refresh": 1, + "regex": "", + "skipUrlSync": false, + "type": "datasource" + }, + { + "current": { + "selected": false, + "text": "Loki", + "value": "Loki" + }, + "hide": 2, + "includeAll": false, + "label": "loki", + "multi": false, + "name": "DS_LOKI", + "options": [], + "query": "loki", + "refresh": 1, + "regex": "", + "skipUrlSync": false, + "type": "datasource" + }, + { + "current": {}, + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "definition": "trilio_system_info", + "hide": 2, + "includeAll": false, + "multi": false, + "name": "Cluster", + "options": [], + "query": { + "query": "trilio_system_info", + "refId": "Prometheus-Cluster-Variable-Query" + }, + "refresh": 1, + "regex": "/.*cluster=\"([^\"]*).*/", + "skipUrlSync": false, + "sort": 0, + "tagValuesQuery": "", + "tagsQuery": "", + "type": "query", + "useTags": false + }, + { + "current": {}, + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "definition": "trilio_system_info{cluster=~\"$Cluster\"}", + "hide": 2, + "includeAll": false, + "multi": false, + "name": "Scope", + "options": [], + "query": { + "query": "trilio_system_info{cluster=~\"$Cluster\"}", + "refId": "Prometheus-Scope-Variable-Query" + }, + "refresh": 1, + "regex": "/.*scope=\"([^\"]*).*/", + "skipUrlSync": false, + "sort": 0, + "tagValuesQuery": "", + "tagsQuery": "", + "type": "query", + "useTags": false + }, + { + "current": {}, + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "definition": "trilio_system_info{scope=~\"$Scope\",cluster=~\"$Cluster\"}", + "hide": 0, + "includeAll": false, + "label": "Install Namespace", + "multi": false, + "name": "Install_Namespace", + "options": [], + "query": { + "query": "trilio_system_info{scope=~\"$Scope\",cluster=~\"$Cluster\"}", + "refId": "Prometheus-Install_Namespace-Variable-Query" + }, + "refresh": 2, + "regex": "/.*install_namespace=\"([^\"]*).*/", + "skipUrlSync": false, + "sort": 0, + "tagValuesQuery": "", + "tagsQuery": "", + "type": "query", + "useTags": false + }, + { + "current": {}, + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "definition": "trilio_restore_info{cluster=~\"$Cluster\",kind=\"Restore\"}", + "hide": 0, + "includeAll": false, + "label": "Restore", + "multi": false, + "name": "Restore", + "options": [], + "query": { + "query": "trilio_restore_info{cluster=~\"$Cluster\",kind=\"Restore\"}", + "refId": "StandardVariableQuery" + }, + "refresh": 2, + "regex": "/.*restore=\"([^\"]*).*/", + "skipUrlSync": false, + "sort": 0, + "tagValuesQuery": "", + "tagsQuery": "", + "type": "query", + "useTags": false + }, + { + "current": {}, + "datasource": { + "type": "loki", + "uid": "${DS_LOKI}" + }, + "definition": "label_values({transaction_type=~\"Restore\",transaction_resource_name=~\"$Restore\"},transaction_resource_namespace)", + "description": "Restore Namespace", + "hide": 0, + "includeAll": true, + "label": "Restore Namespace", + "multi": false, + "name": "Namespace", + "options": [], + "query": "label_values({transaction_type=~\"Restore\",transaction_resource_name=~\"$Restore\"},transaction_resource_namespace)", + "refresh": 2, + "regex": "", + "skipUrlSync": false, + "sort": 0, + "type": "query" + }, + { + "current": {}, + "datasource": { + "type": "loki", + "uid": "${DS_LOKI}" + }, + "definition": "label_values({transaction_type=\"Restore\",transaction_resource_name=~\"$Restore\"}, service_type)", + "description": "Service Type", + "hide": 0, + "includeAll": true, + "label": "Service Type", + "multi": false, + "name": "service_type", + "options": [], + "query": "label_values({transaction_type=\"Restore\",transaction_resource_name=~\"$Restore\"}, service_type)", + "refresh": 2, + "regex": "", + "skipUrlSync": false, + "sort": 0, + "type": "query" + } + ] + }, + "time": { + "from": "now-15m", + "to": "now" + }, + "timepicker": { + "refresh_intervals": [ + "10s", + "30s", + "1m", + "5m", + "15m", + "30m", + "1h", + "2h", + "1d" + ] + }, + "timezone": "", + "title": "Restore Detail", + "uid": "Restore", + "version": 1, + "weekStart": "" +} \ No newline at end of file diff --git a/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/visualization/charts/grafana/dashboards/restore-overview.json b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/visualization/charts/grafana/dashboards/restore-overview.json new file mode 100644 index 000000000..2072f9941 --- /dev/null +++ b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/visualization/charts/grafana/dashboards/restore-overview.json @@ -0,0 +1,853 @@ +{ + "__inputs": [ + { + "name": "DS_PROMETHEUS", + "label": "Prometheus", + "description": "", + "type": "datasource", + "pluginId": "prometheus", + "pluginName": "Prometheus" + } + ], + "__elements": [], + "__requires": [ + { + "type": "grafana", + "id": "grafana", + "name": "Grafana", + "version": "8.5.0" + }, + { + "type": "datasource", + "id": "prometheus", + "name": "Prometheus", + "version": "1.0.0" + }, + { + "type": "panel", + "id": "stat", + "name": "Stat", + "version": "" + }, + { + "type": "panel", + "id": "table", + "name": "Table", + "version": "" + }, + { + "type": "panel", + "id": "text", + "name": "Text", + "version": "" + } + ], + "annotations": { + "list": [ + { + "$$hashKey": "object:48276", + "builtIn": 1, + "datasource": { + "type": "datasource", + "uid": "grafana" + }, + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "limit": 100, + "name": "Annotations & Alerts", + "showIn": 0, + "target": { + "limit": 100, + "matchAny": false, + "tags": [], + "type": "dashboard" + }, + "type": "dashboard" + } + ] + }, + "editable": true, + "fiscalYearStartMonth": 0, + "gnetId": 12602, + "graphTooltip": 0, + "id": null, + "iteration": 1655396733765, + "links": [], + "liveNow": false, + "panels": [ + { + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "gridPos": { + "h": 2, + "w": 23, + "x": 1, + "y": 0 + }, + "id": 4, + "options": { + "content": "

Restores Overview

", + "mode": "html" + }, + "pluginVersion": "8.5.0", + "transparent": true, + "type": "text" + }, + { + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "links": [], + "mappings": [], + "min": 0, + "noValue": "0", + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "rgb(115, 181, 181)", + "value": null + } + ] + }, + "unit": "short" + }, + "overrides": [] + }, + "gridPos": { + "h": 3, + "w": 4, + "x": 2, + "y": 2 + }, + "id": 35, + "links": [], + "options": { + "colorMode": "background", + "graphMode": "none", + "justifyMode": "center", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "first" + ], + "fields": "", + "values": false + }, + "textMode": "auto" + }, + "pluginVersion": "8.5.0", + "targets": [ + { + "expr": "count(trilio_restore_info{install_namespace=~\"$Install_Namespace\",cluster=~\"$Cluster\"}) ", + "format": "time_series", + "instant": true, + "interval": "", + "legendFormat": "All", + "refId": "A", + "datasource": "${DS_PROMETHEUS}" + } + ], + "title": "All", + "transparent": true, + "type": "stat" + }, + { + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "links": [], + "mappings": [], + "min": 0, + "noValue": "0", + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "short" + }, + "overrides": [] + }, + "gridPos": { + "h": 3, + "w": 4, + "x": 6, + "y": 2 + }, + "id": 36, + "links": [], + "options": { + "colorMode": "background", + "graphMode": "none", + "justifyMode": "center", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "last" + ], + "fields": "/^Completed$/", + "values": false + }, + "textMode": "auto" + }, + "pluginVersion": "8.5.0", + "targets": [ + { + "expr": "count(trilio_restore_info{status=\"Completed\",install_namespace=~\"$Install_Namespace\",cluster=~\"$Cluster\"}) by (status)", + "format": "time_series", + "instant": true, + "interval": "", + "legendFormat": "{{status}}", + "refId": "A", + "datasource": "${DS_PROMETHEUS}" + } + ], + "title": "Completed", + "transparent": true, + "type": "stat" + }, + { + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "links": [], + "mappings": [], + "min": 0, + "noValue": "0", + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "blue", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "short" + }, + "overrides": [] + }, + "gridPos": { + "h": 3, + "w": 4, + "x": 10, + "y": 2 + }, + "id": 39, + "links": [], + "options": { + "colorMode": "background", + "graphMode": "none", + "justifyMode": "center", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "last" + ], + "fields": "/^InProgress$/", + "values": false + }, + "textMode": "auto" + }, + "pluginVersion": "8.5.0", + "targets": [ + { + "expr": "count(trilio_restore_info{status=\"InProgress\",install_namespace=~\"$Install_Namespace\",cluster=~\"$Cluster\"}) by (status)", + "format": "time_series", + "instant": true, + "interval": "", + "legendFormat": "{{status}}", + "refId": "A", + "datasource": "${DS_PROMETHEUS}" + } + ], + "title": "InProgress", + "transparent": true, + "type": "stat" + }, + { + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "links": [], + "mappings": [], + "min": 0, + "noValue": "0", + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "red", + "value": null + } + ] + }, + "unit": "short" + }, + "overrides": [] + }, + "gridPos": { + "h": 3, + "w": 4, + "x": 14, + "y": 2 + }, + "id": 38, + "links": [], + "options": { + "colorMode": "background", + "graphMode": "none", + "justifyMode": "center", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "last" + ], + "fields": "/^Failed$/", + "values": false + }, + "textMode": "auto" + }, + "pluginVersion": "8.5.0", + "targets": [ + { + "expr": "count(trilio_restore_info{status=\"Failed\",install_namespace=~\"$Install_Namespace\",cluster=~\"$Cluster\"}) by (status)", + "format": "time_series", + "instant": true, + "interval": "", + "legendFormat": "{{status}}", + "refId": "A", + "datasource": "${DS_PROMETHEUS}" + } + ], + "title": "Failed", + "transparent": true, + "type": "stat" + }, + { + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "description": "", + "fieldConfig": { + "defaults": { + "links": [], + "mappings": [], + "min": 0, + "noValue": "0", + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "rgb(79, 145, 145)", + "value": null + } + ] + }, + "unit": "short" + }, + "overrides": [] + }, + "gridPos": { + "h": 3, + "w": 4, + "x": 18, + "y": 2 + }, + "id": 37, + "links": [], + "options": { + "colorMode": "background", + "graphMode": "none", + "justifyMode": "center", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "first" + ], + "fields": "", + "values": false + }, + "textMode": "auto" + }, + "pluginVersion": "8.5.0", + "targets": [ + { + "expr": "count(trilio_restore_info{status=\"UnKnown\",install_namespace=~\"$Install_Namespace\",cluster=~\"$Cluster\"}) by (status)", + "format": "time_series", + "instant": true, + "interval": "", + "legendFormat": "{{status}}", + "refId": "A", + "datasource": "${DS_PROMETHEUS}" + } + ], + "title": "UnKnown", + "transformations": [], + "transparent": true, + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "custom": { + "align": "auto", + "displayMode": "auto", + "inspect": false + }, + "decimals": 2, + "displayName": "", + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "short" + }, + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "restore" + }, + "properties": [ + { + "id": "displayName", + "value": "Restore" + }, + { + "id": "unit", + "value": "short" + }, + { + "id": "decimals", + "value": 2 + }, + { + "id": "links", + "value": [ + { + "targetBlank": false, + "title": "Show Restore Detail", + "url": "/d/${__data.fields.kind}/?refresh=5s&var-Restore=${__value.text}&var-Cluster=${Cluster}&var-Install_Namespace=${Install_Namespace}" + } + ] + }, + { + "id": "custom.align" + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "completion_ts" + }, + "properties": [ + { + "id": "displayName", + "value": "Completion" + }, + { + "id": "unit", + "value": "short" + }, + { + "id": "decimals", + "value": 2 + }, + { + "id": "unit", + "value": "time: YYYY-MM-DD HH:mm:ss.SSS" + }, + { + "id": "custom.align" + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Value" + }, + "properties": [ + { + "id": "displayName", + "value": "Percentage" + }, + { + "id": "unit", + "value": "percent" + }, + { + "id": "custom.align" + }, + { + "id": "decimals", + "value": 0 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "backup" + }, + "properties": [ + { + "id": "displayName", + "value": "Backup" + }, + { + "id": "unit", + "value": "decbytes" + }, + { + "id": "decimals", + "value": 2 + }, + { + "id": "links", + "value": [] + }, + { + "id": "custom.align" + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "target" + }, + "properties": [ + { + "id": "displayName", + "value": "Target" + }, + { + "id": "unit", + "value": "short" + }, + { + "id": "decimals", + "value": 2 + }, + { + "id": "links", + "value": [ + { + "targetBlank": false, + "title": "Show Target Detail", + "url": "/d/TargetDetail/target-detail?refresh=5s&var-Target=${__value.text}&var-Cluster=${Cluster}&var-Install_Namespace=${Install_Namespace}" + } + ] + }, + { + "id": "custom.align" + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "size" + }, + "properties": [ + { + "id": "displayName", + "value": "Size" + }, + { + "id": "unit", + "value": "decbytes" + }, + { + "id": "custom.align" + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "status" + }, + "properties": [ + { + "id": "displayName", + "value": "Status" + }, + { + "id": "unit", + "value": "short" + }, + { + "id": "decimals", + "value": 2 + }, + { + "id": "custom.align" + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "kind" + }, + "properties": [ + { + "id": "displayName", + "value": "Restore Kind" + } + ] + } + ] + }, + "gridPos": { + "h": 10, + "w": 24, + "x": 0, + "y": 5 + }, + "id": 29, + "options": { + "footer": { + "fields": "", + "reducer": [ + "sum" + ], + "show": false + }, + "showHeader": true + }, + "pluginVersion": "8.5.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "expr": "trilio_restore_status_percentage{status=~\"$Status\",install_namespace=~\"$Install_Namespace\",cluster=~\"$Cluster\"}", + "format": "table", + "instant": true, + "interval": "", + "intervalFactor": 1, + "legendFormat": "", + "refId": "A" + } + ], + "transformations": [ + { + "id": "merge", + "options": { + "reducers": [] + } + }, + { + "id": "filterFieldsByName", + "options": { + "include": { + "names": [ + "completion_ts", + "kind", + "restore", + "size", + "status", + "target", + "Value", + "backup" + ] + } + } + } + ], + "type": "table" + } + ], + "refresh": "5s", + "schemaVersion": 36, + "style": "dark", + "tags": [], + "templating": { + "list": [ + { + "current": { + "selected": false, + "text": "Prometheus", + "value": "Prometheus" + }, + "hide": 2, + "includeAll": false, + "label": "datasource", + "multi": false, + "name": "DS_PROMETHEUS", + "options": [], + "query": "prometheus", + "refresh": 1, + "regex": "", + "skipUrlSync": false, + "type": "datasource" + }, + { + "current": {}, + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "definition": "trilio_system_info", + "hide": 2, + "includeAll": false, + "multi": false, + "name": "Cluster", + "options": [], + "query": { + "query": "trilio_system_info", + "refId": "Prometheus-Cluster-Variable-Query" + }, + "refresh": 1, + "regex": "/.*cluster=\"([^\"]*).*/", + "skipUrlSync": false, + "sort": 0, + "tagValuesQuery": "", + "tagsQuery": "", + "type": "query", + "useTags": false + }, + { + "current": {}, + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "definition": "trilio_system_info{cluster=~\"$Cluster\"}", + "hide": 2, + "includeAll": false, + "multi": false, + "name": "Scope", + "options": [], + "query": { + "query": "trilio_system_info{cluster=~\"$Cluster\"}", + "refId": "Prometheus-Scope-Variable-Query" + }, + "refresh": 1, + "regex": "/.*scope=\"([^\"]*).*/", + "skipUrlSync": false, + "sort": 0, + "tagValuesQuery": "", + "tagsQuery": "", + "type": "query", + "useTags": false + }, + { + "current": {}, + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "definition": "trilio_system_info{scope=~\"$Scope\",cluster=~\"$Cluster\"}", + "hide": 0, + "includeAll": false, + "label": "Install Namespace", + "multi": false, + "name": "Install_Namespace", + "options": [], + "query": { + "query": "trilio_system_info{scope=~\"$Scope\",cluster=~\"$Cluster\"}", + "refId": "Prometheus-Install_Namespace-Variable-Query" + }, + "refresh": 1, + "regex": "/.*install_namespace=\"([^\"]*).*/", + "skipUrlSync": false, + "sort": 0, + "tagValuesQuery": "", + "tagsQuery": "", + "type": "query", + "useTags": false + }, + { + "current": {}, + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "definition": "trilio_restore_info", + "hide": 0, + "includeAll": true, + "label": "status", + "multi": false, + "name": "Status", + "options": [], + "query": { + "query": "trilio_restore_info", + "refId": "Prometheus-Status-Variable-Query" + }, + "refresh": 1, + "regex": "/.*status=\"([^\"]*).*/", + "skipUrlSync": false, + "sort": 0, + "tagValuesQuery": "", + "tagsQuery": "", + "type": "query", + "useTags": false + } + ] + }, + "time": { + "from": "now-15m", + "to": "now" + }, + "timepicker": { + "refresh_intervals": [ + "10s", + "30s", + "1m", + "5m", + "15m", + "30m", + "1h", + "2h", + "1d" + ] + }, + "timezone": "", + "title": "Restore Overview", + "uid": "RestoreOverview", + "version": 1, + "weekStart": "" +} \ No newline at end of file diff --git a/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/visualization/charts/grafana/dashboards/target-detail.json b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/visualization/charts/grafana/dashboards/target-detail.json new file mode 100644 index 000000000..5fa45353d --- /dev/null +++ b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/visualization/charts/grafana/dashboards/target-detail.json @@ -0,0 +1,1327 @@ +{ + "__inputs": [ + { + "name": "DS_PROMETHEUS", + "label": "Prometheus", + "description": "", + "type": "datasource", + "pluginId": "prometheus", + "pluginName": "Prometheus" + }, + { + "name": "DS_LOKI", + "label": "Loki", + "description": "", + "type": "datasource", + "pluginId": "loki", + "pluginName": "Loki" + } + ], + "__elements": [], + "__requires": [ + { + "type": "grafana", + "id": "grafana", + "name": "Grafana", + "version": "8.5.0" + }, + { + "type": "panel", + "id": "logs", + "name": "Logs", + "version": "" + }, + { + "type": "datasource", + "id": "loki", + "name": "Loki", + "version": "1.0.0" + }, + { + "type": "datasource", + "id": "prometheus", + "name": "Prometheus", + "version": "1.0.0" + }, + { + "type": "panel", + "id": "stat", + "name": "Stat", + "version": "" + }, + { + "type": "panel", + "id": "table", + "name": "Table", + "version": "" + }, + { + "type": "panel", + "id": "text", + "name": "Text", + "version": "" + } + ], + "annotations": { + "list": [ + { + "$$hashKey": "object:63480", + "builtIn": 1, + "datasource": { + "type": "datasource", + "uid": "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, + "gnetId": 12606, + "graphTooltip": 0, + "id": 14, + "iteration": 1655445162139, + "links": [], + "liveNow": false, + "panels": [ + { + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "gridPos": { + "h": 2, + "w": 24, + "x": 0, + "y": 0 + }, + "id": 4, + "options": { + "content": "

Targets

", + "mode": "html" + }, + "pluginVersion": "8.5.0", + "transparent": true, + "type": "text" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "mappings": [ + { + "options": { + "0": { + "text": "Unavailable" + }, + "1": { + "text": "Available" + } + }, + "type": "value" + } + ], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "#d44a3a", + "value": null + }, + { + "color": "dark-red", + "value": 0 + }, + { + "color": "#299c46", + "value": 1 + } + ] + }, + "unit": "none" + }, + "overrides": [] + }, + "gridPos": { + "h": 3, + "w": 3, + "x": 0, + "y": 2 + }, + "id": 8, + "links": [], + "maxDataPoints": 100, + "options": { + "colorMode": "value", + "fieldOptions": { + "calcs": [ + "mean" + ] + }, + "graphMode": "none", + "justifyMode": "auto", + "orientation": "horizontal", + "reduceOptions": { + "calcs": [ + "first" + ], + "fields": "", + "values": false + }, + "text": { + "valueSize": 30 + }, + "textMode": "auto" + }, + "pluginVersion": "8.5.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "exemplar": false, + "expr": "avg(trilio_target_info{target=~\"$Target\",install_namespace=~\"$Install_Namespace\",cluster=~\"$Cluster\"})", + "format": "time_series", + "instant": true, + "interval": "", + "legendFormat": "", + "refId": "A" + } + ], + "title": "Health", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [ + { + "options": { + "false": { + "index": 1, + "text": "Data Target" + }, + "true": { + "index": 0, + "text": "Event Target" + } + }, + "type": "value" + } + ], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "none" + }, + "overrides": [] + }, + "gridPos": { + "h": 3, + "w": 4, + "x": 3, + "y": 2 + }, + "id": 43, + "links": [], + "maxDataPoints": 100, + "options": { + "colorMode": "none", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "horizontal", + "reduceOptions": { + "calcs": [ + "first" + ], + "fields": "/^eventTarget$/", + "values": false + }, + "text": { + "valueSize": 30 + }, + "textMode": "auto" + }, + "pluginVersion": "8.5.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "exemplar": false, + "expr": "trilio_target_info{ target=~\"$Target\",install_namespace=~\"$Install_Namespace\",cluster=~\"$Cluster\"}", + "format": "table", + "instant": true, + "interval": "", + "legendFormat": "", + "refId": "A" + } + ], + "title": "Target Type", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [ + { + "options": { + "false": { + "text": "False" + }, + "true": { + "text": "True" + } + }, + "type": "value" + } + ], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "none" + }, + "overrides": [] + }, + "gridPos": { + "h": 3, + "w": 2, + "x": 7, + "y": 2 + }, + "id": 35, + "links": [], + "maxDataPoints": 100, + "options": { + "colorMode": "none", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "horizontal", + "reduceOptions": { + "calcs": [ + "first" + ], + "fields": "/^browsing$/", + "values": false + }, + "text": { + "valueSize": 30 + }, + "textMode": "auto" + }, + "pluginVersion": "8.5.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "exemplar": false, + "expr": "trilio_target_info{ target=~\"$Target\",install_namespace=~\"$Install_Namespace\",cluster=~\"$Cluster\"}", + "format": "table", + "instant": true, + "interval": "", + "legendFormat": "", + "refId": "A" + } + ], + "title": "Browsing", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [ + { + "options": { + "0": { + "text": "N/A" + } + }, + "type": "value" + } + ], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "none" + }, + "overrides": [] + }, + "gridPos": { + "h": 3, + "w": 3, + "x": 9, + "y": 2 + }, + "id": 39, + "links": [], + "maxDataPoints": 100, + "options": { + "colorMode": "none", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "horizontal", + "reduceOptions": { + "calcs": [ + "first" + ], + "fields": "/^threshold_capacity$/", + "values": false + }, + "text": { + "valueSize": 30 + }, + "textMode": "auto" + }, + "pluginVersion": "8.5.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "exemplar": false, + "expr": "trilio_target_info{ target=~\"$Target\",install_namespace=~\"$Install_Namespace\",cluster=~\"$Cluster\"}", + "format": "table", + "instant": true, + "interval": "", + "legendFormat": "", + "refId": "A" + } + ], + "title": "Threshold Capacity", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "mappings": [], + "noValue": "0", + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "#c7d0d9", + "value": null + } + ] + }, + "unit": "decbytes" + }, + "overrides": [] + }, + "gridPos": { + "h": 3, + "w": 3, + "x": 12, + "y": 2 + }, + "id": 38, + "links": [], + "maxDataPoints": 100, + "options": { + "colorMode": "value", + "fieldOptions": { + "calcs": [ + "mean" + ] + }, + "graphMode": "none", + "justifyMode": "auto", + "orientation": "horizontal", + "reduceOptions": { + "calcs": [ + "sum" + ], + "fields": "", + "values": false + }, + "text": { + "valueSize": 30 + }, + "textMode": "auto" + }, + "pluginVersion": "8.5.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "exemplar": false, + "expr": "sum(trilio_backup_storage{target=~\"$Target\",status=~\"Available\",install_namespace=~\"$Install_Namespace\",cluster=~\"$Cluster\"})", + "instant": true, + "interval": "", + "legendFormat": "", + "refId": "A" + } + ], + "title": "Total Capacity", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [ + { + "options": { + "match": "null", + "result": { + "text": "N/A" + } + }, + "type": "special" + } + ], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "none" + }, + "overrides": [] + }, + "gridPos": { + "h": 3, + "w": 3, + "x": 15, + "y": 2 + }, + "id": 36, + "links": [], + "maxDataPoints": 100, + "options": { + "colorMode": "none", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "horizontal", + "reduceOptions": { + "calcs": [ + "first" + ], + "fields": "/^vendorType$/", + "values": false + }, + "text": { + "valueSize": 30 + }, + "textMode": "auto" + }, + "pluginVersion": "8.5.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "exemplar": false, + "expr": "trilio_target_info{ target=~\"$Target\",install_namespace=~\"$Install_Namespace\",cluster=~\"$Cluster\"}", + "format": "table", + "instant": true, + "interval": "", + "legendFormat": "", + "refId": "A" + } + ], + "title": "Vendor Type", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [ + { + "options": { + "match": "null", + "result": { + "text": "N/A" + } + }, + "type": "special" + } + ], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "none" + }, + "overrides": [] + }, + "gridPos": { + "h": 3, + "w": 2, + "x": 18, + "y": 2 + }, + "id": 40, + "links": [], + "maxDataPoints": 100, + "options": { + "colorMode": "none", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "horizontal", + "reduceOptions": { + "calcs": [ + "first" + ], + "fields": "/^vendor$/", + "values": false + }, + "text": { + "valueSize": 30 + }, + "textMode": "auto" + }, + "pluginVersion": "8.5.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "exemplar": false, + "expr": "trilio_target_info{ target=~\"$Target\",install_namespace=~\"$Install_Namespace\",cluster=~\"$Cluster\"}", + "format": "table", + "instant": true, + "interval": "", + "legendFormat": "", + "refId": "A" + } + ], + "title": "Vendor ", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [ + { + "options": { + "match": "null", + "result": { + "text": "N/A" + } + }, + "type": "special" + } + ], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "dateTimeAsIso" + }, + "overrides": [] + }, + "gridPos": { + "h": 3, + "w": 4, + "x": 20, + "y": 2 + }, + "id": 37, + "links": [], + "maxDataPoints": 100, + "options": { + "colorMode": "none", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "horizontal", + "reduceOptions": { + "calcs": [ + "first" + ], + "fields": "/^creation_ts$/", + "values": false + }, + "text": {}, + "textMode": "auto" + }, + "pluginVersion": "8.5.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "exemplar": false, + "expr": "trilio_target_info{ target=~\"$Target\",install_namespace=~\"$Install_Namespace\",cluster=~\"$Cluster\"}", + "format": "table", + "instant": true, + "interval": "", + "legendFormat": "", + "refId": "A" + } + ], + "title": "Creattion Timestamp", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "custom": { + "align": "auto", + "displayMode": "auto", + "inspect": false, + "minWidth": 100 + }, + "decimals": 2, + "displayName": "", + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "short" + }, + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "backup" + }, + "properties": [ + { + "id": "displayName", + "value": "Backup" + }, + { + "id": "unit", + "value": "short" + }, + { + "id": "decimals", + "value": 2 + }, + { + "id": "links", + "value": [ + { + "targetBlank": false, + "title": "Show Backup Detail", + "url": "/d/${__data.fields.kind}?refresh=5s&var-Backup=${__value.text}&var-Cluster=${Cluster}&var-Install_Namespace=${Install_Namespace}" + } + ] + }, + { + "id": "custom.align" + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "backupplan" + }, + "properties": [ + { + "id": "displayName", + "value": "Backup Plan" + }, + { + "id": "unit", + "value": "short" + }, + { + "id": "decimals", + "value": 2 + }, + { + "id": "custom.align" + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "completion_ts" + }, + "properties": [ + { + "id": "displayName", + "value": "Completion " + }, + { + "id": "unit", + "value": "short" + }, + { + "id": "decimals", + "value": 2 + }, + { + "id": "custom.align" + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "size" + }, + "properties": [ + { + "id": "displayName", + "value": "Size" + }, + { + "id": "unit", + "value": "decbytes" + }, + { + "id": "custom.align" + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "-" + }, + "properties": [ + { + "id": "displayName", + "value": "Average Data Transfer" + }, + { + "id": "unit", + "value": "short" + }, + { + "id": "decimals", + "value": 2 + }, + { + "id": "custom.align" + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "target" + }, + "properties": [ + { + "id": "displayName", + "value": "Target" + }, + { + "id": "unit", + "value": "short" + }, + { + "id": "decimals", + "value": 2 + }, + { + "id": "custom.align" + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "status" + }, + "properties": [ + { + "id": "displayName", + "value": "Status" + }, + { + "id": "unit", + "value": "short" + }, + { + "id": "decimals", + "value": 2 + }, + { + "id": "custom.align" + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "backup_type" + }, + "properties": [ + { + "id": "displayName", + "value": "Backup Type" + }, + { + "id": "unit", + "value": "short" + }, + { + "id": "decimals", + "value": 2 + }, + { + "id": "custom.align" + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "kind" + }, + "properties": [ + { + "id": "displayName", + "value": "Backup KInd" + } + ] + } + ] + }, + "gridPos": { + "h": 18, + "w": 12, + "x": 0, + "y": 5 + }, + "id": 32, + "options": { + "footer": { + "enablePagination": false, + "fields": "", + "reducer": [ + "sum" + ], + "show": false + }, + "showHeader": true + }, + "pluginVersion": "8.5.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "exemplar": false, + "expr": "topk(10,trilio_backup_info{target=~\"$Target\",install_namespace=~\"$Install_Namespace\",cluster=~\"$Cluster\"}) by (size)", + "format": "table", + "instant": true, + "interval": "", + "intervalFactor": 1, + "legendFormat": "{{backup}} {{status}}", + "refId": "A" + } + ], + "title": "Backups", + "transformations": [ + { + "id": "merge", + "options": { + "reducers": [] + } + }, + { + "id": "filterFieldsByName", + "options": { + "include": { + "names": [ + "backup", + "backup_type", + "backupplan", + "completion_ts", + "kind", + "size", + "status", + "target" + ] + } + } + } + ], + "type": "table" + }, + { + "datasource": { + "type": "loki", + "uid": "${DS_LOKI}" + }, + "gridPos": { + "h": 18, + "w": 12, + "x": 12, + "y": 5 + }, + "id": 42, + "options": { + "dedupStrategy": "none", + "enableLogDetails": true, + "prettifyLogMessage": false, + "showCommonLabels": false, + "showLabels": false, + "showTime": true, + "sortOrder": "Descending", + "wrapLogMessage": false + }, + "targets": [ + { + "datasource": { + "type": "loki", + "uid": "${DS_LOKI}" + }, + "expr": "{transaction_type=\"Target\",transaction_resource_name=~\"$Target\",service_type=~\"$service_type\",transaction_resource_namespace=~\"$Namespace\"}", + "refId": "A" + } + ], + "title": "Target Logs ", + "type": "logs" + } + ], + "refresh": "30s", + "schemaVersion": 36, + "style": "dark", + "tags": [], + "templating": { + "list": [ + { + "current": { + "selected": false, + "text": "Prometheus", + "value": "Prometheus" + }, + "description": "prometheus datasource", + "hide": 2, + "includeAll": false, + "label": "datasource", + "multi": false, + "name": "DS_PROMETHEUS", + "options": [], + "query": "prometheus", + "refresh": 1, + "regex": "", + "skipUrlSync": false, + "type": "datasource" + }, + { + "current": { + "selected": false, + "text": "Loki", + "value": "Loki" + }, + "description": "loki datasource", + "hide": 2, + "includeAll": false, + "label": "loki datasource", + "multi": false, + "name": "DS_LOKI", + "options": [], + "query": "loki", + "refresh": 1, + "regex": "", + "skipUrlSync": false, + "type": "datasource" + }, + { + "current": {}, + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "definition": "trilio_system_info", + "hide": 2, + "includeAll": false, + "multi": false, + "name": "Cluster", + "options": [], + "query": { + "query": "trilio_system_info", + "refId": "Prometheus-Cluster-Variable-Query" + }, + "refresh": 1, + "regex": "/.*cluster=\"([^\"]*).*/", + "skipUrlSync": false, + "sort": 0, + "tagValuesQuery": "", + "tagsQuery": "", + "type": "query", + "useTags": false + }, + { + "current": {}, + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "definition": "trilio_system_info{cluster=~\"$Cluster\"}", + "hide": 2, + "includeAll": false, + "multi": false, + "name": "Scope", + "options": [], + "query": { + "query": "trilio_system_info{cluster=~\"$Cluster\"}", + "refId": "Prometheus-Scope-Variable-Query" + }, + "refresh": 1, + "regex": "/.*scope=\"([^\"]*).*/", + "skipUrlSync": false, + "sort": 0, + "tagValuesQuery": "", + "tagsQuery": "", + "type": "query", + "useTags": false + }, + { + "current": {}, + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "definition": "trilio_system_info{scope=~\"$Scope\",cluster=~\"$Cluster\"}", + "hide": 0, + "includeAll": false, + "label": "Install Namespace", + "multi": false, + "name": "Install_Namespace", + "options": [], + "query": { + "query": "trilio_system_info{scope=~\"$Scope\",cluster=~\"$Cluster\"}", + "refId": "Prometheus-Install_Namespace-Variable-Query" + }, + "refresh": 2, + "regex": "/.*install_namespace=\"([^\"]*).*/", + "skipUrlSync": false, + "sort": 0, + "type": "query" + }, + { + "allValue": ".*", + "current": {}, + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "definition": "trilio_target_info{cluster=~\"$Cluster\"}", + "hide": 0, + "includeAll": false, + "label": "Target", + "multi": false, + "name": "Target", + "options": [], + "query": { + "query": "trilio_target_info{cluster=~\"$Cluster\"}", + "refId": "StandardVariableQuery" + }, + "refresh": 2, + "regex": "/.*target=\"([^\"]*).*/", + "skipUrlSync": false, + "sort": 0, + "tagValuesQuery": "", + "tagsQuery": "", + "type": "query", + "useTags": false + }, + { + "current": {}, + "datasource": { + "type": "loki", + "uid": "${DS_LOKI}" + }, + "definition": "label_values({transaction_type=\"Target\",transaction_resource_name=~\"$Target\"},transaction_resource_namespace)", + "description": "Namespace", + "hide": 0, + "includeAll": true, + "label": "Namespace", + "multi": false, + "name": "Namespace", + "options": [], + "query": "label_values({transaction_type=\"Target\",transaction_resource_name=~\"$Target\"},transaction_resource_namespace)", + "refresh": 2, + "regex": "", + "skipUrlSync": false, + "sort": 0, + "type": "query" + }, + { + "current": {}, + "datasource": { + "type": "loki", + "uid": "${DS_LOKI}" + }, + "definition": "label_values({transaction_type=\"Target\"}, service_type)", + "description": "Service Type", + "hide": 0, + "includeAll": true, + "label": "Service Type", + "multi": false, + "name": "service_type", + "options": [], + "query": "label_values({transaction_type=\"Target\"}, service_type)", + "refresh": 2, + "regex": "", + "skipUrlSync": false, + "sort": 0, + "type": "query" + } + ] + }, + "time": { + "from": "now-15m", + "to": "now" + }, + "timepicker": { + "refresh_intervals": [ + "10s", + "30s", + "1m", + "5m", + "15m", + "30m", + "1h", + "2h", + "1d" + ] + }, + "timezone": "", + "title": "Target Detail", + "uid": "TargetDetail", + "version": 1, + "weekStart": "" +} \ No newline at end of file diff --git a/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/visualization/charts/grafana/templates/NOTES.txt b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/visualization/charts/grafana/templates/NOTES.txt new file mode 100644 index 000000000..1fc8436d9 --- /dev/null +++ b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/visualization/charts/grafana/templates/NOTES.txt @@ -0,0 +1,54 @@ +1. Get your '{{ .Values.adminUser }}' user password by running: + + kubectl get secret --namespace {{ template "grafana.namespace" . }} {{ template "grafana.fullname" . }} -o jsonpath="{.data.admin-password}" | base64 --decode ; echo + +2. The Grafana server can be accessed via port {{ .Values.service.port }} on the following DNS name from within your cluster: + + {{ template "grafana.fullname" . }}.{{ template "grafana.namespace" . }}.svc.cluster.local +{{ if .Values.ingress.enabled }} + If you bind grafana to 80, please update values in values.yaml and reinstall: + ``` + securityContext: + runAsUser: 0 + runAsGroup: 0 + fsGroup: 0 + + command: + - "setcap" + - "'cap_net_bind_service=+ep'" + - "/usr/sbin/grafana-server &&" + - "sh" + - "/run.sh" + ``` + Details refer to https://grafana.com/docs/installation/configuration/#http-port. + Or grafana would always crash. + + From outside the cluster, the server URL(s) are: +{{- range .Values.ingress.hosts }} + http://{{ . }} +{{- end }} +{{ else }} + Get the Grafana URL to visit by running these commands in the same shell: +{{ if contains "NodePort" .Values.service.type -}} + export NODE_PORT=$(kubectl get --namespace {{ template "grafana.namespace" . }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ template "grafana.fullname" . }}) + export NODE_IP=$(kubectl get nodes --namespace {{ template "grafana.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 --namespace {{ template "grafana.namespace" . }} -w {{ template "grafana.fullname" . }}' + export SERVICE_IP=$(kubectl get svc --namespace {{ template "grafana.namespace" . }} {{ template "grafana.fullname" . }} -o jsonpath='{.status.loadBalancer.ingress[0].ip}') + http://$SERVICE_IP:{{ .Values.service.port -}} +{{ else if contains "ClusterIP" .Values.service.type }} + export POD_NAME=$(kubectl get pods --namespace {{ template "grafana.namespace" . }} -l "app.kubernetes.io/name={{ template "grafana.name" . }},app.kubernetes.io/instance={{ .Release.Name }}" -o jsonpath="{.items[0].metadata.name}") + kubectl --namespace {{ template "grafana.namespace" . }} port-forward $POD_NAME 3000 +{{- end }} +{{- end }} + +3. Login with the password from step 1 and the username: {{ .Values.adminUser }} + +{{- if not .Values.persistence.enabled }} +################################################################################# +###### WARNING: Persistence is disabled!!! You will lose your data when ##### +###### the Grafana pod is terminated. ##### +################################################################################# +{{- end }} diff --git a/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/visualization/charts/grafana/templates/_helpers.tpl b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/visualization/charts/grafana/templates/_helpers.tpl new file mode 100644 index 000000000..e3a1fff46 --- /dev/null +++ b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/visualization/charts/grafana/templates/_helpers.tpl @@ -0,0 +1,165 @@ +{{/* vim: set filetype=mustache: */}} +{{/* +Expand the name of the chart. +*/}} +{{- define "grafana.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 "grafana.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 "grafana.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}} +{{- end -}} + +{{/* +Create the name of the service account +*/}} +{{- define "grafana.serviceAccountName" -}} +{{- if .Values.serviceAccount.create -}} + {{ default (include "grafana.fullname" .) .Values.serviceAccount.name }} +{{- else -}} + {{ default "default" .Values.serviceAccount.name }} +{{- end -}} +{{- end -}} + +{{- define "grafana.serviceAccountNameTest" -}} +{{- if .Values.serviceAccount.create -}} + {{ default (print (include "grafana.fullname" .) "-test") .Values.serviceAccount.nameTest }} +{{- else -}} + {{ default "default" .Values.serviceAccount.nameTest }} +{{- end -}} +{{- end -}} + +{{/* +Allow the release namespace to be overridden for multi-namespace deployments in combined charts +*/}} +{{- define "grafana.namespace" -}} + {{- if .Values.namespaceOverride -}} + {{- .Values.namespaceOverride -}} + {{- else -}} + {{- .Release.Namespace -}} + {{- end -}} +{{- end -}} + +{{/* +Common labels +*/}} +{{- define "grafana.labels" -}} +helm.sh/chart: {{ include "grafana.chart" . }} +{{ include "grafana.selectorLabels" . }} +{{- if or .Chart.AppVersion .Values.image.tag }} +app.kubernetes.io/version: {{ .Values.image.tag | default .Chart.AppVersion | quote }} +{{- end }} +app.kubernetes.io/managed-by: {{ .Release.Service }} +{{- if .Values.extraLabels }} +{{ toYaml .Values.extraLabels }} +{{- end }} +{{- end -}} + +{{/* +Selector labels +*/}} +{{- define "grafana.selectorLabels" -}} +app.kubernetes.io/name: {{ include "grafana.name" . }} +app.kubernetes.io/instance: {{ .Release.Name }} +app.kubernetes.io/managed-by: k8s-triliovault-operator +app.kubernetes.io/part-of: k8s-triliovault-operator +{{- end -}} + +{{/* +Common labels +*/}} +{{- define "grafana.imageRenderer.labels" -}} +helm.sh/chart: {{ include "grafana.chart" . }} +{{ include "grafana.imageRenderer.selectorLabels" . }} +{{- if or .Chart.AppVersion .Values.image.tag }} +app.kubernetes.io/version: {{ .Values.image.tag | default .Chart.AppVersion | quote }} +{{- end }} +app.kubernetes.io/managed-by: {{ .Release.Service }} +{{- end -}} + +{{/* +Selector labels ImageRenderer +*/}} +{{- define "grafana.imageRenderer.selectorLabels" -}} +app.kubernetes.io/name: {{ include "grafana.name" . }}-image-renderer +app.kubernetes.io/instance: {{ .Release.Name }} +{{- end -}} + +{{/* +Looks if there's an existing secret and reuse its password. If not it generates +new password and use it. +*/}} +{{- define "grafana.password" -}} +{{- $secret := (lookup "v1" "Secret" (include "grafana.namespace" .) (include "grafana.fullname" .) ) -}} + {{- if $secret -}} + {{- index $secret "data" "admin-password" -}} + {{- else -}} + {{- (randAlphaNum 40) | b64enc | quote -}} + {{- end -}} +{{- end -}} + +{{/* +Return the appropriate apiVersion for rbac. +*/}} +{{- define "grafana.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 "grafana.ingress.apiVersion" -}} + {{- if and (.Capabilities.APIVersions.Has "networking.k8s.io/v1") (semverCompare ">= 1.19-0" .Capabilities.KubeVersion.Version) -}} + {{- 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 "grafana.ingress.isStable" -}} + {{- eq (include "grafana.ingress.apiVersion" .) "networking.k8s.io/v1" -}} +{{- end -}} + +{{/* +Return if ingress supports ingressClassName. +*/}} +{{- define "grafana.ingress.supportsIngressClassName" -}} + {{- or (eq (include "grafana.ingress.isStable" .) "true") (and (eq (include "grafana.ingress.apiVersion" .) "networking.k8s.io/v1beta1") (semverCompare ">= 1.18-0" .Capabilities.KubeVersion.Version)) -}} +{{- end -}} + +{{/* +Return if ingress supports pathType. +*/}} +{{- define "grafana.ingress.supportsPathType" -}} + {{- or (eq (include "grafana.ingress.isStable" .) "true") (and (eq (include "grafana.ingress.apiVersion" .) "networking.k8s.io/v1beta1") (semverCompare ">= 1.18-0" .Capabilities.KubeVersion.Version)) -}} +{{- end -}} diff --git a/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/visualization/charts/grafana/templates/_pod.tpl b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/visualization/charts/grafana/templates/_pod.tpl new file mode 100644 index 000000000..af2c4036e --- /dev/null +++ b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/visualization/charts/grafana/templates/_pod.tpl @@ -0,0 +1,748 @@ + +{{- define "grafana.pod" -}} +{{- if .Values.schedulerName }} +schedulerName: "{{ .Values.schedulerName }}" +{{- end }} +serviceAccountName: {{ template "grafana.serviceAccountName" . }} +automountServiceAccountToken: {{ .Values.serviceAccount.autoMount }} +{{- if .Values.securityContext }} +securityContext: +{{ toYaml .Values.securityContext | indent 2 }} +{{- end }} +{{- if .Values.hostAliases }} +hostAliases: +{{ toYaml .Values.hostAliases | indent 2 }} +{{- end }} +{{- if .Values.priorityClassName }} +priorityClassName: {{ .Values.priorityClassName }} +{{- end }} +{{- if ( or .Values.persistence.enabled .Values.dashboards .Values.sidecar.notifiers.enabled .Values.extraInitContainers (and .Values.sidecar.datasources.enabled .Values.sidecar.datasources.initDatasources)) }} +initContainers: +{{- end }} +{{- if ( and .Values.persistence.enabled .Values.initChownData.enabled ) }} + - name: init-chown-data + {{- if .Values.initChownData.image.sha }} + image: "{{ .Values.initChownData.image.registry }}/{{ .Values.initChownData.image.repository }}:{{ .Values.initChownData.image.tag }}@sha256:{{ .Values.initChownData.image.sha }}" + {{- else }} + image: "{{ .Values.initChownData.image.registry }}/{{ .Values.initChownData.image.repository }}:{{ .Values.initChownData.image.tag }}" + {{- end }} + imagePullPolicy: {{ .Values.initChownData.image.pullPolicy }} + securityContext: + runAsNonRoot: false + runAsUser: 0 + command: ["chown", "-R", "{{ .Values.securityContext.runAsUser }}:{{ .Values.securityContext.runAsGroup }}", "/var/lib/grafana"] + resources: +{{ toYaml .Values.initChownData.resources | indent 6 }} + volumeMounts: + - name: storage + mountPath: "/var/lib/grafana" +{{- if .Values.persistence.subPath }} + subPath: {{ tpl .Values.persistence.subPath . }} +{{- end }} +{{- end }} +{{- if .Values.dashboards }} + - name: download-dashboards + {{- if .Values.downloadDashboardsImage.sha }} + image: "{{ .Values.downloadDashboardsImage.registry }}/{{ .Values.downloadDashboardsImage.repository }}:{{ .Values.downloadDashboardsImage.tag }}@sha256:{{ .Values.downloadDashboardsImage.sha }}" + {{- else }} + image: "{{ .Values.downloadDashboardsImage.registry }}/{{ .Values.downloadDashboardsImage.repository }}:{{ .Values.downloadDashboardsImage.tag }}" + {{- end }} + imagePullPolicy: {{ .Values.downloadDashboardsImage.pullPolicy }} + command: ["/bin/sh"] + args: [ "-c", "mkdir -p /var/lib/grafana/dashboards/default && /bin/sh -x /etc/grafana/download_dashboards.sh" ] + resources: +{{ toYaml .Values.downloadDashboards.resources | indent 6 }} + env: +{{- range $key, $value := .Values.downloadDashboards.env }} + - name: "{{ $key }}" + value: "{{ $value }}" +{{- end }} +{{- if .Values.downloadDashboards.envFromSecret }} + envFrom: + - secretRef: + name: {{ tpl .Values.downloadDashboards.envFromSecret . }} +{{- end }} + volumeMounts: + - name: config + mountPath: "/etc/grafana/download_dashboards.sh" + subPath: download_dashboards.sh + - name: storage + mountPath: "/var/lib/grafana" +{{- if .Values.persistence.subPath }} + subPath: {{ tpl .Values.persistence.subPath . }} +{{- end }} + {{- range .Values.extraSecretMounts }} + - name: {{ .name }} + mountPath: {{ .mountPath }} + readOnly: {{ .readOnly }} + {{- end }} +{{- end }} +{{- if and .Values.sidecar.datasources.enabled .Values.sidecar.datasources.initDatasources }} + - name: {{ template "grafana.name" . }}-init-sc-datasources + {{- if .Values.sidecar.image.sha }} + image: "{{ .Values.sidecar.image.registry }}/{{ .Values.sidecar.image.repository }}:{{ .Values.sidecar.image.tag }}@sha256:{{ .Values.sidecar.image.sha }}" + {{- else }} + image: "{{ .Values.sidecar.image.registry }}/{{ .Values.sidecar.image.repository }}:{{ .Values.sidecar.image.tag }}" + {{- end }} + imagePullPolicy: {{ .Values.sidecar.imagePullPolicy }} + env: + - name: METHOD + value: "LIST" + - name: LABEL + value: "{{ .Values.sidecar.datasources.label }}" + {{- if .Values.sidecar.datasources.labelValue }} + - name: LABEL_VALUE + value: {{ quote .Values.sidecar.datasources.labelValue }} + {{- end }} + - name: FOLDER + value: "/etc/grafana/provisioning/datasources" + - name: RESOURCE + value: {{ quote .Values.sidecar.datasources.resource }} + {{- if .Values.sidecar.enableUniqueFilenames }} + - name: UNIQUE_FILENAMES + value: "{{ .Values.sidecar.enableUniqueFilenames }}" + {{- end }} + {{- if .Values.sidecar.datasources.searchNamespace }} + - name: NAMESPACE + value: "{{ .Values.sidecar.datasources.searchNamespace | join "," }}" + {{- end }} + {{- if .Values.sidecar.skipTlsVerify }} + - name: SKIP_TLS_VERIFY + value: "{{ .Values.sidecar.skipTlsVerify }}" + {{- end }} + resources: +{{ toYaml .Values.sidecar.resources | indent 6 }} +{{- if .Values.sidecar.securityContext }} + securityContext: +{{- toYaml .Values.sidecar.securityContext | nindent 6 }} +{{- end }} + volumeMounts: + - name: sc-datasources-volume + mountPath: "/etc/grafana/provisioning/datasources" +{{- end }} +{{- if .Values.sidecar.notifiers.enabled }} + - name: {{ template "grafana.name" . }}-sc-notifiers + {{- if .Values.sidecar.image.sha }} + image: "{{ .Values.sidecar.image.registry }}/{{ .Values.sidecar.image.repository }}:{{ .Values.sidecar.image.tag }}@sha256:{{ .Values.sidecar.image.sha }}" + {{- else }} + image: "{{ .Values.sidecar.image.registry }}/{{ .Values.sidecar.image.repository }}:{{ .Values.sidecar.image.tag }}" + {{- end }} + imagePullPolicy: {{ .Values.sidecar.imagePullPolicy }} + env: + - name: METHOD + value: LIST + - name: LABEL + value: "{{ .Values.sidecar.notifiers.label }}" + - name: FOLDER + value: "/etc/grafana/provisioning/notifiers" + - name: RESOURCE + value: {{ quote .Values.sidecar.notifiers.resource }} + {{- if .Values.sidecar.enableUniqueFilenames }} + - name: UNIQUE_FILENAMES + value: "{{ .Values.sidecar.enableUniqueFilenames }}" + {{- end }} + {{- if .Values.sidecar.notifiers.searchNamespace }} + - name: NAMESPACE + value: "{{ .Values.sidecar.notifiers.searchNamespace | join "," }}" + {{- end }} + {{- if .Values.sidecar.skipTlsVerify }} + - name: SKIP_TLS_VERIFY + value: "{{ .Values.sidecar.skipTlsVerify }}" + {{- end }} +{{- if .Values.sidecar.livenessProbe }} + livenessProbe: +{{ toYaml .Values.livenessProbe | indent 6 }} +{{- end }} +{{- if .Values.sidecar.readinessProbe }} + readinessProbe: +{{ toYaml .Values.readinessProbe | indent 6 }} +{{- end }} + resources: +{{ toYaml .Values.sidecar.resources | indent 6 }} +{{- if .Values.sidecar.securityContext }} + securityContext: +{{- toYaml .Values.sidecar.securityContext | nindent 6 }} +{{- end }} + volumeMounts: + - name: sc-notifiers-volume + mountPath: "/etc/grafana/provisioning/notifiers" +{{- end}} +{{- if .Values.extraInitContainers }} +{{ tpl (toYaml .Values.extraInitContainers) . | indent 2 }} +{{- end }} +{{- if .Values.image.pullSecrets }} +imagePullSecrets: +{{- $root := . }} +{{- range .Values.image.pullSecrets }} + - name: {{ tpl . $root }} +{{- end}} +{{- end }} +{{- if not .Values.enableKubeBackwardCompatibility }} +enableServiceLinks: {{ .Values.enableServiceLinks }} +{{- end }} +containers: +{{- if .Values.sidecar.dashboards.enabled }} + - name: {{ template "grafana.name" . }}-sc-dashboard + {{- if .Values.sidecar.image.sha }} + image: "{{ .Values.sidecar.image.registry }}{{ .Values.sidecar.image.repository }}:{{ .Values.sidecar.image.tag }}@sha256:{{ .Values.sidecar.image.sha }}" + {{- else }} + image: "{{ .Values.sidecar.image.registry }}/{{ .Values.sidecar.image.repository }}:{{ .Values.sidecar.image.tag }}" + {{- end }} + imagePullPolicy: {{ .Values.sidecar.imagePullPolicy }} + env: + - name: METHOD + value: {{ .Values.sidecar.dashboards.watchMethod }} + - name: LABEL + value: "{{ .Values.sidecar.dashboards.label }}" + {{- if .Values.sidecar.dashboards.labelValue }} + - name: LABEL_VALUE + value: {{ quote .Values.sidecar.dashboards.labelValue }} + {{- end }} + - name: FOLDER + value: "{{ .Values.sidecar.dashboards.folder }}{{- with .Values.sidecar.dashboards.defaultFolderName }}/{{ . }}{{- end }}" + - name: RESOURCE + value: {{ quote .Values.sidecar.dashboards.resource }} + {{- if .Values.sidecar.enableUniqueFilenames }} + - name: UNIQUE_FILENAMES + value: "{{ .Values.sidecar.enableUniqueFilenames }}" + {{- end }} + {{- if .Values.sidecar.dashboards.searchNamespace }} + - name: NAMESPACE + value: "{{ .Values.sidecar.dashboards.searchNamespace | join "," }}" + {{- end }} + {{- if .Values.sidecar.skipTlsVerify }} + - name: SKIP_TLS_VERIFY + value: "{{ .Values.sidecar.skipTlsVerify }}" + {{- end }} + {{- if .Values.sidecar.dashboards.folderAnnotation }} + - name: FOLDER_ANNOTATION + value: "{{ .Values.sidecar.dashboards.folderAnnotation }}" + {{- end }} + {{- if .Values.sidecar.dashboards.script }} + - name: SCRIPT + value: "{{ .Values.sidecar.dashboards.script }}" + {{- end }} + {{- if .Values.sidecar.dashboards.watchServerTimeout }} + - name: WATCH_SERVER_TIMEOUT + value: "{{ .Values.sidecar.dashboards.watchServerTimeout }}" + {{- end }} + {{- if .Values.sidecar.dashboards.watchClientTimeout }} + - name: WATCH_CLIENT_TIMEOUT + value: "{{ .Values.sidecar.dashboards.watchClientTimeout }}" + {{- end }} +{{- if .Values.sidecar.livenessProbe }} + livenessProbe: +{{ toYaml .Values.livenessProbe | indent 6 }} +{{- end }} +{{- if .Values.sidecar.readinessProbe }} + readinessProbe: +{{ toYaml .Values.readinessProbe | indent 6 }} +{{- end }} + resources: +{{ toYaml .Values.sidecar.resources | indent 6 }} +{{- if .Values.sidecar.securityContext }} + securityContext: +{{- toYaml .Values.sidecar.securityContext | nindent 6 }} +{{- end }} + volumeMounts: + - name: sc-dashboard-volume + mountPath: {{ .Values.sidecar.dashboards.folder | quote }} + {{- if .Values.sidecar.dashboards.extraMounts }} + {{- toYaml .Values.sidecar.dashboards.extraMounts | trim | nindent 6}} + {{- end }} +{{- end}} +{{- if .Values.sidecar.datasources.enabled }} + - name: {{ template "grafana.name" . }}-sc-datasources + {{- if .Values.sidecar.image.sha }} + image: "{{ .Values.sidecar.image.registry }}/{{ .Values.sidecar.image.repository }}:{{ .Values.sidecar.image.tag }}@sha256:{{ .Values.sidecar.image.sha }}" + {{- else }} + image: "{{ .Values.sidecar.image.registry }}/{{ .Values.sidecar.image.repository }}:{{ .Values.sidecar.image.tag }}" + {{- end }} + imagePullPolicy: {{ .Values.sidecar.imagePullPolicy }} + env: + - name: METHOD + value: {{ .Values.sidecar.datasources.watchMethod }} + - name: LABEL + value: "{{ .Values.sidecar.datasources.label }}" + {{- if .Values.sidecar.datasources.labelValue }} + - name: LABEL_VALUE + value: {{ quote .Values.sidecar.datasources.labelValue }} + {{- end }} + - name: FOLDER + value: "/etc/grafana/provisioning/datasources" + - name: RESOURCE + value: {{ quote .Values.sidecar.datasources.resource }} + {{- if .Values.sidecar.enableUniqueFilenames }} + - name: UNIQUE_FILENAMES + value: "{{ .Values.sidecar.enableUniqueFilenames }}" + {{- end }} + {{- if .Values.sidecar.datasources.searchNamespace }} + - name: NAMESPACE + value: "{{ .Values.sidecar.datasources.searchNamespace | join "," }}" + {{- end }} + {{- if .Values.sidecar.skipTlsVerify }} + - name: SKIP_TLS_VERIFY + value: "{{ .Values.sidecar.skipTlsVerify }}" + {{- end }} + {{- if and (not .Values.env.GF_SECURITY_ADMIN_USER) (not .Values.env.GF_SECURITY_DISABLE_INITIAL_ADMIN_CREATION) }} + - name: REQ_USERNAME + valueFrom: + secretKeyRef: + name: {{ (tpl .Values.admin.existingSecret .) | default (include "grafana.fullname" .) }} + key: {{ .Values.admin.userKey | default "admin-user" }} + {{- end }} + {{- if and (not .Values.env.GF_SECURITY_ADMIN_PASSWORD) (not .Values.env.GF_SECURITY_ADMIN_PASSWORD__FILE) (not .Values.env.GF_SECURITY_DISABLE_INITIAL_ADMIN_CREATION) }} + - name: REQ_PASSWORD + valueFrom: + secretKeyRef: + name: {{ (tpl .Values.admin.existingSecret .) | default (include "grafana.fullname" .) }} + key: {{ .Values.admin.passwordKey | default "admin-password" }} + {{- end }} + {{- if not .Values.sidecar.datasources.skipReload }} + - name: REQ_URL + value: {{ .Values.sidecar.datasources.reloadURL }} + - name: REQ_METHOD + value: POST + {{- end }} +{{- if .Values.sidecar.livenessProbe }} + livenessProbe: +{{ toYaml .Values.livenessProbe | indent 6 }} +{{- end }} +{{- if .Values.sidecar.readinessProbe }} + readinessProbe: +{{ toYaml .Values.readinessProbe | indent 6 }} +{{- end }} + resources: +{{ toYaml .Values.sidecar.resources | indent 6 }} +{{- if .Values.sidecar.securityContext }} + securityContext: +{{- toYaml .Values.sidecar.securityContext | nindent 6 }} +{{- end }} + volumeMounts: + - name: sc-datasources-volume + mountPath: "/etc/grafana/provisioning/datasources" +{{- end}} +{{- if .Values.sidecar.plugins.enabled }} + - name: {{ template "grafana.name" . }}-sc-plugins + {{- if .Values.sidecar.image.sha }} + image: "{{ .Values.sidecar.image.registry }}/{{ .Values.sidecar.image.repository }}:{{ .Values.sidecar.image.tag }}@sha256:{{ .Values.sidecar.image.sha }}" + {{- else }} + image: "{{ .Values.sidecar.image.registry }}/{{ .Values.sidecar.image.repository }}:{{ .Values.sidecar.image.tag }}" + {{- end }} + imagePullPolicy: {{ .Values.sidecar.imagePullPolicy }} + env: + - name: METHOD + value: {{ .Values.sidecar.plugins.watchMethod }} + - name: LABEL + value: "{{ .Values.sidecar.plugins.label }}" + {{- if .Values.sidecar.plugins.labelValue }} + - name: LABEL_VALUE + value: {{ quote .Values.sidecar.plugins.labelValue }} + {{- end }} + - name: FOLDER + value: "/etc/grafana/provisioning/plugins" + - name: RESOURCE + value: {{ quote .Values.sidecar.plugins.resource }} + {{- if .Values.sidecar.enableUniqueFilenames }} + - name: UNIQUE_FILENAMES + value: "{{ .Values.sidecar.enableUniqueFilenames }}" + {{- end }} + {{- if .Values.sidecar.plugins.searchNamespace }} + - name: NAMESPACE + value: "{{ .Values.sidecar.plugins.searchNamespace | join "," }}" + {{- end }} + {{- if .Values.sidecar.skipTlsVerify }} + - name: SKIP_TLS_VERIFY + value: "{{ .Values.sidecar.skipTlsVerify }}" + {{- end }} + {{- if and (not .Values.env.GF_SECURITY_ADMIN_USER) (not .Values.env.GF_SECURITY_DISABLE_INITIAL_ADMIN_CREATION) }} + - name: REQ_USERNAME + valueFrom: + secretKeyRef: + name: {{ (tpl .Values.admin.existingSecret .) | default (include "grafana.fullname" .) }} + key: {{ .Values.admin.userKey | default "admin-user" }} + {{- end }} + {{- if and (not .Values.env.GF_SECURITY_ADMIN_PASSWORD) (not .Values.env.GF_SECURITY_ADMIN_PASSWORD__FILE) (not .Values.env.GF_SECURITY_DISABLE_INITIAL_ADMIN_CREATION) }} + - name: REQ_PASSWORD + valueFrom: + secretKeyRef: + name: {{ (tpl .Values.admin.existingSecret .) | default (include "grafana.fullname" .) }} + key: {{ .Values.admin.passwordKey | default "admin-password" }} + {{- end }} + {{- if not .Values.sidecar.plugins.skipReload }} + - name: REQ_URL + value: {{ .Values.sidecar.plugins.reloadURL }} + - name: REQ_METHOD + value: POST + {{- end }} +{{- if .Values.sidecar.livenessProbe }} + livenessProbe: +{{ toYaml .Values.livenessProbe | indent 6 }} +{{- end }} +{{- if .Values.sidecar.readinessProbe }} + readinessProbe: +{{ toYaml .Values.readinessProbe | indent 6 }} +{{- end }} + resources: +{{ toYaml .Values.sidecar.resources | indent 6 }} +{{- if .Values.sidecar.securityContext }} + securityContext: +{{- toYaml .Values.sidecar.securityContext | nindent 6 }} +{{- end }} + volumeMounts: + - name: sc-plugins-volume + mountPath: "/etc/grafana/provisioning/plugins" +{{- end}} + - name: {{ .Chart.Name }} + {{- if .Values.image.sha }} + image: "{{ .Values.image.registry }}/{{ .Values.image.repository }}:{{ .Values.image.tag }}@sha256:{{ .Values.image.sha }}" + {{- else }} + image: "{{ .Values.image.registry }}/{{ .Values.image.repository }}:{{ .Values.image.tag }}" + {{- end }} + imagePullPolicy: {{ .Values.image.pullPolicy }} + {{- if .Values.command }} + command: + {{- range .Values.command }} + - {{ . }} + {{- end }} + {{- end}} +{{- if .Values.containerSecurityContext }} + securityContext: +{{- toYaml .Values.containerSecurityContext | nindent 6 }} +{{- end }} + volumeMounts: + - name: config + mountPath: "/etc/grafana/grafana.ini" + subPath: grafana.ini + {{- if .Values.ldap.enabled }} + - name: ldap + mountPath: "/etc/grafana/ldap.toml" + subPath: ldap.toml + {{- end }} + {{- $root := . }} + {{- range .Values.extraConfigmapMounts }} + - name: {{ tpl .name $root }} + mountPath: {{ tpl .mountPath $root }} + subPath: {{ (tpl .subPath $root) | default "" }} + readOnly: {{ .readOnly }} + {{- end }} + - name: storage + mountPath: "/var/lib/grafana" +{{- if .Values.persistence.subPath }} + subPath: {{ tpl .Values.persistence.subPath . }} +{{- end }} +{{- if .Values.dashboards }} +{{- range $provider, $dashboards := .Values.dashboards }} +{{- range $key, $value := $dashboards }} +{{- if (or (hasKey $value "json") (hasKey $value "file")) }} + - name: dashboards-{{ $provider }} + mountPath: "/var/lib/grafana/dashboards/{{ $provider }}/{{ $key }}.json" + subPath: "{{ $key }}.json" +{{- end }} +{{- end }} +{{- end }} +{{- end -}} +{{- if .Values.dashboardsConfigMaps }} +{{- range (keys .Values.dashboardsConfigMaps | sortAlpha) }} + - name: dashboards-{{ . }} + mountPath: "/var/lib/grafana/dashboards/{{ . }}" +{{- end }} +{{- end }} +{{- if .Values.datasources }} +{{- range (keys .Values.datasources | sortAlpha) }} + - name: config + mountPath: "/etc/grafana/provisioning/datasources/{{ . }}" + subPath: {{ . | quote }} +{{- end }} +{{- end }} +{{- if .Values.notifiers }} +{{- range (keys .Values.notifiers | sortAlpha) }} + - name: config + mountPath: "/etc/grafana/provisioning/notifiers/{{ . }}" + subPath: {{ . | quote }} +{{- end }} +{{- end }} +{{- if .Values.dashboardProviders }} +{{- range (keys .Values.dashboardProviders | sortAlpha) }} + - name: config + mountPath: "/etc/grafana/provisioning/dashboards/{{ . }}" + subPath: {{ . | quote }} +{{- end }} +{{- end }} +{{- if .Values.sidecar.dashboards.enabled }} + - name: sc-dashboard-volume + mountPath: {{ .Values.sidecar.dashboards.folder | quote }} +{{ if .Values.sidecar.dashboards.SCProvider }} + - name: sc-dashboard-provider + mountPath: "/etc/grafana/provisioning/dashboards/sc-dashboardproviders.yaml" + subPath: provider.yaml +{{- end}} +{{- end}} +{{- if .Values.sidecar.datasources.enabled }} + - name: sc-datasources-volume + mountPath: "/etc/grafana/provisioning/datasources" +{{- end}} +{{- if .Values.sidecar.plugins.enabled }} + - name: sc-plugins-volume + mountPath: "/etc/grafana/provisioning/plugins" +{{- end}} +{{- if .Values.sidecar.notifiers.enabled }} + - name: sc-notifiers-volume + mountPath: "/etc/grafana/provisioning/notifiers" +{{- end}} + {{- range .Values.extraSecretMounts }} + - name: {{ .name }} + mountPath: {{ .mountPath }} + readOnly: {{ .readOnly }} + subPath: {{ .subPath | default "" }} + {{- end }} + {{- range .Values.extraVolumeMounts }} + - name: {{ .name }} + mountPath: {{ .mountPath }} + subPath: {{ .subPath | default "" }} + readOnly: {{ .readOnly }} + {{- end }} + {{- range .Values.extraEmptyDirMounts }} + - name: {{ .name }} + mountPath: {{ .mountPath }} + {{- end }} + ports: + - name: {{ .Values.service.portName }} + containerPort: {{ .Values.service.port }} + protocol: TCP + - name: {{ .Values.podPortName }} + containerPort: 3000 + protocol: TCP + env: + {{- if and (not .Values.env.GF_SECURITY_ADMIN_USER) (not .Values.env.GF_SECURITY_DISABLE_INITIAL_ADMIN_CREATION) }} + - name: GF_SECURITY_ADMIN_USER + valueFrom: + secretKeyRef: + name: {{ (tpl .Values.admin.existingSecret .) | default (include "grafana.fullname" .) }} + key: {{ .Values.admin.userKey | default "admin-user" }} + {{- end }} + {{- if and (not .Values.env.GF_SECURITY_ADMIN_PASSWORD) (not .Values.env.GF_SECURITY_ADMIN_PASSWORD__FILE) (not .Values.env.GF_SECURITY_DISABLE_INITIAL_ADMIN_CREATION) }} + - name: GF_SECURITY_ADMIN_PASSWORD + valueFrom: + secretKeyRef: + name: {{ (tpl .Values.admin.existingSecret .) | default (include "grafana.fullname" .) }} + key: {{ .Values.admin.passwordKey | default "admin-password" }} + {{- end }} + {{- if .Values.plugins }} + - name: GF_INSTALL_PLUGINS + valueFrom: + configMapKeyRef: + name: {{ template "grafana.fullname" . }} + key: plugins + {{- end }} + {{- if .Values.smtp.existingSecret }} + - name: GF_SMTP_USER + valueFrom: + secretKeyRef: + name: {{ .Values.smtp.existingSecret }} + key: {{ .Values.smtp.userKey | default "user" }} + - name: GF_SMTP_PASSWORD + valueFrom: + secretKeyRef: + name: {{ .Values.smtp.existingSecret }} + key: {{ .Values.smtp.passwordKey | default "password" }} + {{- end }} + {{- if .Values.imageRenderer.enabled }} + - name: GF_RENDERING_SERVER_URL + value: http://{{ template "grafana.fullname" . }}-image-renderer.{{ template "grafana.namespace" . }}:{{ .Values.imageRenderer.service.port }}/render + - name: GF_RENDERING_CALLBACK_URL + value: {{ .Values.imageRenderer.grafanaProtocol }}://{{ template "grafana.fullname" . }}.{{ template "grafana.namespace" . }}:{{ .Values.service.port }}/{{ .Values.imageRenderer.grafanaSubPath }} + {{- end }} + - name: GF_PATHS_DATA + value: {{ (get .Values "grafana.ini").paths.data }} + - name: GF_PATHS_LOGS + value: {{ (get .Values "grafana.ini").paths.logs }} + - name: GF_PATHS_PLUGINS + value: {{ (get .Values "grafana.ini").paths.plugins }} + - name: GF_PATHS_PROVISIONING + value: {{ (get .Values "grafana.ini").paths.provisioning }} + {{- range $key, $value := .Values.envValueFrom }} + - name: {{ $key | quote }} + valueFrom: +{{ tpl (toYaml $value) $ | indent 10 }} + {{- end }} +{{- range $key, $value := .Values.env }} + - name: "{{ tpl $key $ }}" + value: "{{ tpl (print $value) $ }}" +{{- end }} + {{- if or .Values.envFromSecret (or .Values.envRenderSecret .Values.envFromSecrets) .Values.envFromConfigMaps }} + envFrom: + {{- if .Values.envFromSecret }} + - secretRef: + name: {{ tpl .Values.envFromSecret . }} + {{- end }} + {{- if .Values.envRenderSecret }} + - secretRef: + name: {{ template "grafana.fullname" . }}-env + {{- end }} + {{- range .Values.envFromSecrets }} + - secretRef: + name: {{ tpl .name $ }} + optional: {{ .optional | default false }} + {{- end }} + {{- range .Values.envFromConfigMaps }} + - configMapRef: + name: {{ tpl .name $ }} + optional: {{ .optional | default false }} + {{- end }} + {{- end }} + livenessProbe: +{{ toYaml .Values.livenessProbe | indent 6 }} + readinessProbe: +{{ toYaml .Values.readinessProbe | indent 6 }} +{{- if .Values.lifecycleHooks }} + lifecycle: {{ tpl (.Values.lifecycleHooks | toYaml) . | nindent 6 }} +{{- end }} + resources: +{{ toYaml .Values.resources | indent 6 }} +{{- with .Values.extraContainers }} +{{ tpl . $ | indent 2 }} +{{- end }} +{{- with .Values.nodeSelector }} +nodeSelector: +{{ toYaml . | indent 2 }} +{{- end }} +{{- $root := . }} +{{- with .Values.affinity }} +affinity: +{{ tpl (toYaml .) $root | indent 2 }} +{{- end }} +{{- with .Values.tolerations }} +tolerations: +{{ toYaml . | indent 2 }} +{{- end }} +volumes: + - name: config + configMap: + name: {{ template "grafana.fullname" . }} +{{- $root := . }} +{{- range .Values.extraConfigmapMounts }} + - name: {{ tpl .name $root }} + configMap: + name: {{ tpl .configMap $root }} +{{- end }} + {{- if .Values.dashboards }} + {{- range (keys .Values.dashboards | sortAlpha) }} + - name: dashboards-{{ . }} + configMap: + name: {{ template "grafana.fullname" $ }}-dashboards-{{ . }} + {{- end }} + {{- end }} + {{- if .Values.dashboardsConfigMaps }} + {{ $root := . }} + {{- range $provider, $name := .Values.dashboardsConfigMaps }} + - name: dashboards-{{ $provider }} + configMap: + name: {{ tpl $name $root }} + {{- end }} + {{- end }} + {{- if .Values.ldap.enabled }} + - name: ldap + secret: + {{- if .Values.ldap.existingSecret }} + secretName: {{ .Values.ldap.existingSecret }} + {{- else }} + secretName: {{ template "grafana.fullname" . }} + {{- end }} + items: + - key: ldap-toml + path: ldap.toml + {{- end }} +{{- if and .Values.persistence.enabled (eq .Values.persistence.type "pvc") }} + - name: storage + persistentVolumeClaim: + claimName: {{ tpl (.Values.persistence.existingClaim | default (include "grafana.fullname" .)) . }} +{{- else if and .Values.persistence.enabled (eq .Values.persistence.type "statefulset") }} +# nothing +{{- else }} + - name: storage +{{- if .Values.persistence.inMemory.enabled }} + emptyDir: + medium: Memory +{{- if .Values.persistence.inMemory.sizeLimit }} + sizeLimit: {{ .Values.persistence.inMemory.sizeLimit }} +{{- end -}} +{{- else }} + emptyDir: {} +{{- end -}} +{{- end -}} +{{- if .Values.sidecar.dashboards.enabled }} + - name: sc-dashboard-volume +{{- if .Values.sidecar.dashboards.sizeLimit }} + emptyDir: + sizeLimit: {{ .Values.sidecar.dashboards.sizeLimit }} +{{- else }} + emptyDir: {} +{{- end -}} +{{- if .Values.sidecar.dashboards.SCProvider }} + - name: sc-dashboard-provider + configMap: + name: {{ template "grafana.fullname" . }}-config-dashboards +{{- end }} +{{- end }} +{{- if .Values.sidecar.datasources.enabled }} + - name: sc-datasources-volume +{{- if .Values.sidecar.datasources.sizeLimit }} + emptyDir: + sizeLimit: {{ .Values.sidecar.datasources.sizeLimit }} +{{- else }} + emptyDir: {} +{{- end -}} +{{- end -}} +{{- if .Values.sidecar.plugins.enabled }} + - name: sc-plugins-volume +{{- if .Values.sidecar.plugins.sizeLimit }} + emptyDir: + sizeLimit: {{ .Values.sidecar.plugins.sizeLimit }} +{{- else }} + emptyDir: {} +{{- end -}} +{{- end -}} +{{- if .Values.sidecar.notifiers.enabled }} + - name: sc-notifiers-volume +{{- if .Values.sidecar.notifiers.sizeLimit }} + emptyDir: + sizeLimit: {{ .Values.sidecar.notifiers.sizeLimit }} +{{- else }} + emptyDir: {} +{{- end -}} +{{- end -}} +{{- range .Values.extraSecretMounts }} +{{- if .secretName }} + - name: {{ .name }} + secret: + secretName: {{ .secretName }} + defaultMode: {{ .defaultMode }} +{{- else if .projected }} + - name: {{ .name }} + projected: {{- toYaml .projected | nindent 6 }} +{{- else if .csi }} + - name: {{ .name }} + csi: {{- toYaml .csi | nindent 6 }} +{{- end }} +{{- end }} +{{- range .Values.extraVolumeMounts }} + - name: {{ .name }} + {{- if .existingClaim }} + persistentVolumeClaim: + claimName: {{ .existingClaim }} + {{- else if .hostPath }} + hostPath: + path: {{ .hostPath }} + {{- else }} + emptyDir: {} + {{- end }} +{{- end }} +{{- range .Values.extraEmptyDirMounts }} + - name: {{ .name }} + emptyDir: {} +{{- end -}} +{{- if .Values.extraContainerVolumes }} +{{ toYaml .Values.extraContainerVolumes | indent 2 }} +{{- end }} +{{- end }} diff --git a/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/visualization/charts/grafana/templates/clusterrole.yaml b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/visualization/charts/grafana/templates/clusterrole.yaml new file mode 100644 index 000000000..f09e06563 --- /dev/null +++ b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/visualization/charts/grafana/templates/clusterrole.yaml @@ -0,0 +1,25 @@ +{{- if and .Values.rbac.create (not .Values.rbac.namespaced) (not .Values.rbac.useExistingRole) }} +kind: ClusterRole +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + labels: + {{- include "grafana.labels" . | nindent 4 }} +{{- with .Values.annotations }} + annotations: +{{ toYaml . | indent 4 }} +{{- end }} + name: {{ template "grafana.fullname" . }}-clusterrole +{{- if or .Values.sidecar.dashboards.enabled (or .Values.sidecar.datasources.enabled .Values.rbac.extraClusterRoleRules) }} +rules: +{{- if or .Values.sidecar.dashboards.enabled .Values.sidecar.datasources.enabled }} +- apiGroups: [""] # "" indicates the core API group + resources: ["configmaps", "secrets"] + verbs: ["get", "watch", "list"] +{{- end}} +{{- with .Values.rbac.extraClusterRoleRules }} +{{ toYaml . | indent 0 }} +{{- end}} +{{- else }} +rules: [] +{{- end}} +{{- end}} diff --git a/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/visualization/charts/grafana/templates/clusterrolebinding.yaml b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/visualization/charts/grafana/templates/clusterrolebinding.yaml new file mode 100644 index 000000000..4accbfac0 --- /dev/null +++ b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/visualization/charts/grafana/templates/clusterrolebinding.yaml @@ -0,0 +1,24 @@ +{{- if and .Values.rbac.create (not .Values.rbac.namespaced) }} +kind: ClusterRoleBinding +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: {{ template "grafana.fullname" . }}-clusterrolebinding + labels: + {{- include "grafana.labels" . | nindent 4 }} +{{- with .Values.annotations }} + annotations: +{{ toYaml . | indent 4 }} +{{- end }} +subjects: + - kind: ServiceAccount + name: {{ template "grafana.serviceAccountName" . }} + namespace: {{ template "grafana.namespace" . }} +roleRef: + kind: ClusterRole +{{- if (not .Values.rbac.useExistingRole) }} + name: {{ template "grafana.fullname" . }}-clusterrole +{{- else }} + name: {{ .Values.rbac.useExistingRole }} +{{- end }} + apiGroup: rbac.authorization.k8s.io +{{- end -}} diff --git a/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/visualization/charts/grafana/templates/configmap-dashboard-provider.yaml b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/visualization/charts/grafana/templates/configmap-dashboard-provider.yaml new file mode 100644 index 000000000..65d73858e --- /dev/null +++ b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/visualization/charts/grafana/templates/configmap-dashboard-provider.yaml @@ -0,0 +1,29 @@ +{{- if .Values.sidecar.dashboards.enabled }} +apiVersion: v1 +kind: ConfigMap +metadata: + labels: + {{- include "grafana.labels" . | nindent 4 }} +{{- with .Values.annotations }} + annotations: +{{ toYaml . | indent 4 }} +{{- end }} + name: {{ template "grafana.fullname" . }}-config-dashboards + namespace: {{ template "grafana.namespace" . }} +data: + provider.yaml: |- + apiVersion: 1 + providers: + - name: '{{ .Values.sidecar.dashboards.provider.name }}' + orgId: {{ .Values.sidecar.dashboards.provider.orgid }} + {{- if not .Values.sidecar.dashboards.provider.foldersFromFilesStructure }} + folder: '{{ .Values.sidecar.dashboards.provider.folder }}' + {{- end}} + type: {{ .Values.sidecar.dashboards.provider.type }} + disableDeletion: {{ .Values.sidecar.dashboards.provider.disableDelete }} + allowUiUpdates: {{ .Values.sidecar.dashboards.provider.allowUiUpdates }} + updateIntervalSeconds: {{ .Values.sidecar.dashboards.provider.updateIntervalSeconds | default 30 }} + options: + foldersFromFilesStructure: {{ .Values.sidecar.dashboards.provider.foldersFromFilesStructure }} + path: {{ .Values.sidecar.dashboards.folder }}{{- with .Values.sidecar.dashboards.defaultFolderName }}/{{ . }}{{- end }} +{{- end}} diff --git a/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/visualization/charts/grafana/templates/configmap.yaml b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/visualization/charts/grafana/templates/configmap.yaml new file mode 100644 index 000000000..401e2aaa6 --- /dev/null +++ b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/visualization/charts/grafana/templates/configmap.yaml @@ -0,0 +1,88 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ template "grafana.fullname" . }} + namespace: {{ template "grafana.namespace" . }} + labels: + {{- include "grafana.labels" . | nindent 4 }} +{{- with .Values.annotations }} + annotations: +{{ toYaml . | indent 4 }} +{{- end }} +data: +{{- if .Values.plugins }} + plugins: {{ join "," .Values.plugins }} +{{- end }} + grafana.ini: | +{{- range $key, $value := index .Values "grafana.ini" }} + [{{ $key }}] + {{- range $elem, $elemVal := $value }} + {{- if kindIs "invalid" $elemVal }} + {{ $elem }} = + {{- else if kindIs "string" $elemVal }} + {{ $elem }} = {{ tpl $elemVal $ }} + {{- else }} + {{ $elem }} = {{ $elemVal }} + {{- end }} + {{- end }} +{{- end }} + +{{- if .Values.datasources }} +{{ $root := . }} + {{- range $key, $value := .Values.datasources }} + {{ $key }}: | +{{ tpl (toYaml $value | indent 4) $root }} + {{- end -}} +{{- end -}} + +{{- if .Values.notifiers }} + {{- range $key, $value := .Values.notifiers }} + {{ $key }}: | +{{ toYaml $value | indent 4 }} + {{- end -}} +{{- end -}} + +{{- if .Values.dashboardProviders }} + {{- range $key, $value := .Values.dashboardProviders }} + {{ $key }}: | +{{ toYaml $value | indent 4 }} + {{- end -}} +{{- end -}} + +{{- if .Values.dashboards }} + download_dashboards.sh: | + #!/usr/bin/env sh + set -euf + {{- if .Values.dashboardProviders }} + {{- range $key, $value := .Values.dashboardProviders }} + {{- range $value.providers }} + mkdir -p {{ .options.path }} + {{- end }} + {{- end }} + {{- end }} + {{ $dashboardProviders := .Values.dashboardProviders }} + {{- range $provider, $dashboards := .Values.dashboards }} + {{- range $key, $value := $dashboards }} + {{- if (or (hasKey $value "gnetId") (hasKey $value "url")) }} + curl -skf \ + --connect-timeout 60 \ + --max-time 60 \ + {{- if not $value.b64content }} + -H "Accept: application/json" \ + {{- if $value.token }} + -H "Authorization: token {{ $value.token }}" \ + {{- end }} + -H "Content-Type: application/json;charset=UTF-8" \ + {{ end }} + {{- $dpPath := "" -}} + {{- range $kd := (index $dashboardProviders "dashboardproviders.yaml").providers -}} + {{- if eq $kd.name $provider -}} + {{- $dpPath = $kd.options.path -}} + {{- end -}} + {{- end -}} + {{- if $value.url -}}"{{ $value.url }}"{{- else -}}"https://grafana.com/api/dashboards/{{ $value.gnetId }}/revisions/{{- if $value.revision -}}{{ $value.revision }}{{- else -}}1{{- end -}}/download"{{- end -}}{{ if $value.datasource }} | sed '/-- .* --/! s/"datasource":.*,/"datasource": "{{ $value.datasource }}",/g'{{ end }}{{- if $value.b64content -}} | base64 -d {{- end -}} \ + > "{{- if $dpPath -}}{{ $dpPath }}{{- else -}}/var/lib/grafana/dashboards/{{ $provider }}{{- end -}}/{{ $key }}.json" + {{- end }} + {{- end -}} + {{- end }} +{{- end }} diff --git a/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/visualization/charts/grafana/templates/dashboards-json-configmap.yaml b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/visualization/charts/grafana/templates/dashboards-json-configmap.yaml new file mode 100644 index 000000000..24212b736 --- /dev/null +++ b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/visualization/charts/grafana/templates/dashboards-json-configmap.yaml @@ -0,0 +1,36 @@ +{{- if .Values.dashboards }} +{{ $files := .Files }} +{{- range $provider, $dashboards := .Values.dashboards }} +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ template "grafana.fullname" $ }}-dashboards-{{ $provider }} + namespace: {{ template "grafana.namespace" $ }} + labels: + {{- include "grafana.labels" $ | nindent 4 }} + dashboard-provider: {{ $provider }} + grafana_dashboard: tvm +{{- if $dashboards }} +data: +{{- $dashboardFound := false }} +{{- range $key, $value := $dashboards }} +{{- if (or (hasKey $value "json") (hasKey $value "file")) }} +{{- $dashboardFound = true }} +{{ print $key | indent 2 }}.json: +{{- if hasKey $value "json" }} + |- +{{ $value.json | indent 6 }} +{{- end }} +{{- if hasKey $value "file" }} +{{ toYaml ( $files.Get $value.file ) | indent 4}} +{{- end }} +{{- end }} +{{- end }} +{{- if not $dashboardFound }} + {} +{{- end }} +{{- end }} +--- +{{- end }} + +{{- end }} diff --git a/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/visualization/charts/grafana/templates/deployment.yaml b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/visualization/charts/grafana/templates/deployment.yaml new file mode 100644 index 000000000..8dbe5e107 --- /dev/null +++ b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/visualization/charts/grafana/templates/deployment.yaml @@ -0,0 +1,50 @@ +{{ if (or (not .Values.persistence.enabled) (eq .Values.persistence.type "pvc")) }} +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ template "grafana.fullname" . }} + namespace: {{ template "grafana.namespace" . }} + labels: + {{- include "grafana.labels" . | nindent 4 }} +{{- if .Values.labels }} +{{ toYaml .Values.labels | indent 4 }} +{{- end }} +{{- with .Values.annotations }} + annotations: +{{ toYaml . | indent 4 }} +{{- end }} +spec: + {{- if and (not .Values.autoscaling.enabled) (.Values.replicas) }} + replicas: {{ .Values.replicas }} + {{- end }} + revisionHistoryLimit: {{ .Values.revisionHistoryLimit }} + selector: + matchLabels: + {{- include "grafana.selectorLabels" . | nindent 6 }} +{{- with .Values.deploymentStrategy }} + strategy: +{{ toYaml . | trim | indent 4 }} +{{- end }} + template: + metadata: + labels: + {{- include "grafana.selectorLabels" . | nindent 8 }} +{{- with .Values.podLabels }} +{{ toYaml . | indent 8 }} +{{- end }} + annotations: + checksum/config: {{ include (print $.Template.BasePath "/configmap.yaml") . | sha256sum }} + checksum/dashboards-json-config: {{ include (print $.Template.BasePath "/dashboards-json-configmap.yaml") . | sha256sum }} + checksum/sc-dashboard-provider-config: {{ include (print $.Template.BasePath "/configmap-dashboard-provider.yaml") . | sha256sum }} +{{- if and (or (and (not .Values.admin.existingSecret) (not .Values.env.GF_SECURITY_ADMIN_PASSWORD__FILE) (not .Values.env.GF_SECURITY_ADMIN_PASSWORD)) (and .Values.ldap.enabled (not .Values.ldap.existingSecret))) (not .Values.env.GF_SECURITY_DISABLE_INITIAL_ADMIN_CREATION) }} + checksum/secret: {{ include (print $.Template.BasePath "/secret.yaml") . | sha256sum }} +{{- end }} +{{- if .Values.envRenderSecret }} + checksum/secret-env: {{ include (print $.Template.BasePath "/secret-env.yaml") . | sha256sum }} +{{- end }} +{{- with .Values.podAnnotations }} +{{ toYaml . | indent 8 }} +{{- end }} + spec: + {{- include "grafana.pod" . | nindent 6 }} +{{- end }} diff --git a/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/visualization/charts/grafana/templates/extra-manifests.yaml b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/visualization/charts/grafana/templates/extra-manifests.yaml new file mode 100644 index 000000000..a9bb3b6ba --- /dev/null +++ b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/visualization/charts/grafana/templates/extra-manifests.yaml @@ -0,0 +1,4 @@ +{{ range .Values.extraObjects }} +--- +{{ tpl (toYaml .) $ }} +{{ end }} diff --git a/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/visualization/charts/grafana/templates/headless-service.yaml b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/visualization/charts/grafana/templates/headless-service.yaml new file mode 100644 index 000000000..1df42e967 --- /dev/null +++ b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/visualization/charts/grafana/templates/headless-service.yaml @@ -0,0 +1,22 @@ +{{- if or .Values.headlessService (and .Values.persistence.enabled (not .Values.persistence.existingClaim) (eq .Values.persistence.type "statefulset"))}} +apiVersion: v1 +kind: Service +metadata: + name: {{ template "grafana.fullname" . }}-headless + namespace: {{ template "grafana.namespace" . }} + labels: + {{- include "grafana.labels" . | nindent 4 }} +{{- with .Values.annotations }} + annotations: +{{ toYaml . | indent 4 }} +{{- end }} +spec: + clusterIP: None + selector: + {{- include "grafana.selectorLabels" . | nindent 4 }} + type: ClusterIP + ports: + - protocol: TCP + port: 3000 + targetPort: 3000 +{{- end }} diff --git a/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/visualization/charts/grafana/templates/hpa.yaml b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/visualization/charts/grafana/templates/hpa.yaml new file mode 100644 index 000000000..9c186d74a --- /dev/null +++ b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/visualization/charts/grafana/templates/hpa.yaml @@ -0,0 +1,20 @@ +{{- if .Values.autoscaling.enabled }} +apiVersion: autoscaling/v2beta1 +kind: HorizontalPodAutoscaler +metadata: + name: {{ template "grafana.fullname" . }} + labels: + app.kubernetes.io/name: {{ template "grafana.name" . }} + helm.sh/chart: {{ template "grafana.chart" . }} + app.kubernetes.io/managed-by: {{ .Release.Service }} + app.kubernetes.io/instance: {{ .Release.Name }} +spec: + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: {{ template "grafana.fullname" . }} + minReplicas: {{ .Values.autoscaling.minReplicas }} + maxReplicas: {{ .Values.autoscaling.maxReplicas }} + metrics: +{{ toYaml .Values.autoscaling.metrics | indent 4 }} +{{- end }} diff --git a/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/visualization/charts/grafana/templates/image-renderer-deployment.yaml b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/visualization/charts/grafana/templates/image-renderer-deployment.yaml new file mode 100644 index 000000000..acf262e9d --- /dev/null +++ b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/visualization/charts/grafana/templates/image-renderer-deployment.yaml @@ -0,0 +1,121 @@ +{{ if .Values.imageRenderer.enabled }} +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ template "grafana.fullname" . }}-image-renderer + namespace: {{ template "grafana.namespace" . }} + labels: + {{- include "grafana.imageRenderer.labels" . | nindent 4 }} +{{- if .Values.imageRenderer.labels }} +{{ toYaml .Values.imageRenderer.labels | indent 4 }} +{{- end }} +{{- with .Values.imageRenderer.annotations }} + annotations: +{{ toYaml . | indent 4 }} +{{- end }} +spec: + replicas: {{ .Values.imageRenderer.replicas }} + revisionHistoryLimit: {{ .Values.imageRenderer.revisionHistoryLimit }} + selector: + matchLabels: + {{- include "grafana.imageRenderer.selectorLabels" . | nindent 6 }} +{{- with .Values.imageRenderer.deploymentStrategy }} + strategy: +{{ toYaml . | trim | indent 4 }} +{{- end }} + template: + metadata: + labels: + {{- include "grafana.imageRenderer.selectorLabels" . | nindent 8 }} +{{- with .Values.imageRenderer.podLabels }} +{{ toYaml . | indent 8 }} +{{- end }} + annotations: + checksum/config: {{ include (print $.Template.BasePath "/configmap.yaml") . | sha256sum }} +{{- with .Values.imageRenderer.podAnnotations }} +{{ toYaml . | indent 8 }} +{{- end }} + spec: + + {{- if .Values.imageRenderer.schedulerName }} + schedulerName: "{{ .Values.imageRenderer.schedulerName }}" + {{- end }} + {{- if .Values.imageRenderer.serviceAccountName }} + serviceAccountName: "{{ .Values.imageRenderer.serviceAccountName }}" + {{- end }} + {{- if .Values.imageRenderer.securityContext }} + securityContext: + {{- toYaml .Values.imageRenderer.securityContext | nindent 8 }} + {{- end }} + {{- if .Values.imageRenderer.hostAliases }} + hostAliases: + {{- toYaml .Values.imageRenderer.hostAliases | nindent 8 }} + {{- end }} + {{- if .Values.imageRenderer.priorityClassName }} + priorityClassName: {{ .Values.imageRenderer.priorityClassName }} + {{- end }} + {{- if .Values.imageRenderer.image.pullSecrets }} + imagePullSecrets: + {{- $root := . }} + {{- range .Values.imageRenderer.image.pullSecrets }} + - name: {{ tpl . $root }} + {{- end}} + {{- end }} + containers: + - name: {{ .Chart.Name }}-image-renderer + {{- if .Values.imageRenderer.image.sha }} + image: "{{ .Values.imageRenderer.image.registry }}/{{ .Values.imageRenderer.image.repository }}:{{ .Values.imageRenderer.image.tag }}@sha256:{{ .Values.imageRenderer.image.sha }}" + {{- else }} + image: "{{ .Values.imageRenderer.image.registry }}/{{ .Values.imageRenderer.image.repository }}:{{ .Values.imageRenderer.image.tag }}" + {{- end }} + imagePullPolicy: {{ .Values.imageRenderer.image.pullPolicy }} + {{- if .Values.imageRenderer.command }} + command: + {{- range .Values.imageRenderer.command }} + - {{ . }} + {{- end }} + {{- end}} + ports: + - name: {{ .Values.imageRenderer.service.portName }} + containerPort: {{ .Values.imageRenderer.service.port }} + protocol: TCP + livenessProbe: + httpGet: + path: / + port: {{ .Values.imageRenderer.service.portName }} + env: + - name: HTTP_PORT + value: {{ .Values.imageRenderer.service.port | quote }} + {{- range $key, $value := .Values.imageRenderer.env }} + - name: {{ $key | quote }} + value: {{ $value | quote }} + {{- end }} + securityContext: + capabilities: + drop: ['all'] + allowPrivilegeEscalation: false + readOnlyRootFilesystem: true + volumeMounts: + - mountPath: /tmp + name: image-renderer-tmpfs + {{- with .Values.imageRenderer.resources }} + resources: +{{ toYaml . | indent 12 }} + {{- end }} + {{- with .Values.imageRenderer.nodeSelector }} + nodeSelector: +{{ toYaml . | indent 8 }} + {{- end }} + {{- $root := . }} + {{- with .Values.imageRenderer.affinity }} + affinity: +{{ tpl (toYaml .) $root | indent 8 }} + {{- end }} + {{- with .Values.imageRenderer.tolerations }} + tolerations: +{{ toYaml . | indent 8 }} + {{- end }} + volumes: + - name: image-renderer-tmpfs + emptyDir: {} +{{- end }} diff --git a/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/visualization/charts/grafana/templates/image-renderer-network-policy.yaml b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/visualization/charts/grafana/templates/image-renderer-network-policy.yaml new file mode 100644 index 000000000..f8ca73aab --- /dev/null +++ b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/visualization/charts/grafana/templates/image-renderer-network-policy.yaml @@ -0,0 +1,76 @@ +{{- if and (.Values.imageRenderer.enabled) (.Values.imageRenderer.networkPolicy.limitIngress) }} +--- +apiVersion: networking.k8s.io/v1 +kind: NetworkPolicy +metadata: + name: {{ template "grafana.fullname" . }}-image-renderer-ingress + namespace: {{ template "grafana.namespace" . }} + annotations: + comment: Limit image-renderer ingress traffic from grafana +spec: + podSelector: + matchLabels: + {{- include "grafana.imageRenderer.selectorLabels" . | nindent 6 }} + {{- if .Values.imageRenderer.podLabels }} + {{ toYaml .Values.imageRenderer.podLabels | nindent 6 }} + {{- end }} + + policyTypes: + - Ingress + ingress: + - ports: + - port: {{ .Values.imageRenderer.service.port }} + protocol: TCP + from: + - namespaceSelector: + matchLabels: + name: {{ template "grafana.namespace" . }} + podSelector: + matchLabels: + {{- include "grafana.selectorLabels" . | nindent 14 }} + {{- if .Values.podLabels }} + {{ toYaml .Values.podLabels | nindent 14 }} + {{- end }} +{{ end }} + +{{- if and (.Values.imageRenderer.enabled) (.Values.imageRenderer.networkPolicy.limitEgress) }} +--- +apiVersion: networking.k8s.io/v1 +kind: NetworkPolicy +metadata: + name: {{ template "grafana.fullname" . }}-image-renderer-egress + namespace: {{ template "grafana.namespace" . }} + annotations: + comment: Limit image-renderer egress traffic to grafana +spec: + podSelector: + matchLabels: + {{- include "grafana.imageRenderer.selectorLabels" . | nindent 6 }} + {{- if .Values.imageRenderer.podLabels }} + {{ toYaml .Values.imageRenderer.podLabels | nindent 6 }} + {{- end }} + + policyTypes: + - Egress + egress: + # allow dns resolution + - ports: + - port: 53 + protocol: UDP + - port: 53 + protocol: TCP + # talk only to grafana + - ports: + - port: {{ .Values.service.port }} + protocol: TCP + to: + - namespaceSelector: + matchLabels: + name: {{ template "grafana.namespace" . }} + podSelector: + matchLabels: + {{- include "grafana.selectorLabels" . | nindent 14 }} + {{- if .Values.podLabels }} + {{ toYaml .Values.podLabels | nindent 14 }} + {{- end }} +{{ end }} diff --git a/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/visualization/charts/grafana/templates/image-renderer-service.yaml b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/visualization/charts/grafana/templates/image-renderer-service.yaml new file mode 100644 index 000000000..f29586c3a --- /dev/null +++ b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/visualization/charts/grafana/templates/image-renderer-service.yaml @@ -0,0 +1,30 @@ +{{ if .Values.imageRenderer.enabled }} +{{ if .Values.imageRenderer.service.enabled }} +apiVersion: v1 +kind: Service +metadata: + name: {{ template "grafana.fullname" . }}-image-renderer + namespace: {{ template "grafana.namespace" . }} + labels: + {{- include "grafana.imageRenderer.labels" . | nindent 4 }} +{{- if .Values.imageRenderer.service.labels }} +{{ toYaml .Values.imageRenderer.service.labels | indent 4 }} +{{- end }} +{{- with .Values.imageRenderer.service.annotations }} + annotations: +{{ toYaml . | indent 4 }} +{{- end }} +spec: + type: ClusterIP + {{- if .Values.imageRenderer.service.clusterIP }} + clusterIP: {{ .Values.imageRenderer.service.clusterIP }} + {{end}} + ports: + - name: {{ .Values.imageRenderer.service.portName }} + port: {{ .Values.imageRenderer.service.port }} + protocol: TCP + targetPort: {{ .Values.imageRenderer.service.targetPort }} + selector: + {{- include "grafana.imageRenderer.selectorLabels" . | nindent 4 }} +{{ end }} +{{ end }} diff --git a/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/visualization/charts/grafana/templates/ingress.yaml b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/visualization/charts/grafana/templates/ingress.yaml new file mode 100644 index 000000000..7699cecaa --- /dev/null +++ b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/visualization/charts/grafana/templates/ingress.yaml @@ -0,0 +1,78 @@ +{{- if .Values.ingress.enabled -}} +{{- $ingressApiIsStable := eq (include "grafana.ingress.isStable" .) "true" -}} +{{- $ingressSupportsIngressClassName := eq (include "grafana.ingress.supportsIngressClassName" .) "true" -}} +{{- $ingressSupportsPathType := eq (include "grafana.ingress.supportsPathType" .) "true" -}} +{{- $fullName := include "grafana.fullname" . -}} +{{- $servicePort := .Values.service.port -}} +{{- $ingressPath := .Values.ingress.path -}} +{{- $ingressPathType := .Values.ingress.pathType -}} +{{- $extraPaths := .Values.ingress.extraPaths -}} +apiVersion: {{ include "grafana.ingress.apiVersion" . }} +kind: Ingress +metadata: + name: {{ $fullName }} + namespace: {{ template "grafana.namespace" . }} + labels: + {{- include "grafana.labels" . | nindent 4 }} +{{- if .Values.ingress.labels }} +{{ toYaml .Values.ingress.labels | indent 4 }} +{{- end }} + {{- if .Values.ingress.annotations }} + annotations: + {{- range $key, $value := .Values.ingress.annotations }} + {{ $key }}: {{ tpl $value $ | quote }} + {{- end }} + {{- end }} +spec: + {{- if and $ingressSupportsIngressClassName .Values.ingress.ingressClassName }} + ingressClassName: {{ .Values.ingress.ingressClassName }} + {{- end -}} +{{- if .Values.ingress.tls }} + tls: +{{ tpl (toYaml .Values.ingress.tls) $ | indent 4 }} +{{- end }} + rules: + {{- if .Values.ingress.hosts }} + {{- range .Values.ingress.hosts }} + - host: {{ tpl . $}} + http: + paths: +{{- if $extraPaths }} +{{ toYaml $extraPaths | indent 10 }} +{{- end }} + - path: {{ $ingressPath }} + {{- if $ingressSupportsPathType }} + pathType: {{ $ingressPathType }} + {{- end }} + backend: + {{- if $ingressApiIsStable }} + service: + name: {{ $fullName }} + port: + number: {{ $servicePort }} + {{- else }} + serviceName: {{ $fullName }} + servicePort: {{ $servicePort }} + {{- end }} + {{- end }} + {{- else }} + - http: + paths: + - backend: + {{- if $ingressApiIsStable }} + service: + name: {{ $fullName }} + port: + number: {{ $servicePort }} + {{- else }} + serviceName: {{ $fullName }} + servicePort: {{ $servicePort }} + {{- end }} + {{- if $ingressPath }} + path: {{ $ingressPath }} + {{- end }} + {{- if $ingressSupportsPathType }} + pathType: {{ $ingressPathType }} + {{- end }} + {{- end -}} +{{- end }} diff --git a/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/visualization/charts/grafana/templates/networkpolicy.yaml b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/visualization/charts/grafana/templates/networkpolicy.yaml new file mode 100644 index 000000000..fc243828e --- /dev/null +++ b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/visualization/charts/grafana/templates/networkpolicy.yaml @@ -0,0 +1,37 @@ +{{- if .Values.networkPolicy.enabled }} +apiVersion: networking.k8s.io/v1 +kind: NetworkPolicy +metadata: + name: {{ template "grafana.fullname" . }} + namespace: {{ template "grafana.namespace" . }} + labels: + {{- include "grafana.labels" . | nindent 4 }} +{{- if .Values.labels }} +{{ toYaml .Values.labels | indent 4 }} +{{- end }} +{{- with .Values.annotations }} + annotations: +{{ toYaml . | indent 4 }} +{{- end }} +spec: + podSelector: + matchLabels: + {{- include "grafana.selectorLabels" . | nindent 6 }} + ingress: + - ports: + - port: {{ .Values.service.targetPort }} + {{- if not .Values.networkPolicy.allowExternal }} + from: + - podSelector: + matchLabels: + {{ template "grafana.fullname" . }}-client: "true" + {{- if .Values.networkPolicy.explicitNamespacesSelector }} + namespaceSelector: + {{ toYaml .Values.networkPolicy.explicitNamespacesSelector | indent 12 }} + {{- end }} + - podSelector: + matchLabels: + {{- include "grafana.labels" . | nindent 14 }} + role: read + {{- end }} +{{- end }} diff --git a/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/visualization/charts/grafana/templates/poddisruptionbudget.yaml b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/visualization/charts/grafana/templates/poddisruptionbudget.yaml new file mode 100644 index 000000000..61813a436 --- /dev/null +++ b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/visualization/charts/grafana/templates/poddisruptionbudget.yaml @@ -0,0 +1,22 @@ +{{- if .Values.podDisruptionBudget }} +apiVersion: policy/v1beta1 +kind: PodDisruptionBudget +metadata: + name: {{ template "grafana.fullname" . }} + namespace: {{ template "grafana.namespace" . }} + labels: + {{- include "grafana.labels" . | nindent 4 }} +{{- if .Values.labels }} +{{ toYaml .Values.labels | indent 4 }} +{{- end }} +spec: +{{- if .Values.podDisruptionBudget.minAvailable }} + minAvailable: {{ .Values.podDisruptionBudget.minAvailable }} +{{- end }} +{{- if .Values.podDisruptionBudget.maxUnavailable }} + maxUnavailable: {{ .Values.podDisruptionBudget.maxUnavailable }} +{{- end }} + selector: + matchLabels: + {{- include "grafana.selectorLabels" . | nindent 6 }} +{{- end }} diff --git a/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/visualization/charts/grafana/templates/podsecuritypolicy.yaml b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/visualization/charts/grafana/templates/podsecuritypolicy.yaml new file mode 100644 index 000000000..7de6c021d --- /dev/null +++ b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/visualization/charts/grafana/templates/podsecuritypolicy.yaml @@ -0,0 +1,49 @@ +{{- if .Values.rbac.pspEnabled }} +apiVersion: policy/v1beta1 +kind: PodSecurityPolicy +metadata: + name: {{ template "grafana.fullname" . }} + labels: + {{- include "grafana.labels" . | nindent 4 }} + annotations: + seccomp.security.alpha.kubernetes.io/allowedProfileNames: 'docker/default,runtime/default' + seccomp.security.alpha.kubernetes.io/defaultProfileName: 'docker/default' + {{- if .Values.rbac.pspUseAppArmor }} + apparmor.security.beta.kubernetes.io/allowedProfileNames: 'runtime/default' + apparmor.security.beta.kubernetes.io/defaultProfileName: 'runtime/default' + {{- end }} +spec: + privileged: false + allowPrivilegeEscalation: false + requiredDropCapabilities: + # Default set from Docker, with DAC_OVERRIDE and CHOWN + - ALL + volumes: + - 'configMap' + - 'emptyDir' + - 'projected' + - 'csi' + - 'secret' + - 'downwardAPI' + - 'persistentVolumeClaim' + hostNetwork: false + hostIPC: false + hostPID: 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 }} diff --git a/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/visualization/charts/grafana/templates/pvc.yaml b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/visualization/charts/grafana/templates/pvc.yaml new file mode 100644 index 000000000..8d93f5c23 --- /dev/null +++ b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/visualization/charts/grafana/templates/pvc.yaml @@ -0,0 +1,33 @@ +{{- if and .Values.persistence.enabled (not .Values.persistence.existingClaim) (eq .Values.persistence.type "pvc")}} +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: {{ template "grafana.fullname" . }} + namespace: {{ template "grafana.namespace" . }} + labels: + {{- include "grafana.labels" . | nindent 4 }} + {{- with .Values.persistence.annotations }} + annotations: +{{ toYaml . | indent 4 }} + {{- end }} + {{- with .Values.persistence.finalizers }} + finalizers: +{{ toYaml . | indent 4 }} + {{- end }} +spec: + accessModes: + {{- range .Values.persistence.accessModes }} + - {{ . | quote }} + {{- end }} + resources: + requests: + storage: {{ .Values.persistence.size | quote }} + {{- if .Values.persistence.storageClassName }} + storageClassName: {{ .Values.persistence.storageClassName }} + {{- end -}} + {{- with .Values.persistence.selectorLabels }} + selector: + matchLabels: +{{ toYaml . | indent 6 }} + {{- end }} +{{- end -}} diff --git a/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/visualization/charts/grafana/templates/role.yaml b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/visualization/charts/grafana/templates/role.yaml new file mode 100644 index 000000000..6a1890fb9 --- /dev/null +++ b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/visualization/charts/grafana/templates/role.yaml @@ -0,0 +1,32 @@ +{{- if and .Values.rbac.create (not .Values.rbac.useExistingRole) -}} +apiVersion: {{ template "grafana.rbac.apiVersion" . }} +kind: Role +metadata: + name: {{ template "grafana.fullname" . }} + namespace: {{ template "grafana.namespace" . }} + labels: + {{- include "grafana.labels" . | nindent 4 }} +{{- with .Values.annotations }} + annotations: +{{ toYaml . | indent 4 }} +{{- end }} +{{- if or .Values.rbac.pspEnabled (and .Values.rbac.namespaced (or .Values.sidecar.dashboards.enabled (or .Values.sidecar.datasources.enabled .Values.rbac.extraRoleRules))) }} +rules: +{{- if .Values.rbac.pspEnabled }} +- apiGroups: ['extensions'] + resources: ['podsecuritypolicies'] + verbs: ['use'] + resourceNames: [{{ template "grafana.fullname" . }}] +{{- end }} +{{- if and .Values.rbac.namespaced (or .Values.sidecar.dashboards.enabled .Values.sidecar.datasources.enabled) }} +- apiGroups: [""] # "" indicates the core API group + resources: ["configmaps", "secrets"] + verbs: ["get", "watch", "list"] +{{- end }} +{{- with .Values.rbac.extraRoleRules }} +{{ toYaml . | indent 0 }} +{{- end}} +{{- else }} +rules: [] +{{- end }} +{{- end }} diff --git a/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/visualization/charts/grafana/templates/rolebinding.yaml b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/visualization/charts/grafana/templates/rolebinding.yaml new file mode 100644 index 000000000..e0107255e --- /dev/null +++ b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/visualization/charts/grafana/templates/rolebinding.yaml @@ -0,0 +1,25 @@ +{{- if .Values.rbac.create -}} +apiVersion: {{ template "grafana.rbac.apiVersion" . }} +kind: RoleBinding +metadata: + name: {{ template "grafana.fullname" . }} + namespace: {{ template "grafana.namespace" . }} + labels: + {{- include "grafana.labels" . | nindent 4 }} +{{- with .Values.annotations }} + annotations: +{{ toYaml . | indent 4 }} +{{- end }} +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role +{{- if (not .Values.rbac.useExistingRole) }} + name: {{ template "grafana.fullname" . }} +{{- else }} + name: {{ .Values.rbac.useExistingRole }} +{{- end }} +subjects: +- kind: ServiceAccount + name: {{ template "grafana.serviceAccountName" . }} + namespace: {{ template "grafana.namespace" . }} +{{- end -}} diff --git a/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/visualization/charts/grafana/templates/secret-env.yaml b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/visualization/charts/grafana/templates/secret-env.yaml new file mode 100644 index 000000000..5c09313e6 --- /dev/null +++ b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/visualization/charts/grafana/templates/secret-env.yaml @@ -0,0 +1,14 @@ +{{- if .Values.envRenderSecret }} +apiVersion: v1 +kind: Secret +metadata: + name: {{ template "grafana.fullname" . }}-env + namespace: {{ template "grafana.namespace" . }} + labels: + {{- include "grafana.labels" . | nindent 4 }} +type: Opaque +data: +{{- range $key, $val := .Values.envRenderSecret }} + {{ $key }}: {{ $val | b64enc | quote }} +{{- end -}} +{{- end }} diff --git a/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/visualization/charts/grafana/templates/secret.yaml b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/visualization/charts/grafana/templates/secret.yaml new file mode 100644 index 000000000..c8aa750ac --- /dev/null +++ b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/visualization/charts/grafana/templates/secret.yaml @@ -0,0 +1,26 @@ +{{- if or (and (not .Values.admin.existingSecret) (not .Values.env.GF_SECURITY_ADMIN_PASSWORD__FILE) (not .Values.env.GF_SECURITY_ADMIN_PASSWORD) (not .Values.env.GF_SECURITY_DISABLE_INITIAL_ADMIN_CREATION)) (and .Values.ldap.enabled (not .Values.ldap.existingSecret)) }} +apiVersion: v1 +kind: Secret +metadata: + name: {{ template "grafana.fullname" . }} + namespace: {{ template "grafana.namespace" . }} + labels: + {{- include "grafana.labels" . | nindent 4 }} +{{- with .Values.annotations }} + annotations: +{{ toYaml . | indent 4 }} +{{- end }} +type: Opaque +data: + {{- if and (not .Values.env.GF_SECURITY_DISABLE_INITIAL_ADMIN_CREATION) (not .Values.admin.existingSecret) (not .Values.env.GF_SECURITY_ADMIN_PASSWORD__FILE) (not .Values.env.GF_SECURITY_ADMIN_PASSWORD) }} + admin-user: {{ .Values.adminUser | b64enc | quote }} + {{- if .Values.adminPassword }} + admin-password: {{ .Values.adminPassword | b64enc | quote }} + {{- else }} + admin-password: {{ template "grafana.password" . }} + {{- end }} + {{- end }} + {{- if not .Values.ldap.existingSecret }} + ldap-toml: {{ tpl .Values.ldap.config $ | b64enc | quote }} + {{- end }} +{{- end }} diff --git a/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/visualization/charts/grafana/templates/service.yaml b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/visualization/charts/grafana/templates/service.yaml new file mode 100644 index 000000000..ba84ef970 --- /dev/null +++ b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/visualization/charts/grafana/templates/service.yaml @@ -0,0 +1,51 @@ +{{ if .Values.service.enabled }} +apiVersion: v1 +kind: Service +metadata: + name: {{ template "grafana.fullname" . }} + namespace: {{ template "grafana.namespace" . }} + labels: + {{- include "grafana.labels" . | nindent 4 }} +{{- if .Values.service.labels }} +{{ toYaml .Values.service.labels | indent 4 }} +{{- end }} +{{- with .Values.service.annotations }} + annotations: +{{ toYaml . | indent 4 }} +{{- end }} +spec: +{{- if (or (eq .Values.service.type "ClusterIP") (empty .Values.service.type)) }} + type: ClusterIP + {{- if .Values.service.clusterIP }} + clusterIP: {{ .Values.service.clusterIP }} + {{end}} +{{- else if eq .Values.service.type "LoadBalancer" }} + type: {{ .Values.service.type }} + {{- if .Values.service.loadBalancerIP }} + loadBalancerIP: {{ .Values.service.loadBalancerIP }} + {{- end }} + {{- if .Values.service.loadBalancerSourceRanges }} + loadBalancerSourceRanges: +{{ toYaml .Values.service.loadBalancerSourceRanges | indent 4 }} + {{- end -}} +{{- else }} + type: {{ .Values.service.type }} +{{- end }} +{{- if .Values.service.externalIPs }} + externalIPs: +{{ toYaml .Values.service.externalIPs | indent 4 }} +{{- end }} + ports: + - name: {{ .Values.service.portName }} + port: {{ .Values.service.port }} + protocol: TCP + targetPort: {{ .Values.service.targetPort }} +{{ if (and (eq .Values.service.type "NodePort") (not (empty .Values.service.nodePort))) }} + nodePort: {{.Values.service.nodePort}} +{{ end }} + {{- if .Values.extraExposePorts }} + {{- tpl (toYaml .Values.extraExposePorts) . | indent 4 }} + {{- end }} + selector: + {{- include "grafana.selectorLabels" . | nindent 4 }} +{{ end }} diff --git a/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/visualization/charts/grafana/templates/serviceaccount.yaml b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/visualization/charts/grafana/templates/serviceaccount.yaml new file mode 100644 index 000000000..4ccee15ed --- /dev/null +++ b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/visualization/charts/grafana/templates/serviceaccount.yaml @@ -0,0 +1,14 @@ +{{- if .Values.serviceAccount.create }} +apiVersion: v1 +kind: ServiceAccount +metadata: + labels: + {{- include "grafana.labels" . | nindent 4 }} +{{- $root := . }} +{{- with .Values.serviceAccount.annotations }} + annotations: +{{ tpl (toYaml . | indent 4) $root }} +{{- end }} + name: {{ template "grafana.serviceAccountName" . }} + namespace: {{ template "grafana.namespace" . }} +{{- end }} diff --git a/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/visualization/charts/grafana/templates/servicemonitor.yaml b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/visualization/charts/grafana/templates/servicemonitor.yaml new file mode 100644 index 000000000..a18c6d336 --- /dev/null +++ b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/visualization/charts/grafana/templates/servicemonitor.yaml @@ -0,0 +1,44 @@ +{{- if .Values.serviceMonitor.enabled }} +--- +apiVersion: monitoring.coreos.com/v1 +kind: ServiceMonitor +metadata: + name: {{ template "grafana.fullname" . }} + {{- if .Values.serviceMonitor.namespace }} + namespace: {{ .Values.serviceMonitor.namespace }} + {{- else }} + namespace: {{ template "grafana.namespace" . }} + {{- end }} + labels: + {{- include "grafana.labels" . | nindent 4 }} + {{- if .Values.serviceMonitor.labels }} + {{- toYaml .Values.serviceMonitor.labels | nindent 4 }} + {{- end }} +spec: + endpoints: + - port: {{ .Values.service.portName }} + {{- with .Values.serviceMonitor.interval }} + interval: {{ . }} + {{- end }} + {{- with .Values.serviceMonitor.scrapeTimeout }} + scrapeTimeout: {{ . }} + {{- end }} + honorLabels: true + path: {{ .Values.serviceMonitor.path }} + scheme: {{ .Values.serviceMonitor.scheme }} + {{- if .Values.serviceMonitor.tlsConfig }} + tlsConfig: + {{- toYaml .Values.serviceMonitor.tlsConfig | nindent 6 }} + {{- end }} + {{- if .Values.serviceMonitor.relabelings }} + relabelings: + {{- toYaml .Values.serviceMonitor.relabelings | nindent 4 }} + {{- end }} + jobLabel: "{{ .Release.Name }}" + selector: + matchLabels: + {{- include "grafana.selectorLabels" . | nindent 8 }} + namespaceSelector: + matchNames: + - {{ .Release.Namespace }} +{{- end }} diff --git a/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/visualization/charts/grafana/templates/statefulset.yaml b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/visualization/charts/grafana/templates/statefulset.yaml new file mode 100644 index 000000000..ad3dd0696 --- /dev/null +++ b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/visualization/charts/grafana/templates/statefulset.yaml @@ -0,0 +1,52 @@ +{{- if and .Values.persistence.enabled (not .Values.persistence.existingClaim) (eq .Values.persistence.type "statefulset")}} +apiVersion: apps/v1 +kind: StatefulSet +metadata: + name: {{ template "grafana.fullname" . }} + namespace: {{ template "grafana.namespace" . }} + labels: + {{- include "grafana.labels" . | nindent 4 }} +{{- with .Values.annotations }} + annotations: +{{ toYaml . | indent 4 }} +{{- end }} +spec: + replicas: {{ .Values.replicas }} + selector: + matchLabels: + {{- include "grafana.selectorLabels" . | nindent 6 }} + serviceName: {{ template "grafana.fullname" . }}-headless + template: + metadata: + labels: + {{- include "grafana.selectorLabels" . | nindent 8 }} +{{- with .Values.podLabels }} +{{ toYaml . | indent 8 }} +{{- end }} + annotations: + checksum/config: {{ include (print $.Template.BasePath "/configmap.yaml") . | sha256sum }} + checksum/dashboards-json-config: {{ include (print $.Template.BasePath "/dashboards-json-configmap.yaml") . | sha256sum }} + checksum/sc-dashboard-provider-config: {{ include (print $.Template.BasePath "/configmap-dashboard-provider.yaml") . | sha256sum }} + {{- if and (or (and (not .Values.admin.existingSecret) (not .Values.env.GF_SECURITY_ADMIN_PASSWORD__FILE) (not .Values.env.GF_SECURITY_ADMIN_PASSWORD)) (and .Values.ldap.enabled (not .Values.ldap.existingSecret))) (not .Values.env.GF_SECURITY_DISABLE_INITIAL_ADMIN_CREATION) }} + checksum/secret: {{ include (print $.Template.BasePath "/secret.yaml") . | sha256sum }} +{{- end }} +{{- with .Values.podAnnotations }} +{{ toYaml . | indent 8 }} +{{- end }} + spec: + {{- include "grafana.pod" . | nindent 6 }} + volumeClaimTemplates: + - metadata: + name: storage + spec: + accessModes: {{ .Values.persistence.accessModes }} + storageClassName: {{ .Values.persistence.storageClassName }} + resources: + requests: + storage: {{ .Values.persistence.size }} + {{- with .Values.persistence.selectorLabels }} + selector: + matchLabels: +{{ toYaml . | indent 10 }} + {{- end }} +{{- end }} diff --git a/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/visualization/charts/grafana/templates/user-secret.yaml b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/visualization/charts/grafana/templates/user-secret.yaml new file mode 100644 index 000000000..3e9703fff --- /dev/null +++ b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/visualization/charts/grafana/templates/user-secret.yaml @@ -0,0 +1,9 @@ +apiVersion: v1 +kind: Secret +metadata: + name: k8s-triliovault-operator-grafana + namespace: {{ template "grafana.namespace" . }} +type: Opaque +data: + admin-user: YWRtaW4= + admin-password: YWRtaW4xMjM= diff --git a/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/visualization/charts/grafana/values.yaml b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/visualization/charts/grafana/values.yaml new file mode 100644 index 000000000..5d50d4443 --- /dev/null +++ b/charts/trilio/k8s-triliovault-operator/4.0.5/charts/observability/charts/visualization/charts/grafana/values.yaml @@ -0,0 +1,938 @@ +rbac: + create: true + ## Use an existing ClusterRole/Role (depending on rbac.namespaced false/true) + # useExistingRole: name-of-some-(cluster)role + pspEnabled: false + pspUseAppArmor: false + namespaced: false + extraRoleRules: [] + # - apiGroups: [] + # resources: [] + # verbs: [] + extraClusterRoleRules: [] + # - apiGroups: [] + # resources: [] + # verbs: [] +serviceAccount: + create: true + name: + nameTest: +## Service account annotations. Can be templated. +# annotations: +# eks.amazonaws.com/role-arn: arn:aws:iam::123456789000:role/iam-role-name-here + autoMount: true + +replicas: 1 + +## Create a headless service for the deployment +headlessService: false + +## Create HorizontalPodAutoscaler object for deployment type +# +autoscaling: + enabled: false +# minReplicas: 1 +# maxReplicas: 10 +# metrics: +# - type: Resource +# resource: +# name: cpu +# targetAverageUtilization: 60 +# - type: Resource +# resource: +# name: memory +# targetAverageUtilization: 60 + +## See `kubectl explain poddisruptionbudget.spec` for more +## ref: https://kubernetes.io/docs/tasks/run-application/configure-pdb/ +podDisruptionBudget: {} +# minAvailable: 1 +# maxUnavailable: 1 + +## See `kubectl explain deployment.spec.strategy` for more +## ref: https://kubernetes.io/docs/concepts/workloads/controllers/deployment/#strategy +deploymentStrategy: + type: RollingUpdate + +readinessProbe: + httpGet: + path: /api/health + port: 3000 + +livenessProbe: + httpGet: + path: /api/health + port: 3000 + initialDelaySeconds: 60 + timeoutSeconds: 30 + failureThreshold: 10 + +## Use an alternate scheduler, e.g. "stork". +## ref: https://kubernetes.io/docs/tasks/administer-cluster/configure-multiple-schedulers/ +## +# schedulerName: "default-scheduler" + +image: + registry: docker.io + repository: grafana/grafana + tag: 8.5.0 + sha: "" + 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/ + ## Can be templated. + ## + # pullSecrets: + # - myRegistrKeySecretName + +testFramework: + enabled: true + registry: docker.io + image: "bats/bats" + tag: "v1.4.1" + imagePullPolicy: IfNotPresent + securityContext: {} + +securityContext: + runAsUser: 472 + runAsGroup: 472 + fsGroup: 472 + +containerSecurityContext: + {} + +# Extra configmaps to mount in grafana pods +# Values are templated. +extraConfigmapMounts: [] + # - name: certs-configmap + # mountPath: /etc/grafana/ssl/ + # subPath: certificates.crt # (optional) + # configMap: certs-configmap + # readOnly: true + + +extraEmptyDirMounts: [] + # - name: provisioning-notifiers + # mountPath: /etc/grafana/provisioning/notifiers + + +# Apply extra labels to common labels. +extraLabels: {} + +## Assign a PriorityClassName to pods if set +# priorityClassName: + +downloadDashboardsImage: + registry: docker.io + repository: curlimages/curl + tag: 7.73.0 + sha: "" + pullPolicy: IfNotPresent + +downloadDashboards: + env: {} + envFromSecret: "" + resources: {} + +## Pod Annotations +# podAnnotations: {} + +## Pod Labels +# podLabels: {} + +podPortName: grafana + +## Deployment annotations +annotations: + ignore-check.kube-linter.io/privileged-ports : "This deployment needs to run on privileged ports 80" + ignore-check.kube-linter.io/read-secret-from-env-var : "This deployment needs to read secret from env variable for grafana admin user and password" + +## Expose the grafana service to be accessed from outside the cluster (LoadBalancer service). +## or access it from within the cluster (ClusterIP service). Set the service type and the port to serve it. +## ref: http://kubernetes.io/docs/user-guide/services/ +## +service: + enabled: true + type: ClusterIP + port: 80 + targetPort: 3000 + # targetPort: 4181 To be used with a proxy extraContainer + annotations: {} + labels: {} + portName: service + +serviceMonitor: + ## If true, a ServiceMonitor CRD is created for a prometheus operator + ## https://github.com/coreos/prometheus-operator + ## + enabled: false + path: /metrics + # namespace: monitoring (defaults to use the namespace this chart is deployed to) + labels: {} + interval: 1m + scheme: http + tlsConfig: {} + scrapeTimeout: 30s + relabelings: [] + +extraExposePorts: [] + # - name: keycloak + # port: 8080 + # targetPort: 8080 + # type: ClusterIP + +# overrides pod.spec.hostAliases in the grafana deployment's pods +hostAliases: [] + # - ip: "1.2.3.4" + # hostnames: + # - "my.host.com" + +ingress: + 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 + # Values can be templated + annotations: {} + # kubernetes.io/ingress.class: nginx + # kubernetes.io/tls-acme: "true" + labels: {} + path: / + + # pathType is only for k8s >= 1.1= + pathType: Prefix + + hosts: + - chart-example.local + ## 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 + ## Or for k8s > 1.19 + # - path: /* + # pathType: Prefix + # backend: + # service: + # name: ssl-redirect + # port: + # name: use-annotation + + + tls: [] + # - secretName: chart-example-tls + # hosts: + # - chart-example.local + +resources: + limits: + cpu: 1000m + memory: 500Mi + requests: + cpu: 200m + memory: 256Mi + +## Node labels for pod assignment +## ref: https://kubernetes.io/docs/user-guide/node-selection/ +# +nodeSelector: {} + +## Tolerations for pod assignment +## ref: https://kubernetes.io/docs/concepts/configuration/taint-and-toleration/ +## +tolerations: [] + +## Affinity for pod assignment (evaluated as template) +## ref: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#affinity-and-anti-affinity +## +affinity: {} + +## Additional init containers (evaluated as template) +## ref: https://kubernetes.io/docs/concepts/workloads/pods/init-containers/ +## +extraInitContainers: [] + +## Enable an Specify container in extraContainers. This is meant to allow adding an authentication proxy to a grafana pod +extraContainers: "" +# extraContainers: | +# - name: proxy +# image: quay.io/gambol99/keycloak-proxy:latest +# args: +# - -provider=github +# - -client-id= +# - -client-secret= +# - -github-org= +# - -email-domain=* +# - -cookie-secret= +# - -http-address=http://0.0.0.0:4181 +# - -upstream-url=http://127.0.0.1:3000 +# ports: +# - name: proxy-web +# containerPort: 4181 + +## Volumes that can be used in init containers that will not be mounted to deployment pods +extraContainerVolumes: [] +# - name: volume-from-secret +# secret: +# secretName: secret-to-mount +# - name: empty-dir-volume +# emptyDir: {} + +## Enable persistence using Persistent Volume Claims +## ref: http://kubernetes.io/docs/user-guide/persistent-volumes/ +## +persistence: + type: pvc + enabled: false + # storageClassName: default + accessModes: + - ReadWriteOnce + size: 10Gi + # annotations: {} + finalizers: + - kubernetes.io/pvc-protection + # selectorLabels: {} + ## Sub-directory of the PV to mount. Can be templated. + # subPath: "" + ## Name of an existing PVC. Can be templated. + # existingClaim: + + ## If persistence is not enabled, this allows to mount the + ## local storage in-memory to improve performance + ## + inMemory: + enabled: false + ## 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 + ## + # sizeLimit: 300Mi + +initChownData: + ## If false, data ownership will not be reset at startup + ## This allows the prometheus-server to be run with an arbitrary user + ## + enabled: true + + ## initChownData container image + ## + image: + registry: docker.io + repository: busybox + tag: "1.31.1" + sha: "" + pullPolicy: IfNotPresent + + ## initChownData resource requests and limits + ## Ref: http://kubernetes.io/docs/user-guide/compute-resources/ + ## + resources: + limits: + cpu: 200m + memory: 2568Mi + requests: + cpu: 100m + memory: 128Mi + + +# Administrator credentials when not using an existing secret (see below) +adminUser: admin +# adminPassword + +# Use an existing secret for the admin user. +admin: + ## Name of the secret. Can be templated. + existingSecret: "" + userKey: admin-user + passwordKey: admin-password + +## Define command to be executed at startup by grafana container +## Needed if using `vault-env` to manage secrets (ref: https://banzaicloud.com/blog/inject-secrets-into-pods-vault/) +## Default is "run.sh" as defined in grafana's Dockerfile +# command: +# - "sh" +# - "/run.sh" + +## Use an alternate scheduler, e.g. "stork". +## ref: https://kubernetes.io/docs/tasks/administer-cluster/configure-multiple-schedulers/ +## +# schedulerName: + +## Extra environment variables that will be pass onto deployment pods +## +## to provide grafana with access to CloudWatch on AWS EKS: +## 1. create an iam role of type "Web identity" with provider oidc.eks.* (note the provider for later) +## 2. edit the "Trust relationships" of the role, add a line inside the StringEquals clause using the +## same oidc eks provider as noted before (same as the existing line) +## also, replace NAMESPACE and prometheus-operator-grafana with the service account namespace and name +## +## "oidc.eks.us-east-1.amazonaws.com/id/XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX:sub": "system:serviceaccount:NAMESPACE:prometheus-operator-grafana", +## +## 3. attach a policy to the role, you can use a built in policy called CloudWatchReadOnlyAccess +## 4. use the following env: (replace 123456789000 and iam-role-name-here with your aws account number and role name) +## +## env: +## AWS_ROLE_ARN: arn:aws:iam::123456789000:role/iam-role-name-here +## AWS_WEB_IDENTITY_TOKEN_FILE: /var/run/secrets/eks.amazonaws.com/serviceaccount/token +## AWS_REGION: us-east-1 +## +## 5. uncomment the EKS section in extraSecretMounts: below +## 6. uncomment the annotation section in the serviceAccount: above +## make sure to replace arn:aws:iam::123456789000:role/iam-role-name-here with your role arn + +env: {} + +## "valueFrom" environment variable references that will be added to deployment pods. Name is templated. +## ref: https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.19/#envvarsource-v1-core +## Renders in container spec as: +## env: +## ... +## - name: +## valueFrom: +## +envValueFrom: {} + # ENV_NAME: + # configMapKeyRef: + # name: configmap-name + # key: value_key + +## The name of a secret in the same kubernetes namespace which contain values to be added to the environment +## This can be useful for auth tokens, etc. Value is templated. +envFromSecret: "" + +## Sensible environment variables that will be rendered as new secret object +## This can be useful for auth tokens, etc +envRenderSecret: {} + +## The names of secrets in the same kubernetes namespace which contain values to be added to the environment +## Each entry should contain a name key, and can optionally specify whether the secret must be defined with an optional key. +## Name is templated. +envFromSecrets: [] +## - name: secret-name +## optional: true + +## The names of conifgmaps in the same kubernetes namespace which contain values to be added to the environment +## Each entry should contain a name key, and can optionally specify whether the configmap must be defined with an optional key. +## Name is templated. +## ref: https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.23/#configmapenvsource-v1-core +envFromConfigMaps: [] +## - name: configmap-name +## optional: true + +# Inject Kubernetes services as environment variables. +# See https://kubernetes.io/docs/concepts/services-networking/connect-applications-service/#environment-variables +enableServiceLinks: true + +## Additional grafana server secret mounts +# Defines additional mounts with secrets. Secrets must be manually created in the namespace. +extraSecretMounts: [] + # - name: secret-files + # mountPath: /etc/secrets + # secretName: grafana-secret-files + # readOnly: true + # subPath: "" + # + # for AWS EKS (cloudwatch) use the following (see also instruction in env: above) + # - name: aws-iam-token + # mountPath: /var/run/secrets/eks.amazonaws.com/serviceaccount + # readOnly: true + # projected: + # defaultMode: 420 + # sources: + # - serviceAccountToken: + # audience: sts.amazonaws.com + # expirationSeconds: 86400 + # path: token + # + # for CSI e.g. Azure Key Vault use the following + # - name: secrets-store-inline + # mountPath: /run/secrets + # readOnly: true + # csi: + # driver: secrets-store.csi.k8s.io + # readOnly: true + # volumeAttributes: + # secretProviderClass: "akv-grafana-spc" + # nodePublishSecretRef: # Only required when using service principal mode + # name: grafana-akv-creds # Only required when using service principal mode + +## Additional grafana server volume mounts +# Defines additional volume mounts. +extraVolumeMounts: [] + # - name: extra-volume-0 + # mountPath: /mnt/volume0 + # readOnly: true + # existingClaim: volume-claim + # - name: extra-volume-1 + # mountPath: /mnt/volume1 + # readOnly: true + # hostPath: /usr/shared/ + +## Container Lifecycle Hooks. Execute a specific bash command or make an HTTP request +lifecycleHooks: {} + # postStart: + # exec: + # command: [] + +## Pass the plugins you want installed as a list. +## +plugins: + - grafana-piechart-panel + # - digrich-bubblechart-panel + # - grafana-clock-panel + +## Configure grafana datasources +## ref: http://docs.grafana.org/administration/provisioning/#datasources +## +datasources: {} +# datasources.yaml: +# apiVersion: 1 +# datasources: +# - name: Prometheus +# type: prometheus +# url: http://prometheus-prometheus-server +# access: proxy +# isDefault: true +# - name: CloudWatch +# type: cloudwatch +# access: proxy +# uid: cloudwatch +# editable: false +# jsonData: +# authType: default +# defaultRegion: us-east-1 + +## Configure notifiers +## ref: http://docs.grafana.org/administration/provisioning/#alert-notification-channels +## +notifiers: {} +# notifiers.yaml: +# notifiers: +# - name: email-notifier +# type: email +# uid: email1 +# # either: +# org_id: 1 +# # or +# org_name: Main Org. +# is_default: true +# settings: +# addresses: an_email_address@example.com +# delete_notifiers: + +## Configure grafana dashboard providers +## ref: http://docs.grafana.org/administration/provisioning/#dashboards +## +## `path` must be /var/lib/grafana/dashboards/ +## +dashboardProviders: {} +# dashboardproviders.yaml: +# apiVersion: 1 +# providers: +# - name: 'default' +# orgId: 1 +# folder: '' +# type: file +# disableDeletion: false +# editable: true +# options: +# path: /var/lib/grafana/dashboards/default + +## Configure grafana dashboard to import +## NOTE: To use dashboards you must also enable/configure dashboardProviders +## ref: https://grafana.com/dashboards +## +## dashboards per provider, use provider name as key. +## +dashboards: + default: + logging-dashboard: + file: dashboards/logging-dashboard.json + backup-detail: + file: dashboards/backup-detail.json + clusterbackup-detail: + file: dashboards/clusterbackup-detail.json + backup-overview: + file: dashboards/backup-overview.json + backupplan-detail: + file: dashboards/backupplan-detail.json + clusterbackupplan-detail: + file: dashboards/clusterbackupplan-detail.json + backupplan-overview: + file: dashboards/backupplan-overview.json + metadata-detail: + file: dashboards/metadata-detail.json + overview: + file: dashboards/overview.json + restore-detail: + file: dashboards/restore-detail.json + clusterrestore-detail: + file: dashboards/clusterrestore-detail.json + restore-overview: + file: dashboards/restore-overview.json + target-detail: + file: dashboards/target-detail.json + continuousrestoreplan-detail: + file: dashboards/continuousrestoreplan-detail.json + consistentset-detail: + file: dashboards/consistentset-detail.json + # default: + # some-dashboard: + # json: | + # $RAW_JSON + # custom-dashboard: + # file: dashboards/custom-dashboard.json + # prometheus-stats: + # gnetId: 2 + # revision: 2 + # datasource: Prometheus + # local-dashboard: + # url: https://example.com/repository/test.json + # token: '' + # local-dashboard-base64: + # url: https://example.com/repository/test-b64.json + # token: '' + # b64content: true + +## Reference to external ConfigMap per provider. Use provider name as key and ConfigMap name as value. +## A provider dashboards must be defined either by external ConfigMaps or in values.yaml, not in both. +## ConfigMap data example: +## +## data: +## example-dashboard.json: | +## RAW_JSON +## +dashboardsConfigMaps: {} +# default: "" + +## Grafana's primary configuration +## NOTE: values in map will be converted to ini format +## ref: http://docs.grafana.org/installation/configuration/ +## +grafana.ini: + dashboards: + default_home_dashboard_path: /var/lib/grafana/dashboards/default/overview.json + paths: + data: /var/lib/grafana/ + logs: /var/log/grafana + plugins: /var/lib/grafana/plugins + provisioning: /etc/grafana/provisioning + analytics: + check_for_updates: false + log: + mode: console + server: + root_url: "%(protocol)s://%(domain)s:%(http_port)s/grafana/" + serve_from_sub_path: true +## grafana Authentication can be enabled with the following values on grafana.ini +# server: + # The full public facing url you use in browser, used for redirects and emails + # root_url: + # https://grafana.com/docs/grafana/latest/auth/github/#enable-github-in-grafana + # auth.github: + # enabled: false + # allow_sign_up: false + # scopes: user:email,read:org + # auth_url: https://github.com/login/oauth/authorize + # token_url: https://github.com/login/oauth/access_token + # api_url: https://api.github.com/user + # team_ids: + # allowed_organizations: + # client_id: + # client_secret: +## LDAP Authentication can be enabled with the following values on grafana.ini +## NOTE: Grafana will fail to start if the value for ldap.toml is invalid + # auth.ldap: + # enabled: true + # allow_sign_up: true + # config_file: /etc/grafana/ldap.toml + +## Grafana's LDAP configuration +## Templated by the template in _helpers.tpl +## NOTE: To enable the grafana.ini must be configured with auth.ldap.enabled +## ref: http://docs.grafana.org/installation/configuration/#auth-ldap +## ref: http://docs.grafana.org/installation/ldap/#configuration +ldap: + enabled: false + # `existingSecret` is a reference to an existing secret containing the ldap configuration + # for Grafana in a key `ldap-toml`. + existingSecret: "" + # `config` is the content of `ldap.toml` that will be stored in the created secret + config: "" + # config: |- + # verbose_logging = true + + # [[servers]] + # host = "my-ldap-server" + # port = 636 + # use_ssl = true + # start_tls = false + # ssl_skip_verify = false + # bind_dn = "uid=%s,ou=users,dc=myorg,dc=com" + +## Grafana's SMTP configuration +## NOTE: To enable, grafana.ini must be configured with smtp.enabled +## ref: http://docs.grafana.org/installation/configuration/#smtp +smtp: + # `existingSecret` is a reference to an existing secret containing the smtp configuration + # for Grafana. + existingSecret: "" + userKey: "user" + passwordKey: "password" + +## Sidecars that collect the configmaps with specified label and stores the included files them into the respective folders +## Requires at least Grafana 5 to work and can't be used together with parameters dashboardProviders, datasources and dashboards +sidecar: + image: + registry: quay.io + repository: kiwigrid/k8s-sidecar + tag: 1.15.6 + sha: "" + imagePullPolicy: IfNotPresent + resources: + limits: + cpu: 200m + memory: 200Mi + requests: + cpu: 100m + memory: 100Mi + securityContext: {} + # skipTlsVerify Set to true to skip tls verification for kube api calls + # skipTlsVerify: true + enableUniqueFilenames: false + readinessProbe: {} + livenessProbe: {} + dashboards: + enabled: true + SCProvider: true + # label that the configmaps with dashboards are marked with + label: grafana_dashboard + # value of label that the configmaps with dashboards are set to + labelValue: null + # folder in the pod that should hold the collected dashboards (unless `defaultFolderName` is set) + folder: /tmp/dashboards + # The default folder name, it will create a subfolder under the `folder` and put dashboards in there instead + defaultFolderName: null + # Namespaces list. If specified, the sidecar will search for config-maps/secrets inside these namespaces. + # Otherwise the namespace in which the sidecar is running will be used. + # It's also possible to specify ALL to search in all namespaces. + searchNamespace: null + # Method to use to detect ConfigMap changes. With WATCH the sidecar will do a WATCH requests, with SLEEP it will list all ConfigMaps, then sleep for 60 seconds. + watchMethod: WATCH + # search in configmap, secret or both + resource: both + # If specified, the sidecar will look for annotation with this name to create folder and put graph here. + # You can use this parameter together with `provider.foldersFromFilesStructure`to annotate configmaps and create folder structure. + folderAnnotation: null + # Absolute path to shell script to execute after a configmap got reloaded + script: null + # watchServerTimeout: request to the server, asking it to cleanly close the connection after that. + # defaults to 60sec; much higher values like 3600 seconds (1h) are feasible for non-Azure K8S + # watchServerTimeout: 3600 + # + # watchClientTimeout: is a client-side timeout, configuring your local socket. + # If you have a network outage dropping all packets with no RST/FIN, + # this is how long your client waits before realizing & dropping the connection. + # defaults to 66sec (sic!) + # watchClientTimeout: 60 + # + # provider configuration that lets grafana manage the dashboards + provider: + # name of the provider, should be unique + name: sidecarProvider + # orgid as configured in grafana + orgid: 1 + # folder in which the dashboards should be imported in grafana + folder: '' + # type of the provider + type: file + # disableDelete to activate a import-only behaviour + disableDelete: false + # allow updating provisioned dashboards from the UI + allowUiUpdates: false + # allow Grafana to replicate dashboard structure from filesystem + foldersFromFilesStructure: false + # Additional dashboard sidecar volume mounts + extraMounts: [] + # Sets the size limit of the dashboard sidecar emptyDir volume + sizeLimit: {} + datasources: + enabled: true + # label that the configmaps with datasources are marked with + label: grafana_datasource + # value of label that the configmaps with datasources are set to + labelValue: null + # If specified, the sidecar will search for datasource config-maps inside this namespace. + # Otherwise the namespace in which the sidecar is running will be used. + # It's also possible to specify ALL to search in all namespaces + searchNamespace: null + # Method to use to detect ConfigMap changes. With WATCH the sidecar will do a WATCH requests, with SLEEP it will list all ConfigMaps, then sleep for 60 seconds. + watchMethod: WATCH + # search in configmap, secret or both + resource: both + # Endpoint to send request to reload datasources + reloadURL: "http://localhost:3000/api/admin/provisioning/datasources/reload" + skipReload: false + # Deploy the datasource sidecar as an initContainer in addition to a container. + # This is needed if skipReload is true, to load any datasources defined at startup time. + initDatasources: false + # Sets the size limit of the datasource sidecar emptyDir volume + sizeLimit: {} + plugins: + enabled: false + # label that the configmaps with plugins are marked with + label: grafana_plugin + # value of label that the configmaps with plugins are set to + labelValue: null + # If specified, the sidecar will search for plugin config-maps inside this namespace. + # Otherwise the namespace in which the sidecar is running will be used. + # It's also possible to specify ALL to search in all namespaces + searchNamespace: null + # Method to use to detect ConfigMap changes. With WATCH the sidecar will do a WATCH requests, with SLEEP it will list all ConfigMaps, then sleep for 60 seconds. + watchMethod: WATCH + # search in configmap, secret or both + resource: both + # Endpoint to send request to reload plugins + reloadURL: "http://localhost:3000/api/admin/provisioning/plugins/reload" + skipReload: false + # Deploy the datasource sidecar as an initContainer in addition to a container. + # This is needed if skipReload is true, to load any plugins defined at startup time. + initPlugins: false + # Sets the size limit of the plugin sidecar emptyDir volume + sizeLimit: {} + notifiers: + enabled: false + # label that the configmaps with notifiers are marked with + label: grafana_notifier + # If specified, the sidecar will search for notifier config-maps inside this namespace. + # Otherwise the namespace in which the sidecar is running will be used. + # It's also possible to specify ALL to search in all namespaces + searchNamespace: null + # search in configmap, secret or both + resource: both + # Sets the size limit of the notifier sidecar emptyDir volume + sizeLimit: {} + +## Override the deployment namespace +## +namespaceOverride: "" + +## Number of old ReplicaSets to retain +## +revisionHistoryLimit: 10 + +## Add a seperate remote image renderer deployment/service +imageRenderer: + # Enable the image-renderer deployment & service + enabled: false + replicas: 1 + image: + registry: docker.io + # image-renderer Image repository + repository: grafana/grafana-image-renderer + # image-renderer Image tag + tag: latest + # image-renderer Image sha (optional) + sha: "" + # image-renderer ImagePullPolicy + pullPolicy: Always + # extra environment variables + env: + HTTP_HOST: "0.0.0.0" + # RENDERING_ARGS: --no-sandbox,--disable-gpu,--window-size=1280x758 + # RENDERING_MODE: clustered + # IGNORE_HTTPS_ERRORS: true + # image-renderer deployment serviceAccount + serviceAccountName: "" + # image-renderer deployment securityContext + securityContext: {} + # image-renderer deployment Host Aliases + hostAliases: [] + # image-renderer deployment priority class + priorityClassName: '' + service: + # Enable the image-renderer service + enabled: true + # image-renderer service port name + portName: 'http' + # image-renderer service port used by both service and deployment + port: 8081 + targetPort: 8081 + # If https is enabled in Grafana, this needs to be set as 'https' to correctly configure the callback used in Grafana + grafanaProtocol: http + # In case a sub_path is used this needs to be added to the image renderer callback + grafanaSubPath: "" + # name of the image-renderer port on the pod + podPortName: http + # number of image-renderer replica sets to keep + revisionHistoryLimit: 10 + networkPolicy: + # Enable a NetworkPolicy to limit inbound traffic to only the created grafana pods + limitIngress: true + # Enable a NetworkPolicy to limit outbound traffic to only the created grafana pods + limitEgress: false + resources: + limits: + cpu: 200m + memory: 200Mi + requests: + cpu: 100m + memory: 100Mi + ## Node labels for pod assignment + ## ref: https://kubernetes.io/docs/user-guide/node-selection/ + # + nodeSelector: {} + + ## Tolerations for pod assignment + ## ref: https://kubernetes.io/docs/concepts/configuration/taint-and-toleration/ + ## + tolerations: [] + + ## Affinity for pod assignment (evaluated as template) + ## ref: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#affinity-and-anti-affinity + ## + affinity: {} + +networkPolicy: + ## @param networkPolicy.enabled Enable creation of NetworkPolicy resources. Only Ingress traffic is filtered for now. + ## + enabled: false + ## @param networkPolicy.allowExternal Don't require client label for connections + ## The Policy model to apply. When set to false, only pods with the correct + ## client label will have network access to grafana port defined. + ## When true, grafana will accept connections from any source + ## (with the correct destination port). + ## + allowExternal: true + ## @param networkPolicy.explicitNamespacesSelector A Kubernetes LabelSelector to explicitly select namespaces from which traffic could be allowed + ## 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 grafana. + ## But sometimes, we want the grafana 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: {} + +# Enable backward compatibility of kubernetes where version below 1.13 doesn't have the enableServiceLinks option +enableKubeBackwardCompatibility: false + +# Create a dynamic manifests via values: +extraObjects: [] + # - apiVersion: "kubernetes-client.io/v1" + # kind: ExternalSecret + # metadata: + # name: grafana-secrets + # spec: + # backendType: gcpSecretsManager + # data: + # - key: grafana-admin-password + # name: adminPassword diff --git a/charts/trilio/k8s-triliovault-operator/4.0.5/crds/triliovault.trilio.io_triliovaultmanagers.yaml b/charts/trilio/k8s-triliovault-operator/4.0.5/crds/triliovault.trilio.io_triliovaultmanagers.yaml new file mode 100644 index 000000000..d16b6465f --- /dev/null +++ b/charts/trilio/k8s-triliovault-operator/4.0.5/crds/triliovault.trilio.io_triliovaultmanagers.yaml @@ -0,0 +1,1227 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.10.0 + creationTimestamp: null + name: triliovaultmanagers.triliovault.trilio.io +spec: + group: triliovault.trilio.io + names: + kind: TrilioVaultManager + listKind: TrilioVaultManagerList + plural: triliovaultmanagers + shortNames: + - tvm + singular: triliovaultmanager + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .status.releaseVersion + name: TrilioVault-Version + type: string + - jsonPath: .spec.applicationScope + name: Scope + type: string + - jsonPath: .status.status + name: Status + type: string + name: v1 + schema: + openAPIV3Schema: + description: TrilioVaultManager is the Schema for the triliovaultmanagers + 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: TrilioVaultManagerSpec defines the desired state of TrilioVaultManager + properties: + affinity: + description: The scheduling constraints on application pods. + 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 + required: + - key + - operator + type: object + type: array + 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 + required: + - key + - operator + type: object + type: array + 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 + 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 + required: + - key + - operator + type: object + type: array + 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 + required: + - key + - operator + type: object + type: array + type: object + x-kubernetes-map-type: atomic + type: array + 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. + 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 + required: + - key + - operator + type: object + type: array + 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 + 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 + required: + - key + - operator + type: object + type: array + 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 + 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 + 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. + 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 + required: + - key + - operator + type: object + type: array + 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 + 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 + required: + - key + - operator + type: object + type: array + 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 + 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 + 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. + 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 + required: + - key + - operator + type: object + type: array + 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 + 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 + required: + - key + - operator + type: object + type: array + 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 + 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 + 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. + 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 + required: + - key + - operator + type: object + type: array + 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 + 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 + required: + - key + - operator + type: object + type: array + 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 + 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 + type: object + type: object + applicationScope: + description: Scope for the application which will be installed in + the cluster NamespaceScope or ClusterScope + enum: + - Cluster + - Namespaced + type: string + componentConfiguration: + description: ComponentConfiguration holds all the field related to + the TVK deployments. + properties: + admission-webhook: + description: AdmissionWebhook holds all configuration for the + admission-webhook deployment + type: object + x-kubernetes-preserve-unknown-fields: true + control-plane: + description: ControlPlane holds all configuration for the control-plane + deployment + type: object + x-kubernetes-preserve-unknown-fields: true + exporter: + description: Exporter holds all configuration for the exporter + deployment. + type: object + x-kubernetes-preserve-unknown-fields: true + ingress-controller: + description: IngressController holds all configuration for the + ingress-controller deployment + type: object + x-kubernetes-preserve-unknown-fields: true + web: + description: Web holds all configuration for the web deployment + type: object + x-kubernetes-preserve-unknown-fields: true + web-backend: + description: WebBackend holds all configuration for the web-backend + deployment + type: object + x-kubernetes-preserve-unknown-fields: true + type: object + csiConfig: + description: CSIConfig is the configuration for the CSI drivers which + doesn't support snapshot functionality + properties: + exclude: + description: Exclude denotes the list of CSI drivers to be excluded + from the non-snapshot functionality category + items: + type: string + type: array + include: + description: Include denotes the list of CSI drivers to be included + in the non-snapshot functionality category + items: + type: string + type: array + type: object + dataJobLimits: + 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: 'Deprecated: DataJobLimits are the resource limits for + all the data processing jobs.' + type: object + dataJobResources: + description: DataJobResources is the resource limits & requests for + all the data processing jobs. + 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. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + type: object + type: object + datamoverLogLevel: + description: DatamoverLogLevel is a log level used in datamover i.e. + data upload/restore part of the TVK. + enum: + - Panic + - Fatal + - Error + - Warn + - Info + - Debug + - Trace + type: string + deploymentLimits: + 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: 'Deprecated: DeploymentLimits are the resource limits + for all the deployments.' + type: object + helmValues: + description: HelmValues holds all the additional fields in the values.yaml + of TVK helm chart. + type: object + x-kubernetes-preserve-unknown-fields: true + helmVersion: + description: 'Deprecated: Helm Version' + properties: + tillerNamespace: + type: string + version: + enum: + - v3 + type: string + required: + - version + type: object + ingressConfig: + description: IngressConfig holds field related to ingress resource + to access the TVK UI. + properties: + annotations: + additionalProperties: + type: string + type: object + host: + type: string + ingressClass: + type: string + tlsSecretName: + type: string + type: object + logLevel: + description: LogLevel is a level used in TVK logging. + enum: + - Panic + - Fatal + - Error + - Warn + - Info + - Debug + - Trace + type: string + metadataJobLimits: + 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: 'Deprecated: MetadataJobLimits are the resource limits + for all the meta processing jobs.' + type: object + metadataJobResources: + description: MetadataJobResources is the resource limits & requests + for all the meta processing jobs. + 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. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + type: object + type: object + nodeSelector: + additionalProperties: + type: string + description: NodeSelector specifies a map of key-value pairs. For + the pod to be eligible to run on a node, the node must have each + of the indicated key-value pairs as labels. + type: object + resources: + description: 'Deprecated: Resources are the resource requirements + for the containers.' + 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. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + type: object + type: object + restoreNamespaces: + description: 'Deprecated: RestoreNamespaces are the namespace where + you want to restore your applications. Restore Namespaces depends + on your k8s RBAC' + items: + type: string + type: array + tolerations: + description: The toleration of application against the specific taints + on the nodes + 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 + trilioVaultAppVersion: + description: 'Deprecated: TrilioVaultAppVersion Helm Chart version' + type: string + tvkInstanceName: + description: TVKInstanceName is a TVK installation name to be displayed + on UI. + type: string + required: + - applicationScope + type: object + status: + description: TrilioVaultManagerStatus defines the observed state of TrilioVaultManager + properties: + conditions: + items: + properties: + lastTransitionTime: + format: date-time + nullable: true + type: string + message: + minLength: 0 + type: string + reason: + enum: + - InstallSuccessful + - UpdateSuccessful + - UninstallSuccessful + - InstallError + - UpdateError + - ReconcileError + - UninstallError + type: string + status: + enum: + - "True" + - "False" + - Unknown + type: string + type: + enum: + - Initialized + - Deployed + - Updated + - ReleaseFailed + - Irreconcilable + type: string + type: object + type: array + dashboard: + type: string + deployedRelease: + properties: + manifest: + type: string + name: + type: string + type: object + helmRevision: + type: integer + releaseVersion: + type: string + status: + type: string + type: object + type: object + served: true + storage: true + subresources: + status: {} diff --git a/charts/trilio/k8s-triliovault-operator/4.0.5/questions.yaml b/charts/trilio/k8s-triliovault-operator/4.0.5/questions.yaml new file mode 100644 index 000000000..c5064a13f --- /dev/null +++ b/charts/trilio/k8s-triliovault-operator/4.0.5/questions.yaml @@ -0,0 +1,158 @@ +questions: +- variable: installTVK.enabled + default: true + description: "TriloVault Manager is an instance of TrilioVault for Kubernetes. Selecting this checkbox automatically creates a TrilioVault Manager instance" + required: true + type: boolean + label: "Install TrilioVault Manager Automatically" + group: "TrilioVault Manager Install Configuration" + +- variable: installTVK.tvkInstanceName + show_if: "installTVK.enabled=true" + default: "triliovault-manager" + description: "TrilioVault Manager Instance Name. This will be used to manage the Kubernetes cluster in TVK Management Console and backups performed by the TrilioVault for Kubernetes" + required: true + type: string + label: "TrilioVault Manager Instance Name" + group: "TrilioVault Manager Install Configuration" + +- variable: installTVK.applicationScope + default: Cluster + description: "TrilioVault Manager installation scope: Cluster or Namespaced" + required: true + type: enum + label: "TrilioVault Manager Installation Scope" + group: "TrilioVault Manager Install Configuration" + options: + - "Cluster" + - "Namespaced" + +- variable: installTVK.ingressConfig.host + default: "rancher.k8s-tvk.com" + description: "Hostname URL to access the TVK Management Console - For example: rancher.k8s-tvk.com" + required: true + type: hostname + label: "TVK Management Console Hostname URL" + group: "Ingress Configuration" + +- variable: installTVK.ingressConfig.tlsSecretName + default: "" + description: "TLS Secret containing an appropriate certificate to access the TVK Management Console over HTTPS protocol. Secret should of type kubernetes.io/tls" + required: false + type: secret + label: "TLS Secret of type kubernetes.io/tls (Optional)" + group: "Ingress Configuration" + + +- variable: installTVK.ComponentConfiguration.ingressController.service.type + default: "NodePort" + description: "Ingress Controller Service Type to access the TVK Management Console" + required: true + type: enum + label: "Ingress Controller Service Type" + group: "Ingress Configuration" + options: + - "NodePort" + - "LoadBalancer" + +- variable: installTVK.ingressConfig.annotations + default: "" + description: "Annotations to add for the TrilioVault Manager ingress resource - For example: {'foo':'bar'}" + required: false + type: string + label: "Annotations for Ingress Resource (Optional)" + group: "Ingress Configuration" + +- variable: proxySettings.PROXY_ENABLED + default: false + description: "Select this checkbox to deploy the TrilioVault Manager via a proxy server" + required: false + type: boolean + label: "Proxy Settings (Optional)" + group: "Proxy Settings" + show_subquestion_if: true + subquestions: + - variable: proxySettings.NO_PROXY + default: "" + description: "Provide the user defined IPs/hosts and subnets to exempt from proxy. User can provide comma separated values. For example: 'localhost,127.0.0.1,10.239.112.0/20,10.240.0.0/14'" + required: false + type: string + label: "No Proxy (Optional)" + group: "Proxy Settings" + - variable: proxySettings.HTTP_PROXY + default: "" + description: "Provide HTTP proxy information. For example: http://:@:" + required: true + type: string + label: "HTTP Proxy" + group: "Proxy Settings" + - variable: proxySettings.HTTPS_PROXY + default: "" + description: "Provide HTTPS proxy information. For example: https://:@:" + required: true + type: string + label: "HTTPS Proxy" + group: "Proxy Settings" + - variable: proxySettings.CA_BUNDLE_CONFIGMAP + default: "" + description: "Provide a CA Certificate bundle configmap present on the Kubernetes cluster to communicate with the proxy server" + required: false + type: string + label: "CA Certificate Bundle Configmap Name (Optional)" + group: "Proxy Settings" + +- variable: observability.enabled + default: false + description: "Select this checkbox to deploy the Observability Stack with Triliovault operator" + required: false + type: boolean + label: "Observability Stack (Optional)" + group: "Observability" + show_subquestion_if: true + subquestions: + - variable: observability.logging.loki.enabled + default: true + description: "Select this checkbox to deploy the Logging Stack with Loki" + required: true + type: boolean + label: "Logging with Loki" + group: "Logging" + - variable: observability.logging.promtail.enabled + default: true + description: "Select this checkbox to deploy the Logging Stack with Promtail" + required: true + type: boolean + label: "Logging with Promtail" + group: "Logging" + - variable: observability.monitoring.prometheus.enabled + default: true + description: "Select this checkbox to deploy the Monitoring Stack with Prometheus" + required: true + type: boolean + label: "Monitoring with Prometheus" + group: "Monitoring" + - variable: observability.monitoring.prometheus.server.enabled + default: true + description: "Select this checkbox to deploy the Monitoring Stack with Prometheus Server" + required: true + type: boolean + label: "Monitoring with Prometheus Server" + group: "Monitoring" + - variable: observability.visualization.grafana.enabled + default: true + description: "Select this checkbox to deploy the Visualization Stack with Grafana" + required: true + type: boolean + label: "Visualization with Grafana" + group: "Visualization" + - variable: observability.visualization.grafana.service.type + show_if: "observability.visualization.grafana.enabled=true" + default: "ClusterIP" + description: "Grafana Service Type to access the Grafana Dashboards" + required: true + type: enum + label: "Grafana Service Type" + group: "Visualization" + options: + - "NodePort" + - "LoadBalancer" diff --git a/charts/trilio/k8s-triliovault-operator/4.0.5/templates/NOTES.txt b/charts/trilio/k8s-triliovault-operator/4.0.5/templates/NOTES.txt new file mode 100644 index 000000000..1af1d0303 --- /dev/null +++ b/charts/trilio/k8s-triliovault-operator/4.0.5/templates/NOTES.txt @@ -0,0 +1,59 @@ +TrilioVault Operator is a helm based operator which install/upgrade/delete the helm Chart of the TrilioVault For Kubernetes. +This operator watches over the entire helm application of TrilioVault for Kubernetes and has self-healing capabilities. + +To verify that TrilioVault Operator has started, run: + + kubectl --namespace={{ .Release.Namespace }} wait --for=condition=ready pod -l "release={{ .Release.Name }}" + +{{ if .Values.installTVK.enabled }} +In one click install, a cluster scope TVM custom resource triliovault-manager is created, you can check its +configuration by running following command: + + kubectl --namespace {{ .Release.Namespace }} get triliovaultmanagers.triliovault.trilio.io triliovault-manager -o yaml + +{{- else }} + +Once the Triliovault operator is in running state, you can create the TrilioVault for Kubernetes(TVK) with the +following custom resource: + + apiVersion: triliovault.trilio.io/v1 + kind: TrilioVaultManager + metadata: + labels: + app: triliovault + name: triliovault-manager + namespace: {{ .Release.Namespace }} + spec: + trilioVaultAppVersion: latest + applicationScope: Cluster + ingressConfig: + host: "" + componentConfiguration: + ingress-controller: + enabled: true + service: + type: LoadBalancer + +Once the above CR has been created, you have to wait for the TVK pods to come up. +{{- end }} + +To check all the TVK pods come into running state, run: + + kubectl --namespace {{ .Release.Namespace }} wait --for=condition=ready pod -l "release=triliovault-manager-{{ .Release.Namespace }}" + +Once all the pods are in running state, you can access the TVK UI from your browser using following steps: + +{{- if .Values.installTVK.enabled }} +{{- if eq .Values.installTVK.ComponentConfiguration.ingressController.service.type "LoadBalancer" }} + 1. Find the external IP of the service `k8s-triliovault-ingress-nginx-controller` + 2. Hit the URL in browser: https:// +{{- else }} + 1. Find the NodePort from the service `k8s-triliovault-ingress-nginx-controller` + 2. Hit the URL in browser with NodePort: https://:/ +{{- end }} +{{- end }} + +For more details on how to access the TVK UI, follow this guide: https://docs.trilio.io/kubernetes/management-console-ui/accessing-the-ui + +You can start backup and restore of your application using TVK. For more details on how to do that, please follow our +getting started guide: https://docs.trilio.io/kubernetes/advanced-configuration/management-console diff --git a/charts/trilio/k8s-triliovault-operator/4.0.5/templates/TVMCustomResource.yaml b/charts/trilio/k8s-triliovault-operator/4.0.5/templates/TVMCustomResource.yaml new file mode 100644 index 000000000..24e4d2052 --- /dev/null +++ b/charts/trilio/k8s-triliovault-operator/4.0.5/templates/TVMCustomResource.yaml @@ -0,0 +1,64 @@ +{{- if .Values.installTVK.enabled }} +{{- if not (lookup "triliovault.trilio.io/v1" "TrilioVaultManager" "" "").items }} + {{template "k8s-triliovault-operator.tlsSecretValidation" .}} +apiVersion: triliovault.trilio.io/v1 +kind: TrilioVaultManager +metadata: + name: "triliovault-manager" + namespace: {{ .Release.Namespace }} + annotations: + "helm.sh/hook": pre-install +spec: + applicationScope: {{ .Values.installTVK.applicationScope }} + {{- if .Values.installTVK.tvkInstanceName }} + tvkInstanceName: {{ .Values.installTVK.tvkInstanceName }} + {{- end }} + {{- if or .Values.imagePullSecret .Values.svcAccountName .Values.observability.enabled }} + helmValues: + {{- if .Values.observability.enabled }} + observability: + name: {{ .Values.observability.name }} + namespace: {{ default .Release.Namespace }} + {{- end }} + {{- if include "k8s-triliovault-operator.imagePullSecret" . }} + imagePullSecret: {{ template "k8s-triliovault-operator.imagePullSecret" . }} + {{- end }} + {{- if .Values.svcAccountName }} + svcAccountName: {{ .Values.svcAccountName }} + {{- end }} + {{- end }} + {{- if .Values.nodeSelector }} + nodeSelector: {{- .Values.nodeSelector | toYaml | nindent 4 }} + {{- end }} + {{- if .Values.affinity }} + affinity: + {{- toYaml .Values.affinity | nindent 4 }} + {{- end }} + {{- if .Values.tolerations }} + tolerations: + {{- toYaml .Values.tolerations | nindent 4 }} + {{- end }} + # User can configure the ingress hosts, annotations and TLS secret through the ingressConfig section + ingressConfig: + {{- if and (gt (len .Values.installTVK.ingressConfig.annotations) 0) (not .Values.installTVK.ComponentConfiguration.ingressController.enabled) }} + annotations: + {{- range $key, $value := .Values.installTVK.ingressConfig.annotations }} + {{ $key }}: {{ $value | quote }} + {{- end -}} + {{- end }} + host: {{ .Values.installTVK.ingressConfig.host | quote }} + {{- if not .Values.installTVK.ComponentConfiguration.ingressController.enabled }} + ingressClass: {{ .Values.installTVK.ingressConfig.ingressClass | quote }} + {{- end }} + {{- if .Values.installTVK.ingressConfig.tlsSecretName }} + tlsSecretName: {{ .Values.installTVK.ingressConfig.tlsSecretName | quote }} + {{- end }} + # TVK components configuration, currently supports control-plane, web, exporter, web-backend, ingress-controller, admission-webhook. + # User can configure resources for all componentes and can configure service type and host for the ingress-controller + componentConfiguration: + ingress-controller: + enabled: {{ .Values.installTVK.ComponentConfiguration.ingressController.enabled }} + service: + type: {{ .Values.installTVK.ComponentConfiguration.ingressController.service.type }} +{{- end -}} +{{- end -}} diff --git a/charts/trilio/k8s-triliovault-operator/4.0.5/templates/TVMSecret.yaml b/charts/trilio/k8s-triliovault-operator/4.0.5/templates/TVMSecret.yaml new file mode 100644 index 000000000..0d9d8a9df --- /dev/null +++ b/charts/trilio/k8s-triliovault-operator/4.0.5/templates/TVMSecret.yaml @@ -0,0 +1,25 @@ +{{- if .Values.observability.enabled }} +apiVersion: v1 +kind: Secret +metadata: + name: tvk-integration + namespace: {{ .Release.Namespace }} + annotations: + meta.helm.sh/release-namespace: {{ .Release.Namespace }} + labels: + {{- include "k8s-triliovault-operator.labels" . | nindent 4 }} + triliovault.trilio.io/owner: {{ template "k8s-triliovault-operator.appName" . }} + app.kubernetes.io/instance: {{ template "k8s-triliovault-operator.appName" . }}-validation-webhook-configuration + triliovault.trilio.io/observability: "true" +type: Opaque +stringData: + integration: |- + type: Loki + protocol: "" + host: "" + port: "" + path: "/api/v1/datasource" + username: "admin" + password: {{ .Values.observability.visualization.grafana.adminPassword | quote }} +{{- end }} + diff --git a/charts/trilio/k8s-triliovault-operator/4.0.5/templates/_helpers.tpl b/charts/trilio/k8s-triliovault-operator/4.0.5/templates/_helpers.tpl new file mode 100644 index 000000000..120ef9c7d --- /dev/null +++ b/charts/trilio/k8s-triliovault-operator/4.0.5/templates/_helpers.tpl @@ -0,0 +1,134 @@ +{{/* +Expand the name of the chart. +*/}} +{{- define "k8s-triliovault-operator.name" -}} +{{- default .Release.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}} +{{- end -}} + +{{- define "k8s-triliovault-operator.appName" -}} +{{- printf "%s" .Chart.Name -}} +{{- 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 "k8s-triliovault-operator.fullname" -}} +{{- printf "%s" .Chart.Name -}} +{{- end -}} + +{{/* +Return the proper TrilioVault Operator image name +*/}} +{{- define "k8s-triliovault-operator.image" -}} +{{- $registryName := .Values.image.registry -}} +{{- $repositoryName := .Values.image.repository -}} +{{- $tag := .Values.image.tag | toString -}} +{{- printf "%s/%s:%s" $registryName $repositoryName $tag -}} +{{- end -}} + +{{/* +Validation of the secret of CA bundle if provided +*/}} +{{- define "k8s-triliovault-operator.caBundleValidation" -}} +{{- if .Values.proxySettings.CA_BUNDLE_CONFIGMAP }} +{{- if not (lookup "v1" "ConfigMap" .Release.Namespace .Values.proxySettings.CA_BUNDLE_CONFIGMAP) }} + {{ fail "Proxy CA bundle proxy is not present in the release namespace" }} +{{- else }} + {{- $caMap := (lookup "v1" "ConfigMap" .Release.Namespace .Values.proxySettings.CA_BUNDLE_CONFIGMAP).data }} + {{- if not (get $caMap "ca-bundle.crt") }} + {{ fail "Proxy CA certificate file key should be ca-bundle.crt" }} + {{- end }} +{{- end }} +{{- end }} +{{- end -}} + +{{/* +Validation for the ingress tlsSecret, should exists if provided +*/}} + +{{- define "k8s-triliovault-operator.tlsSecretValidation" }} +{{- if .Values.installTVK.ingressConfig.tlsSecretName -}} +{{- if not (lookup "v1" "Secret" .Release.Namespace .Values.installTVK.ingressConfig.tlsSecretName ) -}} + {{ fail "Ingress tls secret is not present in the release namespace" }} +{{- end -}} +{{- end -}} +{{- end -}} + + +{{- define "k8s-triliovault-operator.preFlightValidation" }} +{{- if not .Values.preflight.storageClass }} + {{ fail "Provide the name of storage class as you have enabled the preflight" }} +{{- else }} + {{- if not (lookup "storage.k8s.io/v1" "StorageClass" "" .Values.preflight.storageClass) }} + {{ fail "Storage class provided is not present in the cluster" }} + {{- end }} +{{- end }} +{{- end }} + +{{- define "k8s-triliovault-operator.priorityClassValidator" }} +{{- if .Values.priorityClassName -}} +{{- if not (lookup "scheduling.k8s.io/v1" "PriorityClass" "" .Values.priorityClassName) }} + {{ fail "Priority class provided is not present in the cluster" }} +{{- end }} +{{- end }} +{{- end }} + +{{/* +Create unified labels for k8s-triliovault-operator components +*/}} +{{- define "k8s-triliovault-operator.labels" -}} +app.kubernetes.io/part-of: {{ template "k8s-triliovault-operator.appName" . }} +app.kubernetes.io/name: {{ template "k8s-triliovault-operator.appName" . }} +app.kubernetes.io/managed-by: {{ .Release.Service }} +{{- end -}} + +{{- define "k8s-triliovault-operator.serviceAccountName" -}} + {{- if eq .Values.svcAccountName "" -}} + {{- printf "%s" "k8s-triliovault-operator-service-account" -}} + {{- else -}} + {{- printf "%s" .Values.svcAccountName -}} + {{- end -}} +{{- end -}} + +{{- define "k8s-triliovault-operator.preflightServiceAccountName" -}} + {{- if eq .Values.svcAccountName "" -}} + {{- printf "%s" "k8s-triliovault-operator-preflight-service-account" -}} + {{- else -}} + {{- printf "%s" .Values.svcAccountName -}} + {{- end -}} +{{- end -}} + +{{/* +Return the imagePullSecret name in below priority order +1. Returns imagePullSecret name if imagePullSecret is supplied via helm value during installation time +2. If the helm value is not provided and a service account name is provided via svcAccountName parameter, this extracts and returns imagePullSecret from service account if available. + (In case of multiple imagePullSecrets are attached to a service account, only the first one is taken into the consideration) +3. Returns empty string not imagePullSecret is not found in any of the above two +*/}} +{{- define "k8s-triliovault-operator.imagePullSecret" -}} + {{- if eq .Values.imagePullSecret "" -}} + {{- if eq .Values.svcAccountName "" -}} + {{- printf "" -}} + {{- else -}} + {{- if (lookup "v1" "ServiceAccount" .Release.Namespace .Values.svcAccountName).imagePullSecrets -}} + {{- if (index (lookup "v1" "ServiceAccount" .Release.Namespace .Values.svcAccountName).imagePullSecrets 0).name -}} + {{- printf "%s" (index (lookup "v1" "ServiceAccount" .Release.Namespace .Values.svcAccountName).imagePullSecrets 0).name -}} + {{- else -}} + {{- printf "" -}} + {{- end -}} + {{- else -}} + {{- printf "" -}} + {{- end -}} + {{- end -}} + {{- else -}} + {{- printf "%s" .Values.imagePullSecret -}} + {{- end -}} +{{- end -}} + +{{- define "k8s-triliovault-operator.observability" -}} +app.kubernetes.io/part-of: k8s-triliovault-operator +app.kubernetes.io/managed-by: k8s-triliovault-operator +app.kubernetes.io/name: k8s-triliovault-operator +{{- end -}} diff --git a/charts/trilio/k8s-triliovault-operator/4.0.5/templates/clusterrole.yaml b/charts/trilio/k8s-triliovault-operator/4.0.5/templates/clusterrole.yaml new file mode 100644 index 000000000..443e499d4 --- /dev/null +++ b/charts/trilio/k8s-triliovault-operator/4.0.5/templates/clusterrole.yaml @@ -0,0 +1,148 @@ +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: {{template "k8s-triliovault-operator.name" .}}-{{.Release.Namespace}}-manager-role + labels: + {{- include "k8s-triliovault-operator.labels" . | nindent 4 }} + app.kubernetes.io/instance: {{template "k8s-triliovault-operator.appName" .}}-manager-role +rules: + - apiGroups: + - '*' + resources: + - '*' + verbs: + - get + - list + - watch + - apiGroups: + - apiextensions.k8s.io + resources: + - customresourcedefinitions + - customresourcedefinitions/finalizers + verbs: + - create + - update + - delete + - patch + - apiGroups: + - "" + resources: + - serviceaccounts + - services + - secrets + - events + - pods + - endpoints + - configmaps + - secrets/finalizers + - events/finalizers + - pods/finalizers + - endpoints/finalizers + - configmaps/finalizers + - services/finalizers + - serviceaccounts/finalizers + verbs: + - create + - update + - delete + - patch + - apiGroups: + - admissionregistration.k8s.io + resources: + - validatingwebhookconfigurations + - mutatingwebhookconfigurations + - validatingwebhookconfigurations/finalizers + - mutatingwebhookconfigurations/finalizers + verbs: + - create + - update + - delete + - patch + - apiGroups: + - apps + resources: + - deployments + - deployments/finalizers + verbs: + - create + - update + - delete + - patch + - apiGroups: + - rbac.authorization.k8s.io + resources: + - clusterroles + - clusterrolebindings + - roles + - rolebindings + - clusterroles/finalizers + - clusterrolebindings/finalizers + - roles/finalizers + - rolebindings/finalizers + verbs: + - create + - update + - delete + - patch + - bind + - escalate + - apiGroups: + - triliovault.trilio.io + resources: + - '*' + verbs: + - '*' + - apiGroups: + - "" + resources: + - namespaces + - namespaces/finalizers + verbs: + - update + - apiGroups: + - batch + resources: + - cronjobs + - cronjobs/finalizers + verbs: + - create + - delete + - update + - patch + - apiGroups: + - batch + resources: + - jobs + - jobs/finalizers + verbs: + - create + - delete + - apiGroups: + - policy + resources: + - poddisruptionbudgets + - poddisruptionbudgets/finalizers + verbs: + - create + - update + - patch + - delete + - apiGroups: + - networking.k8s.io + resources: + - ingresses + - ingressclasses + - ingresses/finalizers + - ingressclasses/finalizers + verbs: + - create + - patch + - update + - delete + - apiGroups: + - networking.k8s.io + resources: + - ingresses/status + verbs: + - update diff --git a/charts/trilio/k8s-triliovault-operator/4.0.5/templates/clusterrole_binding.yaml b/charts/trilio/k8s-triliovault-operator/4.0.5/templates/clusterrole_binding.yaml new file mode 100644 index 000000000..e0f0bdb5f --- /dev/null +++ b/charts/trilio/k8s-triliovault-operator/4.0.5/templates/clusterrole_binding.yaml @@ -0,0 +1,15 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: {{ template "k8s-triliovault-operator.name" . }}-{{ .Release.Namespace }}-manager-rolebinding + labels: + {{- include "k8s-triliovault-operator.labels" . | nindent 4 }} + app.kubernetes.io/instance: {{ template "k8s-triliovault-operator.appName" . }}-manager-rolebinding +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: {{ template "k8s-triliovault-operator.name" . }}-{{ .Release.Namespace }}-manager-role +subjects: +- kind: ServiceAccount + name: {{ template "k8s-triliovault-operator.serviceAccountName" . }} + namespace: {{ .Release.Namespace }} diff --git a/charts/trilio/k8s-triliovault-operator/4.0.5/templates/deployment.yaml b/charts/trilio/k8s-triliovault-operator/4.0.5/templates/deployment.yaml new file mode 100644 index 000000000..9236b8113 --- /dev/null +++ b/charts/trilio/k8s-triliovault-operator/4.0.5/templates/deployment.yaml @@ -0,0 +1,276 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ template "k8s-triliovault-operator.fullname" . }} + namespace: {{ .Release.Namespace }} + labels: + app: {{ template "k8s-triliovault-operator.fullname" . }} + release: "{{ .Release.Name }}" + {{- include "k8s-triliovault-operator.labels" . | nindent 4 }} + app.kubernetes.io/instance: {{ template "k8s-triliovault-operator.appName" . }} +spec: + strategy: + type: RollingUpdate + rollingUpdate: + maxSurge: 25% + maxUnavailable: 25% + selector: + matchLabels: + app: {{ template "k8s-triliovault-operator.fullname" . }} + release: "{{ .Release.Name }}" + replicas: {{ .Values.replicaCount }} + template: + metadata: + {{- if .Values.podAnnotations }} + annotations: + {{- range $key, $value := .Values.podAnnotations }} + {{ $key }}: {{ $value | quote }} + {{- end }} + {{- end }} + labels: + app: {{ template "k8s-triliovault-operator.fullname" . }} + release: "{{ .Release.Name }}" + {{- include "k8s-triliovault-operator.labels" . | nindent 8 }} + app.kubernetes.io/instance: {{ template "k8s-triliovault-operator.appName" . }} + {{- range $key, $value := .Values.podLabels }} + {{ $key }}: {{ $value | quote }} + {{- end }} + spec: + hostNetwork: {{ .Values.podSpec.hostNetwork }} + hostIPC: {{ .Values.podSpec.hostIPC }} + hostPID: {{ .Values.podSpec.hostPID }} + {{- if .Values.priorityClassName }} + {{ template "k8s-triliovault-operator.priorityClassValidator" .}} + priorityClassName: {{ .Values.priorityClassName }} + {{- end }} + {{- if .Values.securityContext }} + securityContext: + {{- toYaml .Values.podSpec.securityContext | nindent 8 }} + {{- end }} + {{- if include "k8s-triliovault-operator.imagePullSecret" . }} + imagePullSecrets: + - name: {{ template "k8s-triliovault-operator.imagePullSecret" . }} + {{- end }} + containers: + - name: k8s-triliovault-operator + image: {{ .Values.registry }}/{{ index .Values "k8s-triliovault-operator" "repository" }}:{{ .Values.tag }} + imagePullPolicy: {{ .Values.image.pullPolicy | quote }} + env: + {{- if .Values.proxySettings.PROXY_ENABLED }} + - name: HTTP_PROXY + value: {{ .Values.proxySettings.HTTP_PROXY }} + - name: HTTPS_PROXY + value: {{ .Values.proxySettings.HTTPS_PROXY }} + - name: NO_PROXY + value: {{ .Values.proxySettings.NO_PROXY }} + {{- if .Values.proxySettings.CA_BUNDLE_CONFIGMAP }} + - name: PROXY_CA_CONFIGMAP + value: {{ .Values.proxySettings.CA_BUNDLE_CONFIGMAP }} + {{- end }} + {{- end }} + - name: MASTER_ENCRYPTION_KEY_NAMESPACE + value: {{ .Values.masterEncryptionKeyConfig.namespace | default .Release.Namespace }} + - name: MASTER_ENCRYPTION_KEY_NAME + value: {{ .Values.masterEncryptionKeyConfig.name }} + {{- if .Values.observability.enabled }} + - name: OBSERVABILITY_SECRET_NAME + value: {{ .Values.observability.name }} + - name: OBSERVABILITY_SECRET_NAMESPACE + value: {{ .Values.observability.namespace | default .Release.Namespace }} + {{- end}} + - name: INSTALL_NAMESPACE + value: {{ .Release.Namespace }} + - name: REGISTRY + value: {{ .Values.registry }} + - name: RELATED_IMAGE_INGRESS_CONTROLLER + value: {{ .Values.registry }}/{{ index .Values "relatedImages" "ingress-controller" "image"}}:{{ index .Values "relatedImages" "ingress-controller" "tag" }} + - name: RELATED_IMAGE_KUBE_CERTGEN + value: {{ .Values.registry }}/{{ index .Values "relatedImages" "kube-certgen" "image"}}:{{ index .Values "relatedImages" "kube-certgen" "tag" }} + - name: RELATED_IMAGE_METAMOVER + value: {{ .Values.registry }}/{{ .Values.relatedImages.metamover.image }}:{{ .Values.relatedImages.tags.tvk }} + - name: RELATED_IMAGE_CONTROL_PLANE + value: {{ .Values.registry }}/{{index .Values "relatedImages" "control-plane" "image" }}:{{ .Values.relatedImages.tags.tvk }} + - name: RELATED_IMAGE_WEB + value: {{ .Values.registry }}/{{ .Values.relatedImages.web.image }}:{{ .Values.relatedImages.tags.tvk }} + - name: RELATED_IMAGE_WEB_BACKEND + value: {{ .Values.registry }}/{{ index .Values "relatedImages" "web-backend" "image" }}:{{ .Values.relatedImages.tags.tvk }} + - name: RELATED_IMAGE_EXPORTER + value: {{ .Values.registry }}/{{ .Values.relatedImages.exporter.image }}:{{ .Values.relatedImages.tags.tvk }} + - name: RELATED_IMAGE_ADMISSION_WEBHOOK + value: {{ .Values.registry }}/{{ index .Values "relatedImages" "admission-webhook" "image" }}:{{ .Values.relatedImages.tags.tvk }} + - name: RELATED_IMAGE_ANALYZER + value: {{ .Values.registry }}/{{ .Values.relatedImages.analyzer.image }}:{{ .Values.relatedImages.tags.tvk }} + - name: RELATED_IMAGE_DATAMOVER + value: {{ .Values.registry }}/{{ .Values.relatedImages.datamover.image }}:{{ .Values.relatedImages.tags.tvk }} + - name: RELATED_IMAGE_DATASTORE_ATTACHER + value: {{ .Values.registry }}/{{ index .Values "relatedImages" "datastore-attacher" "image" }}:{{ .Values.relatedImages.tags.tvk }} + - name: RELATED_IMAGE_BACKUP_SCHEDULER + value: {{ .Values.registry }}/{{ index .Values "relatedImages" "backup-scheduler" "image" }}:{{ .Values.relatedImages.tags.tvk }} + - name: RELATED_IMAGE_BACKUP_CLEANER + value: {{ .Values.registry }}/{{ index .Values "relatedImages" "backup-cleaner" "image" }}:{{ .Values.relatedImages.tags.tvk }} + - name: RELATED_IMAGE_TARGET_BROWSER + value: {{ .Values.registry }}/{{ index .Values "relatedImages" "target-browser" "image" }}:{{ .Values.relatedImages.tags.tvk }} + - name: RELATED_IMAGE_BACKUP_RETENTION + value: {{ .Values.registry }}/{{ index .Values "relatedImages" "backup-retention" "image" }}:{{ .Values.relatedImages.tags.tvk }} + - name: RELATED_IMAGE_HOOK + value: {{ .Values.registry }}/{{ .Values.relatedImages.hook.image }}:{{ .Values.relatedImages.tags.tvk }} + - name: RELATED_IMAGE_RESOURCE_CLEANER + value: {{ .Values.registry }}/{{ index .Values "relatedImages" "resource-cleaner" "image" }}:{{ .Values.relatedImages.tags.tvk }} + - name: RELATED_IMAGE_TVK_INIT + value: {{ .Values.registry }}/{{ index .Values "relatedImages" "tvk-init" "image" }}:{{ .Values.relatedImages.tags.tvk }} + - name: RELATED_IMAGE_DEX + value: {{ .Values.registry }}/{{ .Values.relatedImages.dex.image }}:{{ .Values.relatedImages.dex.tag }} + - name: RELATED_IMAGE_MINIO + value: {{ .Values.registry }}/{{ .Values.relatedImages.minio.image }}:{{ .Values.relatedImages.tags.tvk }} + - name: RELATED_IMAGE_NATS + value: {{ .Values.registry }}/{{ .Values.relatedImages.nats.image }}:{{ .Values.relatedImages.nats.tag }} + - name: RELATED_IMAGE_SERVICE_MANAGER + value: {{ .Values.registry }}/{{index .Values "relatedImages" "service-manager" "image" }}:{{ .Values.relatedImages.tags.event }} + - name: RELATED_IMAGE_SYNCER + value: {{ .Values.registry }}/{{ .Values.relatedImages.syncer.image }}:{{ .Values.relatedImages.tags.event }} + - name: RELATED_IMAGE_WATCHER + value: {{ .Values.registry }}/{{ .Values.relatedImages.watcher.image }}:{{ .Values.relatedImages.tags.event }} + - name: RELATED_IMAGE_CONTINUOUS_RESTORE + value: {{ .Values.registry }}/{{ index .Values "relatedImages" "continuous-restore" "image" }}:{{ .Values.relatedImages.tags.tvk }} + - name: ADMISSION_MUTATION_CONFIG + value: {{ template "k8s-triliovault-operator.name" . }}-mutating-webhook-configuration + - name: ADMISSION_VALIDATION_CONFIG + value: {{ template "k8s-triliovault-operator.name" . }}-validating-webhook-configuration + - name: RELEASE_VERSION + value: !!str {{ .Chart.AppVersion }} + - name: OPERATOR_INSTANCE_NAME + value: {{ template "k8s-triliovault-operator.appName" . }} + {{- if .Values.podAnnotations }} + - name: POD_ANNOTATIONS + value: {{ .Values.podAnnotations | toPrettyJson | quote }} + {{- end }} + {{- if .Values.podLabels }} + - name: POD_LABELS + value: {{ .Values.podLabels | toPrettyJson | quote }} + {{- end }} + - name: PRIORITY_CLASS_NAME + value: {{ .Values.priorityClassName }} + livenessProbe: + httpGet: + path: /healthz + port: 8081 + scheme: HTTP + initialDelaySeconds: 60 + periodSeconds: 30 + timeoutSeconds: 2 + successThreshold: 1 + failureThreshold: 3 + readinessProbe: + httpGet: + path: /readyz + port: 8081 + scheme: HTTP + initialDelaySeconds: 5 + periodSeconds: 5 + timeoutSeconds: 1 + successThreshold: 1 + failureThreshold: 3 + volumeMounts: + {{- if and .Values.proxySettings.PROXY_ENABLED .Values.proxySettings.CA_BUNDLE_CONFIGMAP }} + - name: proxy-ca-cert + mountPath: /proxy-certs + readOnly: true + {{- end }} + {{- if .Values.tls.enable }} + - name: helm-tls-certs + mountPath: /root/.helm + readOnly: true + {{- if .Values.tls.verify }} + - name: helm-tls-ca + mountPath: /root/.helm/ca.crt + readOnly: true + {{- end }} + {{- end }} + - mountPath: /tmp/k8s-webhook-server/serving-certs + name: webhook-certs + readOnly: true + {{- if .Values.securityContext }} + securityContext: + {{- toYaml .Values.securityContext | nindent 12 }} + {{- end }} + resources: + limits: + cpu: 200m + memory: 512Mi + requests: + cpu: 10m + memory: 256Mi + initContainers: + - name: webhook-init + image: {{ .Values.registry }}/{{ index .Values "operator-webhook-init" "repository" }}:{{ .Values.tag }} + imagePullPolicy: {{ .Values.image.pullPolicy | quote }} + {{- if .Values.securityContext }} + securityContext: + {{- toYaml .Values.securityContext | nindent 12 }} + {{- end }} + env: + {{- if .Values.proxySettings.PROXY_ENABLED }} + - name: HTTP_PROXY + value: {{ .Values.proxySettings.HTTP_PROXY }} + - name: HTTPS_PROXY + value: {{ .Values.proxySettings.HTTPS_PROXY }} + - name: NO_PROXY + value: {{ .Values.proxySettings.NO_PROXY }} + {{- if .Values.proxySettings.CA_BUNDLE_CONFIGMAP }} + - name: PROXY_CA_CONFIGMAP + value: {{ .Values.proxySettings.CA_BUNDLE_CONFIGMAP }} + {{- end }} + {{- end }} + - name: MASTER_ENCRYPTION_KEY_NAMESPACE + value: {{ .Values.masterEncryptionKeyConfig.namespace | default .Release.Namespace }} + - name: MASTER_ENCRYPTION_KEY_NAME + value: {{ .Values.masterEncryptionKeyConfig.name }} + - name: RELEASE_VERSION + value: !!str {{ .Chart.AppVersion }} + - name: ADMISSION_MUTATION_CONFIG + value: {{ template "k8s-triliovault-operator.name" . }}-mutating-webhook-configuration + - name: ADMISSION_VALIDATION_CONFIG + value: {{ template "k8s-triliovault-operator.name" . }}-validating-webhook-configuration + - name: NAMESPACE_VALIDATION_CONFIG + value: {{ template "k8s-triliovault-operator.name" . }}-ns-validating-webhook-configuration + - name: WEBHOOK_SERVICE + value: {{ template "k8s-triliovault-operator.fullname" . }}-webhook-service + - name: WEBHOOK_NAMESPACE + value: {{ .Release.Namespace }} + - name: SECRET_NAME + value: {{ template "k8s-triliovault-operator.fullname" . }}-webhook-certs + {{- if and .Values.proxySettings.PROXY_ENABLED .Values.proxySettings.CA_BUNDLE_CONFIGMAP }} + volumeMounts: + - name: proxy-ca-cert + mountPath: /proxy-certs + readOnly: true + {{- end }} + serviceAccountName: {{ template "k8s-triliovault-operator.serviceAccountName" . }} + {{- if .Values.nodeSelector }} + nodeSelector: {{- .Values.nodeSelector | toYaml | nindent 8 }} + {{- end }} + {{- if .Values.affinity }} + affinity: + {{- toYaml .Values.affinity | nindent 8 }} + {{- end }} + {{- if .Values.tolerations }} + tolerations: + {{- toYaml .Values.tolerations | nindent 8 }} + {{- end }} + volumes: + {{- if .Values.tls.enable }} + - name: helm-tls-certs + secret: + secretName: {{ .Values.tls.secretName }} + defaultMode: 0400 + {{- if .Values.tls.verify }} + - name: helm-tls-ca + configMap: + name: {{ template "k8s-triliovault-operator.fullname" . }}-helm-tls-ca-config + defaultMode: 0600 + {{- end }} + {{- end }} + - name: webhook-certs + secret: + defaultMode: 420 + secretName: {{ template "k8s-triliovault-operator.fullname" . }}-webhook-certs diff --git a/charts/trilio/k8s-triliovault-operator/4.0.5/templates/mutating-webhook.yaml b/charts/trilio/k8s-triliovault-operator/4.0.5/templates/mutating-webhook.yaml new file mode 100644 index 000000000..dc7902c62 --- /dev/null +++ b/charts/trilio/k8s-triliovault-operator/4.0.5/templates/mutating-webhook.yaml @@ -0,0 +1,28 @@ +apiVersion: admissionregistration.k8s.io/v1 +kind: MutatingWebhookConfiguration +metadata: + name: {{ template "k8s-triliovault-operator.name" . }}-mutating-webhook-configuration + labels: + {{- include "k8s-triliovault-operator.labels" . | nindent 4 }} + app.kubernetes.io/instance: {{ template "k8s-triliovault-operator.appName" . }}-mutating-webhook-configuration +webhooks: +- clientConfig: + service: + name: {{ template "k8s-triliovault-operator.fullname" . }}-webhook-service + namespace: {{ .Release.Namespace }} + path: /mutate-triliovault-trilio-io-v1-triliovaultmanager + failurePolicy: Fail + name: v1-tvm-mutation.trilio.io + rules: + - apiGroups: + - triliovault.trilio.io + apiVersions: + - v1 + operations: + - CREATE + - UPDATE + resources: + - triliovaultmanagers + sideEffects: None + admissionReviewVersions: + - v1 diff --git a/charts/trilio/k8s-triliovault-operator/4.0.5/templates/preflight_job_preinstall_hook.yaml b/charts/trilio/k8s-triliovault-operator/4.0.5/templates/preflight_job_preinstall_hook.yaml new file mode 100644 index 000000000..c94ef2f96 --- /dev/null +++ b/charts/trilio/k8s-triliovault-operator/4.0.5/templates/preflight_job_preinstall_hook.yaml @@ -0,0 +1,196 @@ +{{- if .Values.preflight.enabled -}} +{{- template "k8s-triliovault-operator.preFlightValidation" . }} +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: {{template "k8s-triliovault-operator.name" .}}-{{.Release.Namespace}}-preflight-role + labels: + {{- include "k8s-triliovault-operator.labels" . | nindent 4 }} + app.kubernetes.io/instance: {{template "k8s-triliovault-operator.appName" .}}-preflight-role + annotations: + "helm.sh/hook": "pre-install" + "helm.sh/hook-delete-policy": hook-failed, hook-succeeded + "helm.sh/hook-weight": "1" +rules: + - apiGroups: + - '*' + resources: + - '*' + verbs: + - get + - list + - apiGroups: + - apiextensions.k8s.io + resources: + - customresourcedefinitions + verbs: + - create + - update + - delete + - patch + - apiGroups: + - "" + resources: + - serviceaccounts + - pods + - persistentvolumeclaims + - pods/exec + verbs: + - create + - update + - delete + - patch + - apiGroups: + - rbac.authorization.k8s.io + resources: + - clusterroles + - clusterrolebindings + verbs: + - create + - update + - delete + - patch + - apiGroups: + - "" + resources: + - namespaces + verbs: + - get + - list + - apiGroups: + - batch + resources: + - jobs + verbs: + - create + - delete + - apiGroups: + - storage.k8s.io + resources: + - storageclasses + verbs: + - get + - list + - apiGroups: + - snapshot.storage.k8s.io + resources: + - volumesnapshots + - volumesnapshotclasses + verbs: + - get + - list + - create + - update + - delete + - patch + +--- +{{- if eq .Values.svcAccountName "" }} +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ template "k8s-triliovault-operator.preflightServiceAccountName" . }} + namespace: {{ .Release.Namespace }} + labels: + {{- include "k8s-triliovault-operator.labels" . | nindent 4 }} + app.kubernetes.io/instance: {{ template "k8s-triliovault-operator.appName" . }}-preflight-service-account + annotations: + "helm.sh/hook": "pre-install" + "helm.sh/hook-delete-policy": hook-failed, hook-succeeded + "helm.sh/hook-weight": "2" +{{- end }} +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: {{ template "k8s-triliovault-operator.name" . }}-{{ .Release.Namespace }}-preflight-rolebinding + labels: + {{- include "k8s-triliovault-operator.labels" . | nindent 4 }} + app.kubernetes.io/instance: {{ template "k8s-triliovault-operator.appName" . }}-preflight-rolebinding + annotations: + "helm.sh/hook": "pre-install" + "helm.sh/hook-delete-policy": hook-failed, hook-succeeded + "helm.sh/hook-weight": "3" +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: {{ template "k8s-triliovault-operator.name" . }}-{{ .Release.Namespace }}-preflight-role +subjects: + - kind: ServiceAccount + name: {{ template "k8s-triliovault-operator.preflightServiceAccountName" . }} + namespace: {{ .Release.Namespace }} + +--- +apiVersion: batch/v1 +kind: Job +metadata: + name: {{ template "k8s-triliovault-operator.name" . }}-preflight-job-preinstall-hook-{{ randAlphaNum 4 | lower }} + namespace: {{ .Release.Namespace }} + labels: + app: {{ template "k8s-triliovault-operator.fullname" . }} + release: "{{ .Release.Name }}" + {{- include "k8s-triliovault-operator.labels" . | nindent 4 }} + app.kubernetes.io/instance: {{ template "k8s-triliovault-operator.appName" . }}-preflight-job-preinstall-hook + annotations: + "helm.sh/hook": "pre-install" + "helm.sh/hook-delete-policy": hook-succeeded + "helm.sh/hook-weight": "4" +spec: + backoffLimit: 0 + ttlSecondsAfterFinished: 3600 + template: + spec: + containers: + - name: preflight + image: {{ index .Values "registry" }}/{{ index .Values "preflight" "repository" }}:{{ index .Values "preflight" "imageTag" }} + imagePullPolicy: {{ .Values.image.pullPolicy | quote }} + command: + - /bin/sh + - -c + - >- + /opt/tvk-plugins/preflight run --in-cluster + --log-level={{ .Values.preflight.logLevel }} + --namespace={{ .Release.Namespace }} + {{- if .Values.preflight.cleanupOnFailure }} + --cleanup-on-failure + {{- end }} + {{- if .Values.preflight.imagePullSecret }} + --image-pull-secret={{ .Values.preflight.imagePullSecret }} + {{- end }} + {{- if .Values.preflight.limits }} + --limits={{ .Values.preflight.limits }} + {{- end }} + {{- if .Values.preflight.localRegistry }} + --local-registry={{ .Values.preflight.localRegistry }} + {{- end }} + {{- if .Values.preflight.nodeSelector }} + --node-selector={{ .Values.preflight.nodeSelector }} + {{- end }} + {{- if .Values.preflight.pvcStorageRequest }} + --pvc-storage-request={{ .Values.preflight.pvcStorageRequest }} + {{- end }} + {{- if .Values.preflight.requests }} + --requests={{ .Values.preflight.requests }} + {{- end }} + {{- if .Values.preflight.storageClass }} + --storage-class={{ .Values.preflight.storageClass }} + {{- end }} + {{- if .Values.preflight.volumeSnapshotClass }} + --volume-snapshot-class={{ .Values.preflight.volumeSnapshotClass }} + {{- end }} + {{- if .Values.nodeSelector }} + nodeSelector: {{- .Values.nodeSelector | toYaml | nindent 8 }} + {{- end }} + {{- if .Values.affinity }} + affinity: + {{- toYaml .Values.affinity | nindent 8 }} + {{- end }} + {{- if .Values.tolerations }} + tolerations: + {{- toYaml .Values.tolerations | nindent 8 }} + {{- end }} + restartPolicy: Never + terminationGracePeriodSeconds: 0 + serviceAccountName: {{ template "k8s-triliovault-operator.preflightServiceAccountName" . }} +{{- end }} diff --git a/charts/trilio/k8s-triliovault-operator/4.0.5/templates/secret.yaml b/charts/trilio/k8s-triliovault-operator/4.0.5/templates/secret.yaml new file mode 100644 index 000000000..22f56e848 --- /dev/null +++ b/charts/trilio/k8s-triliovault-operator/4.0.5/templates/secret.yaml @@ -0,0 +1,9 @@ +apiVersion: v1 +kind: Secret +metadata: + name: {{ template "k8s-triliovault-operator.fullname" . }}-webhook-certs + namespace: {{ .Release.Namespace }} + labels: + {{- include "k8s-triliovault-operator.labels" . | nindent 4 }} + app.kubernetes.io/instance: {{ template "k8s-triliovault-operator.appName" . }}-webhook-certs +type: Opaque diff --git a/charts/trilio/k8s-triliovault-operator/4.0.5/templates/serviceAccount.yaml b/charts/trilio/k8s-triliovault-operator/4.0.5/templates/serviceAccount.yaml new file mode 100644 index 000000000..c36e39bd0 --- /dev/null +++ b/charts/trilio/k8s-triliovault-operator/4.0.5/templates/serviceAccount.yaml @@ -0,0 +1,14 @@ +{{- if eq .Values.svcAccountName "" }} +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ template "k8s-triliovault-operator.serviceAccountName" . }} + namespace: {{ .Release.Namespace }} + labels: + {{- include "k8s-triliovault-operator.labels" . | nindent 4 }} + app.kubernetes.io/instance: {{ template "k8s-triliovault-operator.appName" . }}-service-account +{{- if .Values.imagePullSecret }} +imagePullSecrets: +- name: {{ .Values.imagePullSecret }} +{{- end}} +{{- end }} \ No newline at end of file diff --git a/charts/trilio/k8s-triliovault-operator/4.0.5/templates/validating-webhook.yaml b/charts/trilio/k8s-triliovault-operator/4.0.5/templates/validating-webhook.yaml new file mode 100644 index 000000000..66d1044d6 --- /dev/null +++ b/charts/trilio/k8s-triliovault-operator/4.0.5/templates/validating-webhook.yaml @@ -0,0 +1,104 @@ +apiVersion: admissionregistration.k8s.io/v1 +kind: ValidatingWebhookConfiguration +metadata: + name: {{ template "k8s-triliovault-operator.name" . }}-validating-webhook-configuration + labels: + {{- include "k8s-triliovault-operator.labels" . | nindent 4 }} + app.kubernetes.io/instance: {{ template "k8s-triliovault-operator.appName" . }}-validating-webhook-configuration +webhooks: +- clientConfig: + service: + name: {{ template "k8s-triliovault-operator.fullname" . }}-webhook-service + namespace: {{ .Release.Namespace }} + path: /validate-triliovault-trilio-io-v1-triliovaultmanager + failurePolicy: Fail + name: v1-tvm-validation.trilio.io + rules: + - apiGroups: + - triliovault.trilio.io + apiVersions: + - v1 + operations: + - CREATE + - UPDATE + resources: + - triliovaultmanagers + sideEffects: None + admissionReviewVersions: + - v1 +- clientConfig: + service: + name: {{ template "k8s-triliovault-operator.fullname" . }}-webhook-service + namespace: {{ .Release.Namespace }} + path: /validate-core-v1-secret + failurePolicy: Fail + name: v1-encryption-secret-validation.trilio.io + objectSelector: + matchExpressions: + - key: triliovault.trilio.io/master-secret + operator: Exists + rules: + - apiGroups: + - "*" + apiVersions: + - v1 + operations: + - DELETE + - UPDATE + resources: + - secrets + sideEffects: None + admissionReviewVersions: + - v1 +- clientConfig: + service: + name: {{ template "k8s-triliovault-operator.fullname" . }}-webhook-service + namespace: {{ .Release.Namespace }} + path: /validate-core-v1-namespace + failurePolicy: Fail + name: v1-tvm-ns-validation.trilio.io + namespaceSelector: + matchExpressions: + - key: trilio-operator-label + operator: In + values: + - {{ .Release.Namespace }} + rules: + - apiGroups: + - "" + apiVersions: + - v1 + operations: + - DELETE + resources: + - namespaces + scope: '*' + sideEffects: None + admissionReviewVersions: + - v1 +{{- if .Values.observability.enabled }} +- clientConfig: + service: + name: {{ template "k8s-triliovault-operator.fullname" . }}-webhook-service + namespace: {{ .Release.Namespace }} + path: /validate-core-v1-secret + failurePolicy: Ignore + name: v1-observability-secret-validation.trilio.io + objectSelector: + matchExpressions: + - key: triliovault.trilio.io/observability + operator: Exists + rules: + - apiGroups: + - "*" + apiVersions: + - v1 + operations: + - DELETE + - UPDATE + resources: + - secrets + sideEffects: None + admissionReviewVersions: + - v1 +{{- end }} \ No newline at end of file diff --git a/charts/trilio/k8s-triliovault-operator/4.0.5/templates/webhook-service.yaml b/charts/trilio/k8s-triliovault-operator/4.0.5/templates/webhook-service.yaml new file mode 100644 index 000000000..8717867d7 --- /dev/null +++ b/charts/trilio/k8s-triliovault-operator/4.0.5/templates/webhook-service.yaml @@ -0,0 +1,17 @@ +apiVersion: v1 +kind: Service +metadata: + name: {{ template "k8s-triliovault-operator.fullname" . }}-webhook-service + namespace: {{ .Release.Namespace }} + labels: + app: {{ template "k8s-triliovault-operator.fullname" . }} + release: "{{ .Release.Name }}" + {{- include "k8s-triliovault-operator.labels" . | nindent 4 }} + app.kubernetes.io/instance: {{ template "k8s-triliovault-operator.appName" . }}-webhook-service +spec: + ports: + - port: 443 + targetPort: 9443 + selector: + app: {{ template "k8s-triliovault-operator.fullname" . }} + release: "{{ .Release.Name }}" diff --git a/charts/trilio/k8s-triliovault-operator/4.0.5/values.yaml b/charts/trilio/k8s-triliovault-operator/4.0.5/values.yaml new file mode 100644 index 000000000..039dc31e6 --- /dev/null +++ b/charts/trilio/k8s-triliovault-operator/4.0.5/values.yaml @@ -0,0 +1,242 @@ +## TrilioVault Operator +registry: "quay.io/triliodata" +operator-webhook-init: + repository: operator-webhook-init +k8s-triliovault-operator: + repository: k8s-triliovault-operator +tag: "4.0.5" +# create image pull secrets and specify the name here. +imagePullSecret: "" +priorityClassName: "" +preflight: + enabled: false + repository: preflight + imageTag: "1.3.0" + logLevel: "INFO" + cleanupOnFailure: false + imagePullSecret: "" + limits: "" + localRegistry: "" + nodeSelector: "" + pvcStorageRequest: "" + requests: "" + storageClass: "" + volumeSnapshotClass: "" +# Affinity rules for scheduling the Pod of this application. +# https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#node-affinity +affinity: + nodeAffinity: + requiredDuringSchedulingIgnoredDuringExecution: + nodeSelectorTerms: + - matchExpressions: + - key: kubernetes.io/arch + operator: In + values: + - amd64 + - ppc64le +# Node selection constraints for scheduling Pods of this application. +# https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#nodeselector +nodeSelector: {} +# Taints to be tolerated by Pods of this application. +# https://kubernetes.io/docs/concepts/configuration/taint-and-toleration/ +tolerations: [] +masterEncryptionKeyConfig: + name: "triliovault-master-encryption-key" + namespace: "" +image: + pullPolicy: Always +tls: + secretName: "helm-client-certs" + verify: false + enable: false + keyFile: "tls.key" + certFile: "tls.crt" + caContent: "" + hostname: "" +nameOverride: "" +replicaCount: 1 +proxySettings: + PROXY_ENABLED: false + NO_PROXY: "" + HTTP_PROXY: "" + HTTPS_PROXY: "" + CA_BUNDLE_CONFIGMAP: "" +podSpec: + hostIPC: false + hostNetwork: false + hostPID: false + securityContext: + runAsNonRoot: true + runAsUser: 1001 +securityContext: + allowPrivilegeEscalation: false + privileged: false + readOnlyRootFilesystem: false + runAsNonRoot: true + runAsUser: 1001 + capabilities: + drop: + - ALL +installTVK: + enabled: true + applicationScope: Cluster + tvkInstanceName: "" + ingressConfig: + host: "" + tlsSecretName: "" + annotations: {} + ingressClass: "" + ComponentConfiguration: + ingressController: + enabled: true + service: + type: NodePort +observability: + enabled: false + name: "tvk-integration" + logging: + loki: + enabled: true + fullnameOverride: "loki" + persistence: + enabled: true + accessModes: + - ReadWriteOnce + size: 10Gi + config: + limits_config: + reject_old_samples_max_age: 168h + table_manager: + retention_period: 168h + image: + registry: docker.io + promtail: + enabled: true + fullnameOverride: "promtail" + config: + clients: + - url: http://loki:3100/loki/api/v1/push + image: + registry: docker.io + monitoring: + prometheus: + enabled: true + fullnameOverride: "prom" + server: + enabled: true + fullnameOverride: "prom-server" + persistentVolume: + enabled: false + image: + registry: quay.io + kubeStateMetrics: + enabled: false + image: + registry: k8s.gcr.io + nodeExporter: + enabled: false + image: + registry: quay.io + pushgateway: + enabled: false + image: + registry: docker.io + alertmanager: + enabled: false + image: + registry: quay.io + configmapReload: + prometheus: + image: + registry: docker.io + alertmanager: + image: + registry: docker.io + visualization: + grafana: + enabled: true + adminPassword: "admin123" + fullnameOverride: "grafana" + service: + type: ClusterIP + image: + registry: docker.io + testFramework: + registry: docker.io + imageRenderer: + image: + registry: docker.io + sidecar: + image: + registry: quay.io + initChownData: + image: + registry: docker.io + downloadDashboardsImage: + registry: docker.io +# these annotations will be added to all tvk pods +podAnnotations: + sidecar.istio.io/inject: false +# these labels will be added to all tvk pods +podLabels: + sidecar.portshift.io/inject: false + linkerd.io/inject: disabled +relatedImages: + tags: + tvk: "4.0.5" + event: "4.0.5" + control-plane: + image: "control-plane" + metamover: + image: "datamover" + datamover: + image: "datamover" + datastore-attacher: + image: "datamover" + admission-webhook: + image: "control-plane" + analyzer: + image: "control-plane" + ingress-controller: + image: "ingress-controller" + tag: "v1.5.1" + kube-certgen: + image: "kube-certgen" + tag: "v1.3.0" + exporter: + image: "control-plane" + web: + image: "web" + web-backend: + image: "control-plane" + backup-scheduler: + image: "control-plane" + backup-cleaner: + image: "datamover" + target-browser: + image: "datamover" + backup-retention: + image: "datamover" + hook: + image: "control-plane" + resource-cleaner: + image: "control-plane" + tvk-init: + image: "control-plane" + dex: + image: "dex" + tag: "2.30.7" + minio: + image: "control-plane" + nats: + image: "nats" + tag: "2.8.5" + service-manager: + image: "event-stack" + syncer: + image: "event-stack" + watcher: + image: "event-stack" + continuous-restore: + image: "datamover" +svcAccountName: "" diff --git a/index.yaml b/index.yaml index 3962b4d77..4ee39bc95 100644 --- a/index.yaml +++ b/index.yaml @@ -4107,6 +4107,38 @@ entries: - assets/cerbos/cerbos-0.37.0.tgz version: 0.37.0 cf-runtime: + - annotations: + artifacthub.io/changes: | + - kind: added + description: "Added support for terminationGracePeriodSeconds configuration for dind and engine" + artifacthub.io/containsSecurityUpdates: "false" + 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: "2024-09-19T00:52:52.053850517Z" + dependencies: + - name: cf-common + repository: file://./charts/cf-common + version: 0.16.0 + description: A Helm chart for Codefresh Runner + digest: 95c13bef9f3e878077a5c761dd7a4552edfb69faef0249c102e98af44546bc12 + 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-6.4.0.tgz + version: 6.4.0 - annotations: artifacthub.io/changes: | - kind: fixed @@ -5270,6 +5302,28 @@ entries: - assets/cloudcasa/cloudcasa-3.4.1.tgz version: 3.4.1 cockroachdb: + - annotations: + catalog.cattle.io/certified: partner + catalog.cattle.io/display-name: CockroachDB + catalog.cattle.io/kube-version: '>=1.8-0' + catalog.cattle.io/release-name: cockroachdb + apiVersion: v1 + appVersion: 24.2.0 + created: "2024-09-19T00:52:52.019388869Z" + description: CockroachDB is a scalable, survivable, strongly-consistent SQL database. + digest: adbe03b3ac2eaab3837bc7183996a4bb4e9b4a1c7210f5391b9ee5f67fd08546 + home: https://www.cockroachlabs.com + icon: file://assets/icons/cockroachdb.png + kubeVersion: '>=1.8-0' + maintainers: + - email: helm-charts@cockroachlabs.com + name: cockroachlabs + name: cockroachdb + sources: + - https://github.com/cockroachdb/cockroach + urls: + - assets/cockroach-labs/cockroachdb-14.0.2.tgz + version: 14.0.2 - annotations: catalog.cattle.io/certified: partner catalog.cattle.io/display-name: CockroachDB @@ -15308,6 +15362,64 @@ entries: - assets/intel/intel-device-plugins-sgx-0.26.1.tgz version: 0.26.1 jenkins: + - annotations: + artifacthub.io/category: integration-delivery + artifacthub.io/changes: | + - Update `kubernetes` to version `4288.v1719f9d0c854` + artifacthub.io/images: | + - name: jenkins + image: docker.io/jenkins/jenkins:2.462.2-jdk17 + - name: k8s-sidecar + image: docker.io/kiwigrid/k8s-sidecar:1.27.6 + - name: inbound-agent + image: jenkins/inbound-agent:3261.v9c670a_4748a_9-1 + artifacthub.io/license: Apache-2.0 + artifacthub.io/links: | + - name: Chart Source + url: https://github.com/jenkinsci/helm-charts/tree/main/charts/jenkins + - name: Jenkins + url: https://www.jenkins.io/ + - name: support + url: https://github.com/jenkinsci/helm-charts/issues + catalog.cattle.io/certified: partner + catalog.cattle.io/display-name: Jenkins + catalog.cattle.io/kube-version: '>=1.14-0' + catalog.cattle.io/release-name: jenkins + apiVersion: v2 + appVersion: 2.462.2 + created: "2024-09-19T00:52:52.99212095Z" + description: 'Jenkins - Build great things at any scale! As the leading open source + automation server, Jenkins provides over 1800 plugins to support building, deploying + and automating any project. ' + digest: fa5bd27b2078845d23f59b4f838e6016e564d6ed35c475a976abe9a6f9dc7273 + home: https://www.jenkins.io/ + icon: file://assets/icons/jenkins.svg + keywords: + - jenkins + - ci + - devops + kubeVersion: '>=1.14-0' + maintainers: + - email: maor.friedman@redhat.com + name: maorfr + - email: mail@torstenwalter.de + name: torstenwalter + - email: garridomota@gmail.com + name: mogaal + - email: wmcdona89@gmail.com + name: wmcdona89 + - email: timjacomb1@gmail.com + name: timja + name: jenkins + sources: + - https://github.com/jenkinsci/jenkins + - https://github.com/jenkinsci/docker-inbound-agent + - https://github.com/maorfr/kube-tasks + - https://github.com/jenkinsci/configuration-as-code-plugin + type: application + urls: + - assets/jenkins/jenkins-5.6.2.tgz + version: 5.6.2 - annotations: artifacthub.io/category: integration-delivery artifacthub.io/changes: | @@ -19249,6 +19361,34 @@ entries: - assets/jenkins/jenkins-4.3.27.tgz version: 4.3.27 k8s-triliovault-operator: + - annotations: + catalog.cattle.io/certified: partner + catalog.cattle.io/display-name: TrilioVault for Kubernetes Operator + catalog.cattle.io/kube-version: '>=1.19.0-0' + catalog.cattle.io/release-name: k8s-triliovault-operator + apiVersion: v2 + appVersion: 4.0.5 + created: "2024-09-19T00:52:56.567257001Z" + dependencies: + - condition: observability.enabled + name: observability + repository: file://./charts/observability + version: ^0.1.0 + description: K8s-TrilioVault-Operator is an operator designed to manage the K8s-TrilioVault + Application Lifecycle. + digest: fbd981a7c43b38eea0d17db829abcb56c31a5c10c47fbe14de7fa9fafa541445 + home: https://github.com/trilioData/k8s-triliovault-operator + icon: file://assets/icons/k8s-triliovault-operator.png + kubeVersion: '>=1.19.0-0' + maintainers: + - email: prafull.ladha@trilio.io + name: prafull11 + name: k8s-triliovault-operator + sources: + - https://github.com/trilioData/k8s-triliovault-operator + urls: + - assets/trilio/k8s-triliovault-operator-4.0.5.tgz + version: 4.0.5 - annotations: catalog.cattle.io/certified: partner catalog.cattle.io/display-name: TrilioVault for Kubernetes Operator @@ -40325,6 +40465,44 @@ entries: - assets/intel/tcs-issuer-0.1.0.tgz version: 0.1.0 traefik: + - annotations: + artifacthub.io/changes: "- \"fix: \U0001F41B update CRD to v3.1\"\n- \"feat: + ✨ input validation using schema\"\n- \"feat: ✨ add AllowACMEByPass and improve + schema/doc on ports values\"\n- \"feat: add new webhooks and removes unnecessary + ones\"\n- \"feat(deps): update traefik docker tag to v3.1.3\"\n- \"chore(release): + \U0001F680 publish v31.1.0\"\n" + catalog.cattle.io/certified: partner + catalog.cattle.io/display-name: Traefik Proxy + catalog.cattle.io/kube-version: '>=1.22.0-0' + catalog.cattle.io/release-name: traefik + apiVersion: v2 + appVersion: v3.1.3 + created: "2024-09-19T00:52:56.47242138Z" + description: A Traefik based Kubernetes ingress controller + digest: e50fa09a409728da23812962f05518024c169257c5c19d9a142e6feb07a6d1ba + home: https://traefik.io/ + icon: file://assets/icons/traefik.png + keywords: + - traefik + - ingress + - networking + kubeVersion: '>=1.22.0-0' + maintainers: + - email: michel.loiseleur@traefik.io + name: mloiseleur + - email: charlie.haley@traefik.io + name: charlie-haley + - email: remi.buisson@traefik.io + name: darkweaver87 + - name: jnoordsij + name: traefik + sources: + - https://github.com/traefik/traefik + - https://github.com/traefik/traefik-helm-chart + type: application + urls: + - assets/traefik/traefik-31.1.0.tgz + version: 31.1.0 - annotations: artifacthub.io/changes: "- \"fix(Traefik Hub): update CRDs to v1.5.0\"\n- \"fix(HTTP3): split udp and tcp Service when service.single is false\"\n- \"fix!: \U0001F41B @@ -42550,4 +42728,4 @@ entries: urls: - assets/netfoundry/ziti-host-1.5.1.tgz version: 1.5.1 -generated: "2024-09-18T00:51:58.441754554Z" +generated: "2024-09-19T00:52:51.644361059Z"